1
0
mirror of synced 2026-03-01 11:16:56 +00:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Stefan Prodan
bda4c8187e Merge pull request #5427 from fluxcd/backport-5426-to-release/v2.6.x
[release/v2.6.x] Update toolkit components
2025-06-27 13:49:43 +03:00
Matheus Pimenta
3f281da738 Fix: Prioritize sha2-512 and sha2-256 for ssh-rsa host keys
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
(cherry picked from commit a8425f50bd)
2025-06-27 10:39:53 +00:00
fluxcdbot
963e99188c Update toolkit components
- source-controller to v1.6.2
  https://github.com/fluxcd/source-controller/blob/v1.6.2/CHANGELOG.md
- image-automation-controller to v0.41.2
  https://github.com/fluxcd/image-automation-controller/blob/v0.41.2/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
(cherry picked from commit 24bf751d4d)
2025-06-27 10:39:53 +00:00
Matheus Pimenta
a48f81a66c Merge pull request #5410 from fluxcd/backport-5409-to-release/v2.6.x
[release/v2.6.x] Update toolkit components
2025-06-13 18:23:46 +01:00
fluxcdbot
55104dc188 Update toolkit components
- source-controller to v1.6.1
  https://github.com/fluxcd/source-controller/blob/v1.6.1/CHANGELOG.md
- image-reflector-controller to v0.35.2
  https://github.com/fluxcd/image-reflector-controller/blob/v0.35.2/CHANGELOG.md
- image-automation-controller to v0.41.1
  https://github.com/fluxcd/image-automation-controller/blob/v0.41.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
(cherry picked from commit 6b3a1134bd)
2025-06-13 17:13:46 +00:00
Matheus Pimenta
e771ff28ab Merge pull request #5405 from fluxcd/backport-5404-to-release/v2.6.x
[release/v2.6.x] Fix `knownhosts key mismatch` regression bug
2025-06-12 19:05:13 +01:00
Matheus Pimenta
998fe11166 Upgrade dependencies
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
(cherry picked from commit 999f61c02e)
2025-06-12 17:54:54 +00:00
Stefan Prodan
a6ac4c5b60 Merge pull request #5396 from fluxcd/backport-5390-to-release/v2.6.x
[release/v2.6.x] fix: Allow Azure CLI calls in `flux push artifact --provider azure` on DevOps runners
2025-06-09 18:18:19 +03:00
Matheus Pimenta
0d397d7d1f Introduce support for shelling out to Azure binaries in authentication
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
(cherry picked from commit ec3804cc6f)
2025-06-09 14:59:47 +00:00
Matheus Pimenta
b73c7f7191 Merge pull request #5384 from fluxcd/backport-5383-to-release/v2.6.x
[release/v2.6.x] Add digest pinning to image automation testing
2025-06-01 20:36:07 +01:00
Stefan Prodan
7aff0327ad Add digest pinning to image automation testing
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
(cherry picked from commit be8acc0cfb)
2025-06-01 19:11:40 +00:00
Stefan Prodan
3bb3ae3617 Merge pull request #5382 from fluxcd/backport-5381-to-release/v2.6.x
[release/v2.6.x] Update image-reflector-controller to v0.35.1
2025-06-01 21:29:29 +03:00
fluxcdbot
bf1af3c120 Update toolkit components
- image-reflector-controller to v0.35.1
  https://github.com/fluxcd/image-reflector-controller/blob/v0.35.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
(cherry picked from commit 4172a8a7f9)
2025-06-01 18:19:25 +00:00
258 changed files with 3147 additions and 4510 deletions

6
.github/labels.yaml vendored
View File

@@ -44,12 +44,12 @@
description: Feature request proposals in the RFC format description: Feature request proposals in the RFC format
color: '#D621C3' color: '#D621C3'
aliases: ['area/RFC'] aliases: ['area/RFC']
- name: backport:release/v2.3.x
description: To be backported to release/v2.3.x
color: '#ffd700'
- name: backport:release/v2.4.x - name: backport:release/v2.4.x
description: To be backported to release/v2.4.x description: To be backported to release/v2.4.x
color: '#ffd700' color: '#ffd700'
- name: backport:release/v2.5.x - name: backport:release/v2.5.x
description: To be backported to release/v2.5.x description: To be backported to release/v2.5.x
color: '#ffd700' color: '#ffd700'
- name: backport:release/v2.6.x
description: To be backported to release/v2.6.x
color: '#ffd700'

View File

@@ -24,6 +24,6 @@ jobs:
name: action on ${{ matrix.version }} name: action on ${{ matrix.version }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup flux - name: Setup flux
uses: ./action uses: ./action

View File

@@ -1,13 +1,34 @@
name: backport name: backport
on: on:
pull_request_target: pull_request_target:
types: [closed, labeled] types: [closed, labeled]
permissions: read-all
permissions:
contents: read
jobs: jobs:
backport: pull-request:
runs-on: ubuntu-latest
permissions: permissions:
contents: write # for reading and creating branches. contents: write
pull-requests: write # for creating pull requests against release branches. pull-requests: write
uses: fluxcd/gha-workflows/.github/workflows/backport.yaml@v0.4.0 if: github.event.pull_request.state == 'closed' && github.event.pull_request.merged && (github.event_name != 'labeled' || startsWith('backport:', github.event.label.name))
secrets: steps:
github-token: ${{ secrets.GITHUB_TOKEN }} - name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs
uses: korthout/backport-action@436145e922f9561fc5ea157ff406f21af2d6b363 # v3.2.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

@@ -9,7 +9,7 @@ permissions:
contents: read contents: read
env: env:
GO_VERSION: 1.25.x GO_VERSION: 1.24.x
jobs: jobs:
conform-kubernetes: conform-kubernetes:
@@ -19,13 +19,13 @@ jobs:
matrix: matrix:
# Keep this list up-to-date with https://endoflife.date/kubernetes # Keep this list up-to-date with https://endoflife.date/kubernetes
# Build images with https://github.com/fluxcd/flux-benchmark/actions/workflows/build-kind.yaml # Build images with https://github.com/fluxcd/flux-benchmark/actions/workflows/build-kind.yaml
KUBERNETES_VERSION: [1.32.1, 1.33.0, 1.34.1] KUBERNETES_VERSION: [1.31.5, 1.32.1, 1.33.0]
fail-fast: false fail-fast: false
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: ${{ env.GO_VERSION }} go-version: ${{ env.GO_VERSION }}
cache-dependency-path: | cache-dependency-path: |
@@ -42,7 +42,7 @@ jobs:
- name: Setup Kubernetes - name: Setup Kubernetes
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0 uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with: with:
version: v0.30.0 version: v0.27.0
cluster_name: ${{ steps.prep.outputs.CLUSTER }} cluster_name: ${{ steps.prep.outputs.CLUSTER }}
node_image: ghcr.io/fluxcd/kindest/node:v${{ matrix.KUBERNETES_VERSION }}-arm64 node_image: ghcr.io/fluxcd/kindest/node:v${{ matrix.KUBERNETES_VERSION }}-arm64
- name: Run e2e tests - name: Run e2e tests
@@ -76,13 +76,13 @@ jobs:
matrix: matrix:
# Keep this list up-to-date with https://endoflife.date/kubernetes # Keep this list up-to-date with https://endoflife.date/kubernetes
# Available versions can be found with "replicated cluster versions" # Available versions can be found with "replicated cluster versions"
K3S_VERSION: [ 1.32.8, 1.33.4 ] K3S_VERSION: [ 1.31.8, 1.32.4, 1.33.0 ]
fail-fast: false fail-fast: false
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: ${{ env.GO_VERSION }} go-version: ${{ env.GO_VERSION }}
cache-dependency-path: | cache-dependency-path: |
@@ -97,7 +97,7 @@ jobs:
KUBECONFIG_PATH="$(git rev-parse --show-toplevel)/bin/kubeconfig.yaml" KUBECONFIG_PATH="$(git rev-parse --show-toplevel)/bin/kubeconfig.yaml"
echo "kubeconfig-path=${KUBECONFIG_PATH}" >> $GITHUB_OUTPUT echo "kubeconfig-path=${KUBECONFIG_PATH}" >> $GITHUB_OUTPUT
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@bf02f0a2d612cc07e0892166369fa8f63246aabb # main uses: fluxcd/pkg/actions/kustomize@7e9c75bbb6a47b08c194edefa11d1c436e5bdd9e # main
- name: Build - name: Build
run: make build-dev run: make build-dev
- name: Create repository - name: Create repository
@@ -169,13 +169,13 @@ jobs:
strategy: strategy:
matrix: matrix:
# Keep this list up-to-date with https://endoflife.date/red-hat-openshift # Keep this list up-to-date with https://endoflife.date/red-hat-openshift
OPENSHIFT_VERSION: [ 4.19.0-okd ] OPENSHIFT_VERSION: [ 4.18.0-okd ]
fail-fast: false fail-fast: false
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: ${{ env.GO_VERSION }} go-version: ${{ env.GO_VERSION }}
cache-dependency-path: | cache-dependency-path: |
@@ -190,7 +190,7 @@ jobs:
KUBECONFIG_PATH="$(git rev-parse --show-toplevel)/bin/kubeconfig.yaml" KUBECONFIG_PATH="$(git rev-parse --show-toplevel)/bin/kubeconfig.yaml"
echo "kubeconfig-path=${KUBECONFIG_PATH}" >> $GITHUB_OUTPUT echo "kubeconfig-path=${KUBECONFIG_PATH}" >> $GITHUB_OUTPUT
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@bf02f0a2d612cc07e0892166369fa8f63246aabb # main uses: fluxcd/pkg/actions/kustomize@7e9c75bbb6a47b08c194edefa11d1c436e5bdd9e # main
- name: Build - name: Build
run: make build-dev run: make build-dev
- name: Create repository - name: Create repository

View File

@@ -22,18 +22,19 @@ permissions:
jobs: jobs:
e2e-aks: e2e-aks:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
defaults: defaults:
run: run:
working-directory: ./tests/integration working-directory: ./tests/integration
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' # This job is currently disabled. Remove the false check when Azure subscription is enabled.
if: false && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
steps: steps:
- name: CheckoutD - name: CheckoutD
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.25.x go-version: 1.24.x
cache-dependency-path: tests/integration/go.sum cache-dependency-path: tests/integration/go.sum
- name: Setup Terraform - name: Setup Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
@@ -50,7 +51,7 @@ jobs:
- name: Authenticate to Azure - name: Authenticate to Azure
uses: Azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v1.4.6 uses: Azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v1.4.6
with: with:
creds: '{"clientId":"${{ secrets.ARM_CLIENT_ID }}","clientSecret":"${{ secrets.ARM_CLIENT_SECRET }}","subscriptionId":"${{ secrets.ARM_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.ARM_TENANT_ID }}"}' creds: '{"clientId":"${{ secrets.AZ_ARM_CLIENT_ID }}","clientSecret":"${{ secrets.AZ_ARM_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZ_ARM_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZ_ARM_TENANT_ID }}"}'
- name: Set dynamic variables in .env - name: Set dynamic variables in .env
run: | run: |
cat > .env <<EOF cat > .env <<EOF
@@ -60,35 +61,33 @@ jobs:
run: cat .env run: cat .env
- name: Run Azure e2e tests - name: Run Azure e2e tests
env: env:
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} ARM_CLIENT_ID: ${{ secrets.AZ_ARM_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} ARM_CLIENT_SECRET: ${{ secrets.AZ_ARM_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} ARM_SUBSCRIPTION_ID: ${{ secrets.AZ_ARM_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} ARM_TENANT_ID: ${{ secrets.AZ_ARM_TENANT_ID }}
TF_VAR_azuredevops_org: ${{ secrets.TF_VAR_azuredevops_org }} TF_VAR_azuredevops_org: ${{ secrets.TF_VAR_azuredevops_org }}
TF_VAR_azuredevops_pat: ${{ secrets.TF_VAR_azuredevops_pat }} TF_VAR_azuredevops_pat: ${{ secrets.TF_VAR_azuredevops_pat }}
TF_VAR_azure_location: ${{ vars.TF_VAR_azure_location }} TF_VAR_location: ${{ vars.TF_VAR_azure_location }}
GITREPO_SSH_CONTENTS: ${{ secrets.GIT_SSH_IDENTITY }} GITREPO_SSH_CONTENTS: ${{ secrets.AZURE_GITREPO_SSH_CONTENTS }}
GITREPO_SSH_PUB_CONTENTS: ${{ secrets.GIT_SSH_IDENTITY_PUB }} GITREPO_SSH_PUB_CONTENTS: ${{ secrets.AZURE_GITREPO_SSH_PUB_CONTENTS }}
run: | run: |
source .env source .env
mkdir -p ./build/ssh mkdir -p ./build/ssh
cat <<EOF > build/ssh/key touch ./build/ssh/key
$GITREPO_SSH_CONTENTS echo $GITREPO_SSH_CONTENTS | base64 -d > build/ssh/key
EOF
export GITREPO_SSH_PATH=build/ssh/key export GITREPO_SSH_PATH=build/ssh/key
cat <<EOF > build/ssh/key.pub touch ./build/ssh/key.pub
$GITREPO_SSH_PUB_CONTENTS echo $GITREPO_SSH_PUB_CONTENTS | base64 -d > ./build/ssh/key.pub
EOF
export GITREPO_SSH_PUB_PATH=build/ssh/key.pub export GITREPO_SSH_PUB_PATH=build/ssh/key.pub
make test-azure make test-azure
- name: Ensure resource cleanup - name: Ensure resource cleanup
if: ${{ always() }} if: ${{ always() }}
env: env:
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} ARM_CLIENT_ID: ${{ secrets.AZ_ARM_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} ARM_CLIENT_SECRET: ${{ secrets.AZ_ARM_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} ARM_SUBSCRIPTION_ID: ${{ secrets.AZ_ARM_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} ARM_TENANT_ID: ${{ secrets.AZ_ARM_TENANT_ID }}
TF_VAR_azuredevops_org: ${{ secrets.TF_VAR_azuredevops_org }} TF_VAR_azuredevops_org: ${{ secrets.TF_VAR_azuredevops_org }}
TF_VAR_azuredevops_pat: ${{ secrets.TF_VAR_azuredevops_pat }} TF_VAR_azuredevops_pat: ${{ secrets.TF_VAR_azuredevops_pat }}
TF_VAR_azure_location: ${{ vars.TF_VAR_azure_location }} TF_VAR_location: ${{ vars.TF_VAR_azure_location }}
run: source .env && make destroy-azure run: source .env && make destroy-azure

View File

@@ -17,27 +17,27 @@ jobs:
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.25.x go-version: 1.24.x
cache-dependency-path: | cache-dependency-path: |
**/go.sum **/go.sum
**/go.mod **/go.mod
- name: Setup Kubernetes - name: Setup Kubernetes
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0 uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with: with:
version: v0.30.0 version: v0.24.0
cluster_name: kind cluster_name: kind
# The versions below should target the newest Kubernetes version # The versions below should target the newest Kubernetes version
# Keep this up-to-date with https://endoflife.date/kubernetes # Keep this up-to-date with https://endoflife.date/kubernetes
node_image: ghcr.io/fluxcd/kindest/node:v1.32.1-amd64 node_image: ghcr.io/fluxcd/kindest/node:v1.33.0-amd64
kubectl_version: v1.32.0 kubectl_version: v1.32.0
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@bf02f0a2d612cc07e0892166369fa8f63246aabb # main uses: fluxcd/pkg/actions/kustomize@7e9c75bbb6a47b08c194edefa11d1c436e5bdd9e # main
- name: Setup yq - name: Setup yq
uses: fluxcd/pkg/actions/yq@bf02f0a2d612cc07e0892166369fa8f63246aabb # main uses: fluxcd/pkg/actions/yq@7e9c75bbb6a47b08c194edefa11d1c436e5bdd9e # main
- name: Build - name: Build
run: make build-dev run: make build-dev
- name: Set outputs - name: Set outputs
@@ -105,7 +105,6 @@ jobs:
--path=test-cluster \ --path=test-cluster \
--read-write-key --read-write-key
./bin/flux reconcile image repository podinfo ./bin/flux reconcile image repository podinfo
./bin/flux reconcile image policy podinfo
./bin/flux reconcile image update flux-system ./bin/flux reconcile image update flux-system
./bin/flux get images all ./bin/flux get images all
./bin/flux -n flux-system events --for ImageUpdateAutomation/flux-system ./bin/flux -n flux-system events --for ImageUpdateAutomation/flux-system

View File

@@ -22,18 +22,18 @@ permissions:
jobs: jobs:
e2e-gcp: e2e-gcp:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
defaults: defaults:
run: run:
working-directory: ./tests/integration working-directory: ./tests/integration
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.25.x go-version: 1.24.x
cache-dependency-path: tests/integration/go.sum cache-dependency-path: tests/integration/go.sum
- name: Setup Terraform - name: Setup Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
@@ -48,19 +48,19 @@ jobs:
env: env:
SOPS_VER: 3.7.1 SOPS_VER: 3.7.1
- name: Authenticate to Google Cloud - name: Authenticate to Google Cloud
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 uses: google-github-actions/auth@ba79af03959ebeac9769e648f473a284504d9193 # v2.1.10
id: 'auth' id: 'auth'
with: with:
credentials_json: '${{ secrets.FLUX2_E2E_GOOGLE_CREDENTIALS }}' credentials_json: '${{ secrets.FLUX2_E2E_GOOGLE_CREDENTIALS }}'
token_format: 'access_token' token_format: 'access_token'
- name: Setup gcloud - name: Setup gcloud
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1 uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker Buildx - name: Setup Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- name: Log into us-central1-docker.pkg.dev - name: Log into us-central1-docker.pkg.dev
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with: with:
registry: us-central1-docker.pkg.dev registry: us-central1-docker.pkg.dev
username: oauth2accesstoken username: oauth2accesstoken

View File

@@ -23,30 +23,30 @@ jobs:
- 5000:5000 - 5000:5000
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.25.x go-version: 1.24.x
cache-dependency-path: | cache-dependency-path: |
**/go.sum **/go.sum
**/go.mod **/go.mod
- name: Setup Kubernetes - name: Setup Kubernetes
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0 uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with: with:
version: v0.30.0 version: v0.24.0
cluster_name: kind cluster_name: kind
wait: 5s wait: 5s
config: .github/kind/config.yaml # disable KIND-net config: .github/kind/config.yaml # disable KIND-net
# The versions below should target the oldest supported Kubernetes version # The versions below should target the oldest supported Kubernetes version
# Keep this up-to-date with https://endoflife.date/kubernetes # Keep this up-to-date with https://endoflife.date/kubernetes
node_image: ghcr.io/fluxcd/kindest/node:v1.32.1-amd64 node_image: ghcr.io/fluxcd/kindest/node:v1.31.5-amd64
kubectl_version: v1.32.0 kubectl_version: v1.32.0
- name: Setup Calico for network policy - name: Setup Calico for network policy
run: | run: |
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/calico.yaml kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/calico.yaml
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@bf02f0a2d612cc07e0892166369fa8f63246aabb # main uses: fluxcd/pkg/actions/kustomize@7e9c75bbb6a47b08c194edefa11d1c436e5bdd9e # main
- name: Run tests - name: Run tests
run: make test run: make test
- name: Run e2e tests - name: Run e2e tests
@@ -238,9 +238,6 @@ jobs:
- name: flux check - name: flux check
run: | run: |
./bin/flux check ./bin/flux check
- name: flux migrate
run: |
./bin/flux migrate
- name: flux version - name: flux version
run: | run: |
./bin/flux version ./bin/flux version

View File

@@ -19,9 +19,9 @@ jobs:
actions: read actions: read
contents: read contents: read
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Run analysis - name: Run analysis
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
with: with:
results_file: results.sarif results_file: results.sarif
results_format: sarif results_format: sarif
@@ -34,6 +34,6 @@ jobs:
path: results.sarif path: results.sarif
retention-days: 5 retention-days: 5
- name: Upload SARIF results - name: Upload SARIF results
uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
with: with:
sarif_file: results.sarif sarif_file: results.sarif

View File

@@ -20,35 +20,33 @@ jobs:
packages: write # needed for ghcr access packages: write # needed for ghcr access
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Unshallow - name: Unshallow
run: git fetch --prune --unshallow run: git fetch --prune --unshallow
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.25.x go-version: 1.24.x
cache: false cache: false
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker Buildx - name: Setup Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- name: Setup Syft - name: Setup Syft
uses: anchore/sbom-action/download-syft@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0.20.6 uses: anchore/sbom-action/download-syft@9f7302141466aa6482940f15371237e9d9f4c34a # v0.19.0
- name: Setup Cosign - name: Setup Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
with:
cosign-release: v2.6.1 # TODO: remove after Flux 2.8 with support for cosign v3
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@bf02f0a2d612cc07e0892166369fa8f63246aabb # main uses: fluxcd/pkg/actions/kustomize@7e9c75bbb6a47b08c194edefa11d1c436e5bdd9e # main
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with: with:
registry: ghcr.io registry: ghcr.io
username: fluxcdbot username: fluxcdbot
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with: with:
username: fluxcdbot username: fluxcdbot
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }} password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
@@ -61,7 +59,7 @@ jobs:
run: | run: |
kustomize build manifests/crds > all-crds.yaml kustomize build manifests/crds > all-crds.yaml
- name: Generate OpenAPI JSON schemas from CRDs - name: Generate OpenAPI JSON schemas from CRDs
uses: fluxcd/pkg/actions/crdjsonschema@bf02f0a2d612cc07e0892166369fa8f63246aabb # main uses: fluxcd/pkg/actions/crdjsonschema@7e9c75bbb6a47b08c194edefa11d1c436e5bdd9e # main
with: with:
crd: all-crds.yaml crd: all-crds.yaml
output: schemas output: schemas
@@ -70,7 +68,7 @@ jobs:
tar -czvf ./output/crd-schemas.tar.gz -C schemas . tar -czvf ./output/crd-schemas.tar.gz -C schemas .
- name: Run GoReleaser - name: Run GoReleaser
id: run-goreleaser id: run-goreleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
with: with:
version: latest version: latest
args: release --skip=validate args: release --skip=validate
@@ -101,26 +99,24 @@ jobs:
id-token: write id-token: write
packages: write packages: write
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@bf02f0a2d612cc07e0892166369fa8f63246aabb # main uses: fluxcd/pkg/actions/kustomize@7e9c75bbb6a47b08c194edefa11d1c436e5bdd9e # main
- name: Setup Flux CLI - name: Setup Flux CLI
uses: ./action/ uses: ./action/
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare - name: Prepare
id: prep id: prep
run: | run: |
VERSION=$(flux version --client | awk '{ print $NF }') VERSION=$(flux version --client | awk '{ print $NF }')
echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: Login to GHCR - name: Login to GHCR
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with: with:
registry: ghcr.io registry: ghcr.io
username: fluxcdbot username: fluxcdbot
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with: with:
username: fluxcdbot username: fluxcdbot
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }} password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
@@ -148,9 +144,7 @@ jobs:
--path="./flux-system" \ --path="./flux-system" \
--source=${{ github.repositoryUrl }} \ --source=${{ github.repositoryUrl }} \
--revision="${{ github.ref_name }}@sha1:${{ github.sha }}" --revision="${{ github.ref_name }}@sha1:${{ github.sha }}"
- uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 - uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
with:
cosign-release: v2.6.1 # TODO: remove after Flux 2.8 with support for cosign v3
- name: Sign manifests - name: Sign manifests
env: env:
COSIGN_EXPERIMENTAL: 1 COSIGN_EXPERIMENTAL: 1

View File

@@ -1,4 +1,5 @@
name: scan name: scan
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
@@ -7,13 +8,46 @@ on:
branches: [ 'main', 'release/**' ] branches: [ 'main', 'release/**' ]
schedule: schedule:
- cron: '18 10 * * 3' - cron: '18 10 * * 3'
permissions: read-all
permissions:
contents: read
jobs: jobs:
analyze: scan-fossa:
runs-on: ubuntu-latest
if: github.actor != 'dependabot[bot]'
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- 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 }}
scan-codeql:
runs-on: ubuntu-latest
permissions: permissions:
contents: read # for reading the repository code. security-events: write
security-events: write # for uploading the CodeQL analysis results. if: github.actor != 'dependabot[bot]'
uses: fluxcd/gha-workflows/.github/workflows/code-scan.yaml@v0.4.0 steps:
secrets: - name: Checkout repository
github-token: ${{ secrets.GITHUB_TOKEN }} uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
fossa-token: ${{ secrets.FOSSA_TOKEN }} - name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version-file: 'go.mod'
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Initialize CodeQL
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
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@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17

View File

@@ -6,12 +6,23 @@ on:
- main - main
paths: paths:
- .github/labels.yaml - .github/labels.yaml
permissions: read-all
permissions:
contents: read
jobs: jobs:
sync-labels: labels:
name: Run sync
runs-on: ubuntu-latest
permissions: permissions:
contents: read # for reading the labels file. issues: write
issues: write # for creating and updating labels. steps:
uses: fluxcd/gha-workflows/.github/workflows/labels-sync.yaml@v0.4.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
secrets: - uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3
github-token: ${{ secrets.GITHUB_TOKEN }} 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

View File

@@ -2,6 +2,8 @@ name: update
on: on:
workflow_dispatch: workflow_dispatch:
schedule:
- cron: "0 * * * *"
push: push:
branches: [main] branches: [main]
@@ -16,37 +18,24 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go - name: Setup Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.25.x go-version: 1.24.x
cache-dependency-path: | cache-dependency-path: |
**/go.sum **/go.sum
**/go.mod **/go.mod
- name: Update component versions - name: Update component versions
id: update id: update
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
PR_BODY=$(mktemp) PR_BODY=$(mktemp)
bump_version() { bump_version() {
local LATEST_VERSION=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name') local LATEST_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
if [[ "$LATEST_VERSION" == *"-rc"* ]]; then
echo "Skipping release candidate version for $1: $LATEST_VERSION"
return
fi
local CTRL_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml) local CTRL_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
local CRD_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p" manifests/crds/kustomization.yaml) local CRD_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p" manifests/crds/kustomization.yaml)
local MOD_VERSION=$(go list -m -f '{{ .Version }}' "github.com/fluxcd/$1/api")
local API_PKG="github.com/fluxcd/$1/api"
if [[ "$1" == "source-watcher" ]]; then
API_PKG="github.com/fluxcd/$1/api/v2"
fi
local MOD_VERSION=$(go list -m -f '{{ .Version }}' "$API_PKG")
local changed=false local changed=false
@@ -61,7 +50,7 @@ jobs:
fi fi
if [[ "${MOD_VERSION}" != "${LATEST_VERSION}" ]]; then if [[ "${MOD_VERSION}" != "${LATEST_VERSION}" ]]; then
go mod edit -require="$API_PKG@${LATEST_VERSION}" go mod edit -require="github.com/fluxcd/$1/api@${LATEST_VERSION}"
make tidy make tidy
changed=true changed=true
fi fi
@@ -80,7 +69,6 @@ jobs:
bump_version notification-controller bump_version notification-controller
bump_version image-reflector-controller bump_version image-reflector-controller
bump_version image-automation-controller bump_version image-automation-controller
bump_version source-watcher
# diff change # diff change
git diff git diff

View File

@@ -88,6 +88,22 @@ brews:
generate_completions_from_executable(bin/"flux", "completion") generate_completions_from_executable(bin/"flux", "completion")
test: | test: |
system "#{bin}/flux --version" system "#{bin}/flux --version"
publishers:
- name: aur-pkg-bin
env:
- AUR_BOT_SSH_PRIVATE_KEY={{ .Env.AUR_BOT_SSH_PRIVATE_KEY }}
cmd: |
.github/aur/flux-bin/publish.sh {{ .Version }}
- name: aur-pkg-scm
env:
- AUR_BOT_SSH_PRIVATE_KEY={{ .Env.AUR_BOT_SSH_PRIVATE_KEY }}
cmd: |
.github/aur/flux-scm/publish.sh {{ .Version }}
- name: aur-pkg-go
env:
- AUR_BOT_SSH_PRIVATE_KEY={{ .Env.AUR_BOT_SSH_PRIVATE_KEY }}
cmd: |
.github/aur/flux-go/publish.sh {{ .Version }}
dockers: dockers:
- image_templates: - image_templates:
- 'fluxcd/flux-cli:{{ .Tag }}-amd64' - 'fluxcd/flux-cli:{{ .Tag }}-amd64'

View File

@@ -1,16 +1,16 @@
FROM alpine:3.22 AS builder FROM alpine:3.21 AS builder
RUN apk add --no-cache ca-certificates curl RUN apk add --no-cache ca-certificates curl
ARG ARCH=linux/amd64 ARG ARCH=linux/amd64
ARG KUBECTL_VER=1.34.1 ARG KUBECTL_VER=1.33.0
RUN curl -sL https://dl.k8s.io/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \ RUN curl -sL https://dl.k8s.io/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl
RUN kubectl version --client=true RUN kubectl version --client=true
FROM alpine:3.22 AS flux-cli FROM alpine:3.21 AS flux-cli
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates

View File

@@ -17,8 +17,8 @@ rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2
all: test build all: test build
tidy: tidy:
go mod tidy -compat=1.25 go mod tidy -compat=1.24
cd tests/integration && go mod tidy -compat=1.25 cd tests/integration && go mod tidy -compat=1.24
fmt: fmt:
go fmt ./... go fmt ./...

View File

@@ -16,24 +16,23 @@ inputs:
description: "Alternative location for the Flux binary, defaults to path relative to $RUNNER_TOOL_CACHE." description: "Alternative location for the Flux binary, defaults to path relative to $RUNNER_TOOL_CACHE."
required: false required: false
token: token:
description: "Token used to authenticate against the GitHub.com API." description: "Token used to authentication against the GitHub.com API. Defaults to the token from the GitHub context of the workflow."
required: false required: false
runs: runs:
using: composite using: composite
steps: steps:
- name: "Download the binary to the runner's cache dir" - name: "Download the binary to the runner's cache dir"
shell: bash shell: bash
env:
VERSION: "${{ inputs.version }}"
FLUX_TOOL_DIR: "${{ inputs.bindir }}"
TOKEN: "${{ inputs.token }}"
run: | run: |
VERSION=${{ inputs.version }}
TOKEN=${{ inputs.token }}
if [[ -z "$TOKEN" ]]; then
TOKEN=${{ github.token }}
fi
if [[ -z "$VERSION" ]] || [[ "$VERSION" = "latest" ]]; then if [[ -z "$VERSION" ]] || [[ "$VERSION" = "latest" ]]; then
if [[ "${TOKEN}" != '' ]]; then VERSION=$(curl -fsSL -H "Authorization: token ${TOKEN}" https://api.github.com/repos/fluxcd/flux2/releases/latest | grep tag_name | cut -d '"' -f 4)
VERSION=$(curl -fsSL -H "Authorization: token ${TOKEN}" https://api.github.com/repos/fluxcd/flux2/releases/latest | grep tag_name | cut -d '"' -f 4)
else
VERSION=$(curl -w "%{url_effective}\n" -IsSL https://github.com/fluxcd/flux2/releases/latest -o /dev/null | sed 's$^.*/$$')
fi
fi fi
if [[ -z "$VERSION" ]]; then if [[ -z "$VERSION" ]]; then
echo "Unable to determine Flux CLI version" echo "Unable to determine Flux CLI version"
@@ -60,6 +59,7 @@ runs:
FLUX_EXEC_FILE="${FLUX_EXEC_FILE}.exe" FLUX_EXEC_FILE="${FLUX_EXEC_FILE}.exe"
fi fi
FLUX_TOOL_DIR=${{ inputs.bindir }}
if [[ -z "$FLUX_TOOL_DIR" ]]; then if [[ -z "$FLUX_TOOL_DIR" ]]; then
FLUX_TOOL_DIR="${RUNNER_TOOL_CACHE}/flux2/${VERSION}/${OS}/${ARCH}" FLUX_TOOL_DIR="${RUNNER_TOOL_CACHE}/flux2/${VERSION}/${OS}/${ARCH}"
fi fi

View File

@@ -1,57 +0,0 @@
/*
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.
*/
package main
import (
"sigs.k8s.io/controller-runtime/pkg/client"
swapi "github.com/fluxcd/source-watcher/api/v2/v1beta1"
)
// swapi.ArtifactGenerator
var artifactGeneratorType = apiType{
kind: swapi.ArtifactGeneratorKind,
humanKind: "artifactgenerator",
groupVersion: swapi.GroupVersion,
}
type artifactGeneratorAdapter struct {
*swapi.ArtifactGenerator
}
func (h artifactGeneratorAdapter) asClientObject() client.Object {
return h.ArtifactGenerator
}
func (h artifactGeneratorAdapter) deepCopyClientObject() client.Object {
return h.ArtifactGenerator.DeepCopy()
}
// swapi.ArtifactGeneratorList
type artifactGeneratorListAdapter struct {
*swapi.ArtifactGeneratorList
}
func (h artifactGeneratorListAdapter) asClientList() client.ObjectList {
return h.ArtifactGeneratorList
}
func (h artifactGeneratorListAdapter) len() int {
return len(h.ArtifactGeneratorList.Items)
}

View File

@@ -97,7 +97,7 @@ func init() {
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts values such as 'image-reflector-controller,image-automation-controller,source-watcher'") "list of components in addition to those supplied or defaulted, accepts values such as 'image-reflector-controller,image-automation-controller'")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd", bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
"container registry where the Flux controller images are published") "container registry where the Flux controller images are published")

View File

@@ -42,7 +42,7 @@ import (
var bootstrapGitLabCmd = &cobra.Command{ var bootstrapGitLabCmd = &cobra.Command{
Use: "gitlab", Use: "gitlab",
Short: "Deploy Flux on a cluster connected to a GitLab repository", Short: "Deploy Flux on a cluster connected to a GitLab repository",
Long: `The bootstrap gitlab command creates the GitLab repository if it doesn't exist and Long: `The bootstrap gitlab command creates the GitLab repository if it doesn't exists and
commits the Flux manifests to the specified branch. commits the Flux manifests to the specified branch.
Then it configures the target cluster to synchronize with that repository. Then it configures the target cluster to synchronize with that repository.
If the Flux components are present on the cluster, If the Flux components are present on the cluster,

View File

@@ -60,7 +60,7 @@ type checkFlags struct {
} }
var kubernetesConstraints = []string{ var kubernetesConstraints = []string{
">=1.32.0-0", ">=1.31.0-0",
} }
var checkArgs checkFlags var checkArgs checkFlags

View File

@@ -94,13 +94,6 @@ var createHelmReleaseCmd = &cobra.Command{
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo --chart=podinfo
# Create a HelmRelease with custom storage namespace for hub-and-spoke model
flux create hr podinfo \
--target-namespace=production \
--storage-namespace=fluxcd-system \
--source=HelmRepository/podinfo \
--chart=podinfo
# Create a HelmRelease using a source from a different namespace # Create a HelmRelease using a source from a different namespace
flux create hr podinfo \ flux create hr podinfo \
--namespace=default \ --namespace=default \
@@ -134,7 +127,6 @@ type helmReleaseFlags struct {
chartVersion string chartVersion string
chartRef string chartRef string
targetNamespace string targetNamespace string
storageNamespace string
createNamespace bool createNamespace bool
valuesFiles []string valuesFiles []string
valuesFrom []string valuesFrom []string
@@ -158,7 +150,6 @@ func init() {
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)") createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'") createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace") createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.storageNamespace, "storage-namespace", "", "namespace to store the Helm release, defaults to the target namespace")
createHelmReleaseCmd.Flags().BoolVar(&helmReleaseArgs.createNamespace, "create-target-namespace", false, "create the target namespace if it does not exist") createHelmReleaseCmd.Flags().BoolVar(&helmReleaseArgs.createNamespace, "create-target-namespace", false, "create the target namespace if it does not exist")
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease") createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.reconcileStrategy, "reconcile-strategy", "ChartVersion", "the reconcile strategy for helm chart created by the helm release(accepted values: Revision and ChartRevision)") createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.reconcileStrategy, "reconcile-strategy", "ChartVersion", "the reconcile strategy for helm chart created by the helm release(accepted values: Revision and ChartRevision)")
@@ -174,10 +165,6 @@ func init() {
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
name := args[0] name := args[0]
if helmReleaseArgs.storageNamespace == "" && helmReleaseArgs.targetNamespace != "" {
helmReleaseArgs.storageNamespace = helmReleaseArgs.targetNamespace
}
if helmReleaseArgs.chart == "" && helmReleaseArgs.chartRef == "" { if helmReleaseArgs.chart == "" && helmReleaseArgs.chartRef == "" {
return fmt.Errorf("chart or chart-ref is required") return fmt.Errorf("chart or chart-ref is required")
} }
@@ -204,27 +191,15 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}, },
Spec: helmv2.HelmReleaseSpec{ Spec: helmv2.HelmReleaseSpec{
ReleaseName: helmReleaseArgs.name, ReleaseName: helmReleaseArgs.name,
DependsOn: utils.MakeDependsOn(helmReleaseArgs.dependsOn),
Interval: metav1.Duration{ Interval: metav1.Duration{
Duration: createArgs.interval, Duration: createArgs.interval,
}, },
TargetNamespace: helmReleaseArgs.targetNamespace, TargetNamespace: helmReleaseArgs.targetNamespace,
StorageNamespace: helmReleaseArgs.storageNamespace, Suspend: false,
Suspend: false,
}, },
} }
if len(helmReleaseArgs.dependsOn) > 0 {
ls := utils.MakeDependsOn(helmReleaseArgs.dependsOn)
hrDependsOn := make([]helmv2.DependencyReference, 0, len(ls))
for _, d := range ls {
hrDependsOn = append(hrDependsOn, helmv2.DependencyReference{
Name: d.Name,
Namespace: d.Namespace,
})
}
helmRelease.Spec.DependsOn = hrDependsOn
}
switch { switch {
case helmReleaseArgs.chart != "": case helmReleaseArgs.chart != "":
helmRelease.Spec.Chart = &helmv2.HelmChartTemplate{ helmRelease.Spec.Chart = &helmv2.HelmChartTemplate{
@@ -259,7 +234,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if helmReleaseArgs.kubeConfigSecretRef != "" { if helmReleaseArgs.kubeConfigSecretRef != "" {
helmRelease.Spec.KubeConfig = &meta.KubeConfigReference{ helmRelease.Spec.KubeConfig = &meta.KubeConfigReference{
SecretRef: &meta.SecretKeyReference{ SecretRef: meta.SecretKeyReference{
Name: helmReleaseArgs.kubeConfigSecretRef, Name: helmReleaseArgs.kubeConfigSecretRef,
}, },
} }

View File

@@ -29,18 +29,18 @@ import (
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var createImagePolicyCmd = &cobra.Command{ var createImagePolicyCmd = &cobra.Command{
Use: "policy [name]", Use: "policy [name]",
Short: "Create or update an ImagePolicy object", Short: "Create or update an ImagePolicy object",
Long: `The create image policy command generates an ImagePolicy resource. Long: withPreviewNote(`The create image policy command generates an ImagePolicy resource.
An ImagePolicy object calculates a "latest image" given an image An ImagePolicy object calculates a "latest image" given an image
repository and a policy, e.g., semver. repository and a policy, e.g., semver.
The image that sorts highest according to the policy is recorded in The image that sorts highest according to the policy is recorded in
the status of the object.`, the status of the object.`),
Example: ` # Create an ImagePolicy to select the latest stable release Example: ` # Create an ImagePolicy to select the latest stable release
flux create image policy podinfo \ flux create image policy podinfo \
--image-ref=podinfo \ --image-ref=podinfo \
@@ -81,6 +81,12 @@ func init() {
createImageCmd.AddCommand(createImagePolicyCmd) createImageCmd.AddCommand(createImagePolicyCmd)
} }
// getObservedGeneration is implemented here, since it's not
// (presently) needed elsewhere.
func (obj imagePolicyAdapter) getObservedGeneration() int64 {
return obj.ImagePolicy.Status.ObservedGeneration
}
func createImagePolicyRun(cmd *cobra.Command, args []string) error { func createImagePolicyRun(cmd *cobra.Command, args []string) error {
objectName := args[0] objectName := args[0]

View File

@@ -26,14 +26,14 @@ import (
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var createImageRepositoryCmd = &cobra.Command{ var createImageRepositoryCmd = &cobra.Command{
Use: "repository [name]", Use: "repository [name]",
Short: "Create or update an ImageRepository object", Short: "Create or update an ImageRepository object",
Long: `The create image repository command generates an ImageRepository resource. Long: withPreviewNote(`The create image repository command generates an ImageRepository resource.
An ImageRepository object specifies an image repository to scan.`, An ImageRepository object specifies an image repository to scan.`),
Example: ` # Create an ImageRepository object to scan the alpine image repository: Example: ` # Create an ImageRepository object to scan the alpine image repository:
flux create image repository alpine-repo --image alpine --interval 20m flux create image repository alpine-repo --image alpine --interval 20m

View File

@@ -22,16 +22,16 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
) )
var createImageUpdateCmd = &cobra.Command{ var createImageUpdateCmd = &cobra.Command{
Use: "update [name]", Use: "update [name]",
Short: "Create or update an ImageUpdateAutomation object", Short: "Create or update an ImageUpdateAutomation object",
Long: `The create image update command generates an ImageUpdateAutomation resource. Long: withPreviewNote(`The create image update command generates an ImageUpdateAutomation resource.
An ImageUpdateAutomation object specifies an automated update to images An ImageUpdateAutomation object specifies an automated update to images
mentioned in YAMLs in a git repository.`, mentioned in YAMLs in a git repository.`),
Example: ` # Configure image updates for the main repository created by flux bootstrap Example: ` # Configure image updates for the main repository created by flux bootstrap
flux create image update flux-system \ flux create image update flux-system \
--git-repo-ref=flux-system \ --git-repo-ref=flux-system \

View File

@@ -153,6 +153,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
Labels: kslabels, Labels: kslabels,
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
DependsOn: utils.MakeDependsOn(kustomizationArgs.dependsOn),
Interval: metav1.Duration{ Interval: metav1.Duration{
Duration: createArgs.interval, Duration: createArgs.interval,
}, },
@@ -168,21 +169,9 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}, },
} }
if len(kustomizationArgs.dependsOn) > 0 {
ls := utils.MakeDependsOn(kustomizationArgs.dependsOn)
ksDependsOn := make([]kustomizev1.DependencyReference, 0, len(ls))
for _, d := range ls {
ksDependsOn = append(ksDependsOn, kustomizev1.DependencyReference{
Name: d.Name,
Namespace: d.Namespace,
})
}
kustomization.Spec.DependsOn = ksDependsOn
}
if kustomizationArgs.kubeConfigSecretRef != "" { if kustomizationArgs.kubeConfigSecretRef != "" {
kustomization.Spec.KubeConfig = &meta.KubeConfigReference{ kustomization.Spec.KubeConfig = &meta.KubeConfigReference{
SecretRef: &meta.SecretKeyReference{ SecretRef: meta.SecretKeyReference{
Name: kustomizationArgs.kubeConfigSecretRef, Name: kustomizationArgs.kubeConfigSecretRef,
}, },
} }

View File

@@ -172,7 +172,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme) return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
} }
secret, err := sourcesecret.GenerateGit(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -99,7 +99,7 @@ func createSecretGitHubAppCmdRun(cmd *cobra.Command, args []string) error {
opts.GitHubAppBaseURL = secretGitHubAppArgs.baseURL opts.GitHubAppBaseURL = secretGitHubAppArgs.baseURL
} }
secret, err := sourcesecret.GenerateGitHubApp(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -83,12 +83,10 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
} }
var certFile, keyFile []byte var certFile, keyFile []byte
if secretHelmArgs.tlsCrtFile != "" { if secretHelmArgs.tlsCrtFile != "" && secretHelmArgs.tlsKeyFile != "" {
if certFile, err = os.ReadFile(secretHelmArgs.tlsCrtFile); err != nil { if certFile, err = os.ReadFile(secretHelmArgs.tlsCrtFile); err != nil {
return fmt.Errorf("failed to read cert file: %w", err) return fmt.Errorf("failed to read cert file: %w", err)
} }
}
if secretHelmArgs.tlsKeyFile != "" {
if keyFile, err = os.ReadFile(secretHelmArgs.tlsKeyFile); err != nil { if keyFile, err = os.ReadFile(secretHelmArgs.tlsKeyFile); err != nil {
return fmt.Errorf("failed to read key file: %w", err) return fmt.Errorf("failed to read key file: %w", err)
} }
@@ -104,7 +102,7 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
TLSCrt: certFile, TLSCrt: certFile,
TLSKey: keyFile, TLSKey: keyFile,
} }
secret, err := sourcesecret.GenerateHelm(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -132,7 +132,7 @@ func createSecretNotationCmdRun(cmd *cobra.Command, args []string) error {
VerificationCrts: caCerts, VerificationCrts: caCerts,
TrustPolicy: policy, TrustPolicy: policy,
} }
secret, err := sourcesecret.GenerateNotation(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -92,7 +92,7 @@ func createSecretOCICmdRun(cmd *cobra.Command, args []string) error {
Username: secretOCIArgs.username, Username: secretOCIArgs.username,
} }
secret, err := sourcesecret.GenerateOCI(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -83,7 +83,7 @@ func createSecretProxyCmdRun(cmd *cobra.Command, args []string) error {
Username: secretProxyArgs.username, Username: secretProxyArgs.username,
Password: secretProxyArgs.password, Password: secretProxyArgs.password,
} }
secret, err := sourcesecret.GenerateProxy(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -84,18 +84,16 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
if secretTLSArgs.tlsCrtFile != "" { if secretTLSArgs.tlsCrtFile != "" && secretTLSArgs.tlsKeyFile != "" {
if opts.TLSCrt, err = os.ReadFile(secretTLSArgs.tlsCrtFile); err != nil { if opts.TLSCrt, err = os.ReadFile(secretTLSArgs.tlsCrtFile); err != nil {
return fmt.Errorf("failed to read cert file: %w", err) return fmt.Errorf("failed to read cert file: %w", err)
} }
}
if secretTLSArgs.tlsKeyFile != "" {
if opts.TLSKey, err = os.ReadFile(secretTLSArgs.tlsKeyFile); err != nil { if opts.TLSKey, err = os.ReadFile(secretTLSArgs.tlsKeyFile); err != nil {
return fmt.Errorf("failed to read key file: %w", err) return fmt.Errorf("failed to read key file: %w", err)
} }
} }
secret, err := sourcesecret.GenerateTLS(opts) secret, err := sourcesecret.Generate(opts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -19,6 +19,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -113,6 +114,12 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
tmpDir, err := os.MkdirTemp("", name)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
var ignorePaths *string var ignorePaths *string
if len(sourceBucketArgs.ignorePaths) > 0 { if len(sourceBucketArgs.ignorePaths) > 0 {
ignorePathsStr := strings.Join(sourceBucketArgs.ignorePaths, "\n") ignorePathsStr := strings.Join(sourceBucketArgs.ignorePaths, "\n")

View File

@@ -44,26 +44,25 @@ import (
) )
type sourceGitFlags struct { type sourceGitFlags struct {
url string url string
branch string branch string
tag string tag string
semver string semver string
refName string refName string
commit string commit string
username string username string
password string password string
keyAlgorithm flags.PublicKeyAlgorithm keyAlgorithm flags.PublicKeyAlgorithm
keyRSABits flags.RSAKeyBits keyRSABits flags.RSAKeyBits
keyECDSACurve flags.ECDSACurve keyECDSACurve flags.ECDSACurve
secretRef string secretRef string
proxySecretRef string proxySecretRef string
provider flags.SourceGitProvider provider flags.SourceGitProvider
caFile string caFile string
privateKeyFile string privateKeyFile string
recurseSubmodules bool recurseSubmodules bool
silent bool silent bool
ignorePaths []string ignorePaths []string
sparseCheckoutPaths []string
} }
var createSourceGitCmd = &cobra.Command{ var createSourceGitCmd = &cobra.Command{
@@ -155,7 +154,6 @@ func init() {
"when enabled, configures the GitRepository source to initialize and include Git submodules in the artifact it produces") "when enabled, configures the GitRepository source to initialize and include Git submodules in the artifact it produces")
createSourceGitCmd.Flags().BoolVarP(&sourceGitArgs.silent, "silent", "s", false, "assumes the deploy key is already setup, skips confirmation") createSourceGitCmd.Flags().BoolVarP(&sourceGitArgs.silent, "silent", "s", false, "assumes the deploy key is already setup, skips confirmation")
createSourceGitCmd.Flags().StringSliceVar(&sourceGitArgs.ignorePaths, "ignore-paths", nil, "set paths to ignore in git resource (can specify multiple paths with commas: path1,path2)") createSourceGitCmd.Flags().StringSliceVar(&sourceGitArgs.ignorePaths, "ignore-paths", nil, "set paths to ignore in git resource (can specify multiple paths with commas: path1,path2)")
createSourceGitCmd.Flags().StringSliceVar(&sourceGitArgs.sparseCheckoutPaths, "sparse-checkout-paths", nil, "set paths to sparse checkout in git resource (can specify multiple paths with commas: path1,path2)")
createSourceCmd.AddCommand(createSourceGitCmd) createSourceCmd.AddCommand(createSourceGitCmd)
} }
@@ -191,6 +189,12 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("specifying a CA file is not supported for Git over SSH") return fmt.Errorf("specifying a CA file is not supported for Git over SSH")
} }
tmpDir, err := os.MkdirTemp("", name)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
sourceLabels, err := parseLabels() sourceLabels, err := parseLabels()
if err != nil { if err != nil {
return err return err
@@ -216,7 +220,6 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
RecurseSubmodules: sourceGitArgs.recurseSubmodules, RecurseSubmodules: sourceGitArgs.recurseSubmodules,
Reference: &sourcev1.GitRepositoryRef{}, Reference: &sourcev1.GitRepositoryRef{},
Ignore: ignorePaths, Ignore: ignorePaths,
SparseCheckout: sourceGitArgs.sparseCheckoutPaths,
}, },
} }
@@ -299,7 +302,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
secretOpts.Username = sourceGitArgs.username secretOpts.Username = sourceGitArgs.username
secretOpts.Password = sourceGitArgs.password secretOpts.Password = sourceGitArgs.password
} }
secret, err := sourcesecret.GenerateGit(secretOpts) secret, err := sourcesecret.Generate(secretOpts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -87,7 +87,7 @@ func (r *reconciler) conditionFunc() (bool, error) {
} }
func TestCreateSourceGitExport(t *testing.T) { func TestCreateSourceGitExport(t *testing.T) {
var command = "create source git podinfo --url=https://github.com/stefanprodan/podinfo --branch=master --sparse-checkout-paths .cosign,non-existent-dir/ --ignore-paths .cosign,non-existent-dir/ -n default --interval 1m --export --timeout=" + testTimeout.String() var command = "create source git podinfo --url=https://github.com/stefanprodan/podinfo --branch=master --ignore-paths .cosign,non-existent-dir/ -n default --interval 1m --export --timeout=" + testTimeout.String()
cases := []struct { cases := []struct {
name string name string
@@ -101,7 +101,7 @@ func TestCreateSourceGitExport(t *testing.T) {
}, },
{ {
name: "no args", name: "no args",
args: "create source git --url=https://github.com/stefanprodan/podinfo", args: "create secret git",
assert: assertError("name is required"), assert: assertError("name is required"),
}, },
{ {
@@ -204,13 +204,12 @@ func TestCreateSourceGit(t *testing.T) {
ObservedGeneration: repo.GetGeneration(), ObservedGeneration: repo.GetGeneration(),
} }
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition) apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
repo.Status.Artifact = &meta.Artifact{ repo.Status.Artifact = &sourcev1.Artifact{
Path: "some-path", Path: "some-path",
Revision: "v1", Revision: "v1",
LastUpdateTime: metav1.Time{ LastUpdateTime: metav1.Time{
Time: time.Now(), Time: time.Now(),
}, },
Digest: "sha256:1234567890abcdef",
} }
repo.Status.ObservedGeneration = repo.GetGeneration() repo.Status.ObservedGeneration = repo.GetGeneration()
}, },

View File

@@ -114,6 +114,12 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
tmpDir, err := os.MkdirTemp("", name)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
if _, err := url.Parse(sourceHelmArgs.url); err != nil { if _, err := url.Parse(sourceHelmArgs.url); err != nil {
return fmt.Errorf("url parse failed: %w", err) return fmt.Errorf("url parse failed: %w", err)
} }
@@ -196,7 +202,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
TLSKey: keyFile, TLSKey: keyFile,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
secret, err := sourcesecret.GenerateHelm(secretOpts) secret, err := sourcesecret.Generate(secretOpts)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -21,6 +21,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/v2/internal/utils"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
@@ -31,8 +32,6 @@ import (
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/v2/internal/utils"
) )
var createTenantCmd = &cobra.Command{ var createTenantCmd = &cobra.Command{
@@ -60,7 +59,6 @@ const (
type tenantFlags struct { type tenantFlags struct {
namespaces []string namespaces []string
clusterRole string clusterRole string
account string
} }
var tenantArgs tenantFlags var tenantArgs tenantFlags
@@ -68,7 +66,6 @@ var tenantArgs tenantFlags
func init() { func init() {
createTenantCmd.Flags().StringSliceVar(&tenantArgs.namespaces, "with-namespace", nil, "namespace belonging to this tenant") createTenantCmd.Flags().StringSliceVar(&tenantArgs.namespaces, "with-namespace", nil, "namespace belonging to this tenant")
createTenantCmd.Flags().StringVar(&tenantArgs.clusterRole, "cluster-role", "cluster-admin", "cluster role of the tenant role binding") createTenantCmd.Flags().StringVar(&tenantArgs.clusterRole, "cluster-role", "cluster-admin", "cluster role of the tenant role binding")
createTenantCmd.Flags().StringVar(&tenantArgs.account, "with-service-account", "", "service account belonging to this tenant")
createCmd.AddCommand(createTenantCmd) createCmd.AddCommand(createTenantCmd)
} }
@@ -110,17 +107,9 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
} }
namespaces = append(namespaces, namespace) namespaces = append(namespaces, namespace)
accountName := tenant
if tenantArgs.account != "" {
accountName = tenantArgs.account
}
if err := validation.IsQualifiedName(accountName); len(err) > 0 {
return fmt.Errorf("invalid service-account name '%s': %v", accountName, err)
}
account := corev1.ServiceAccount{ account := corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: accountName, Name: tenant,
Namespace: ns, Namespace: ns,
Labels: objLabels, Labels: objLabels,
}, },
@@ -142,7 +131,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
}, },
{ {
Kind: "ServiceAccount", Kind: "ServiceAccount",
Name: accountName, Name: tenant,
Namespace: ns, Namespace: ns,
}, },
}, },
@@ -293,10 +282,10 @@ func exportTenant(namespace corev1.Namespace, account corev1.ServiceAccount, rol
if err != nil { if err != nil {
return err return err
} }
data = bytes.Replace(data, []byte("spec: {}\n"), []byte(""), 1)
printlnStdout("---") fmt.Println("---")
printlnStdout(resourceToString(data)) data = bytes.Replace(data, []byte("spec: {}\n"), []byte(""), 1)
fmt.Println(resourceToString(data))
account.TypeMeta = metav1.TypeMeta{ account.TypeMeta = metav1.TypeMeta{
APIVersion: "v1", APIVersion: "v1",
@@ -306,10 +295,10 @@ func exportTenant(namespace corev1.Namespace, account corev1.ServiceAccount, rol
if err != nil { if err != nil {
return err return err
} }
data = bytes.Replace(data, []byte("spec: {}\n"), []byte(""), 1)
printlnStdout("---") fmt.Println("---")
printlnStdout(resourceToString(data)) data = bytes.Replace(data, []byte("spec: {}\n"), []byte(""), 1)
fmt.Println(resourceToString(data))
roleBinding.TypeMeta = metav1.TypeMeta{ roleBinding.TypeMeta = metav1.TypeMeta{
APIVersion: "rbac.authorization.k8s.io/v1", APIVersion: "rbac.authorization.k8s.io/v1",
@@ -320,8 +309,8 @@ func exportTenant(namespace corev1.Namespace, account corev1.ServiceAccount, rol
return err return err
} }
printlnStdout("---") fmt.Println("---")
printlnStdout(resourceToString(data)) fmt.Println(resourceToString(data))
return nil return nil
} }

View File

@@ -1,68 +0,0 @@
//go:build e2e
// +build e2e
/*
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.
*/
package main
import (
"testing"
)
func TestCreateTenant(t *testing.T) {
tests := []struct {
name string
args string
assert assertFunc
}{
{
name: "no args",
args: "create tenant",
assert: assertError("name is required"),
},
{
name: "no namespace",
args: "create tenant dev-team --cluster-role=cluster-admin",
assert: assertError("with-namespace is required"),
},
{
name: "basic tenant",
args: "create tenant dev-team --with-namespace=apps --cluster-role=cluster-admin --export",
assert: assertGoldenFile("./testdata/create_tenant/tenant-basic.yaml"),
},
{
name: "tenant with custom serviceaccount",
args: "create tenant dev-team --with-namespace=apps --cluster-role=cluster-admin --with-service-account=flux-tenant --export",
assert: assertGoldenFile("./testdata/create_tenant/tenant-with-service-account.yaml"),
},
{
name: "tenant with custom cluster role",
args: "create tenant dev-team --with-namespace=apps --cluster-role=custom-role --export",
assert: assertGoldenFile("./testdata/create_tenant/tenant-with-cluster-role.yaml"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := cmdTestCase{
args: tt.args,
assert: tt.assert,
}
cmd.runTestCmd(t)
})
}
}

View File

@@ -40,19 +40,15 @@ WARNING: This command will print sensitive information if Kubernetes Secrets are
flux debug hr podinfo --show-status flux debug hr podinfo --show-status
# Export the final values of a Helm release composed from referred ConfigMaps and Secrets # Export the final values of a Helm release composed from referred ConfigMaps and Secrets
flux debug hr podinfo --show-values > values.yaml flux debug hr podinfo --show-values > values.yaml`,
# Print the reconciliation history of a Helm release
flux debug hr podinfo --show-history`,
RunE: debugHelmReleaseCmdRun, RunE: debugHelmReleaseCmdRun,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)), ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
} }
type debugHelmReleaseFlags struct { type debugHelmReleaseFlags struct {
showStatus bool showStatus bool
showValues bool showValues bool
showHistory bool
} }
var debugHelmReleaseArgs debugHelmReleaseFlags var debugHelmReleaseArgs debugHelmReleaseFlags
@@ -60,25 +56,15 @@ var debugHelmReleaseArgs debugHelmReleaseFlags
func init() { func init() {
debugHelmReleaseCmd.Flags().BoolVar(&debugHelmReleaseArgs.showStatus, "show-status", false, "print the status of the Helm release") debugHelmReleaseCmd.Flags().BoolVar(&debugHelmReleaseArgs.showStatus, "show-status", false, "print the status of the Helm release")
debugHelmReleaseCmd.Flags().BoolVar(&debugHelmReleaseArgs.showValues, "show-values", false, "print the final values of the Helm release") debugHelmReleaseCmd.Flags().BoolVar(&debugHelmReleaseArgs.showValues, "show-values", false, "print the final values of the Helm release")
debugHelmReleaseCmd.Flags().BoolVar(&debugHelmReleaseArgs.showHistory, "show-history", false, "print the reconciliation history of the Helm release")
debugCmd.AddCommand(debugHelmReleaseCmd) debugCmd.AddCommand(debugHelmReleaseCmd)
} }
func debugHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { func debugHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
name := args[0] name := args[0]
flagsSet := 0 if (!debugHelmReleaseArgs.showStatus && !debugHelmReleaseArgs.showValues) ||
if debugHelmReleaseArgs.showStatus { (debugHelmReleaseArgs.showStatus && debugHelmReleaseArgs.showValues) {
flagsSet++ return fmt.Errorf("either --show-status or --show-values must be set")
}
if debugHelmReleaseArgs.showValues {
flagsSet++
}
if debugHelmReleaseArgs.showHistory {
flagsSet++
}
if flagsSet != 1 {
return fmt.Errorf("exactly one of --show-status, --show-values, or --show-history must be set")
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
@@ -123,20 +109,5 @@ func debugHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
rootCmd.Print(string(values)) rootCmd.Print(string(values))
} }
if debugHelmReleaseArgs.showHistory {
if len(hr.Status.History) == 0 {
hr.Status.History = helmv2.Snapshots{}
}
history, err := yaml.Marshal(hr.Status.History)
if err != nil {
return err
}
rootCmd.Println("# History documentation: https://fluxcd.io/flux/components/helm/helmreleases/#history")
rootCmd.Print(string(history))
return nil
}
return nil return nil
} }

View File

@@ -56,18 +56,6 @@ func TestDebugHelmRelease(t *testing.T) {
"testdata/debug_helmrelease/values-from.golden.yaml", "testdata/debug_helmrelease/values-from.golden.yaml",
tmpl, tmpl,
}, },
{
"debug history",
"debug helmrelease test-with-history --show-history --show-status=false",
"testdata/debug_helmrelease/history.golden.yaml",
tmpl,
},
{
"debug history empty",
"debug helmrelease test-values-inline --show-history --show-status=false",
"testdata/debug_helmrelease/history-empty.golden.yaml",
tmpl,
},
} }
for _, tt := range cases { for _, tt := range cases {

View File

@@ -24,7 +24,6 @@ import (
"strings" "strings"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/kustomize" "github.com/fluxcd/pkg/kustomize"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -45,19 +44,15 @@ WARNING: This command will print sensitive information if Kubernetes Secrets are
flux debug ks podinfo --show-status flux debug ks podinfo --show-status
# Export the final variables used for post-build substitutions composed from referred ConfigMaps and Secrets # Export the final variables used for post-build substitutions composed from referred ConfigMaps and Secrets
flux debug ks podinfo --show-vars > vars.env flux debug ks podinfo --show-vars > vars.env`,
# Print the reconciliation history of a Flux Kustomization
flux debug ks podinfo --show-history`,
RunE: debugKustomizationCmdRun, RunE: debugKustomizationCmdRun,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)), ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
} }
type debugKustomizationFlags struct { type debugKustomizationFlags struct {
showStatus bool showStatus bool
showVars bool showVars bool
showHistory bool
} }
var debugKustomizationArgs debugKustomizationFlags var debugKustomizationArgs debugKustomizationFlags
@@ -65,25 +60,15 @@ var debugKustomizationArgs debugKustomizationFlags
func init() { func init() {
debugKustomizationCmd.Flags().BoolVar(&debugKustomizationArgs.showStatus, "show-status", false, "print the status of the Flux Kustomization") debugKustomizationCmd.Flags().BoolVar(&debugKustomizationArgs.showStatus, "show-status", false, "print the status of the Flux Kustomization")
debugKustomizationCmd.Flags().BoolVar(&debugKustomizationArgs.showVars, "show-vars", false, "print the final vars of the Flux Kustomization in dot env format") debugKustomizationCmd.Flags().BoolVar(&debugKustomizationArgs.showVars, "show-vars", false, "print the final vars of the Flux Kustomization in dot env format")
debugKustomizationCmd.Flags().BoolVar(&debugKustomizationArgs.showHistory, "show-history", false, "print the reconciliation history of the Flux Kustomization")
debugCmd.AddCommand(debugKustomizationCmd) debugCmd.AddCommand(debugKustomizationCmd)
} }
func debugKustomizationCmdRun(cmd *cobra.Command, args []string) error { func debugKustomizationCmdRun(cmd *cobra.Command, args []string) error {
name := args[0] name := args[0]
flagsSet := 0 if (!debugKustomizationArgs.showStatus && !debugKustomizationArgs.showVars) ||
if debugKustomizationArgs.showStatus { (debugKustomizationArgs.showStatus && debugKustomizationArgs.showVars) {
flagsSet++ return fmt.Errorf("either --show-status or --show-vars must be set")
}
if debugKustomizationArgs.showVars {
flagsSet++
}
if debugKustomizationArgs.showHistory {
flagsSet++
}
if flagsSet != 1 {
return fmt.Errorf("exactly one of --show-status, --show-vars, or --show-history must be set")
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
@@ -145,20 +130,5 @@ func debugKustomizationCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
if debugKustomizationArgs.showHistory {
if len(ks.Status.History) == 0 {
ks.Status.History = meta.History{}
}
history, err := yaml.Marshal(ks.Status.History)
if err != nil {
return err
}
rootCmd.Println("# History documentation: https://fluxcd.io/flux/components/kustomize/kustomizations/#history")
rootCmd.Print(string(history))
return nil
}
return nil return nil
} }

View File

@@ -55,17 +55,6 @@ func TestDebugKustomization(t *testing.T) {
"debug ks test-from --show-vars --show-status=false", "debug ks test-from --show-vars --show-status=false",
"testdata/debug_kustomization/vars-from.golden.env", "testdata/debug_kustomization/vars-from.golden.env",
tmpl, tmpl,
}, {
"debug history",
"debug ks test-with-history --show-history --show-status=false",
"testdata/debug_kustomization/history.golden.yaml",
tmpl,
},
{
"debug history empty",
"debug ks test --show-history --show-status=false",
"testdata/debug_kustomization/history-empty.golden.yaml",
tmpl,
}, },
} }

View File

@@ -19,13 +19,13 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var deleteImagePolicyCmd = &cobra.Command{ var deleteImagePolicyCmd = &cobra.Command{
Use: "policy [name]", Use: "policy [name]",
Short: "Delete an ImagePolicy object", Short: "Delete an ImagePolicy object",
Long: `The delete image policy command deletes the given ImagePolicy from the cluster.`, Long: withPreviewNote(`The delete image policy command deletes the given ImagePolicy from the cluster.`),
Example: ` # Delete an image policy Example: ` # Delete an image policy
flux delete image policy alpine3.x`, flux delete image policy alpine3.x`,
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)), ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),

View File

@@ -19,13 +19,13 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var deleteImageRepositoryCmd = &cobra.Command{ var deleteImageRepositoryCmd = &cobra.Command{
Use: "repository [name]", Use: "repository [name]",
Short: "Delete an ImageRepository object", Short: "Delete an ImageRepository object",
Long: "The delete image repository command deletes the given ImageRepository from the cluster.", Long: withPreviewNote("The delete image repository command deletes the given ImageRepository from the cluster."),
Example: ` # Delete an image repository Example: ` # Delete an image repository
flux delete image repository alpine`, flux delete image repository alpine`,
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),

View File

@@ -19,13 +19,13 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
) )
var deleteImageUpdateCmd = &cobra.Command{ var deleteImageUpdateCmd = &cobra.Command{
Use: "update [name]", Use: "update [name]",
Short: "Delete an ImageUpdateAutomation object", Short: "Delete an ImageUpdateAutomation object",
Long: `The delete image update command deletes the given ImageUpdateAutomation from the cluster.`, Long: withPreviewNote(`The delete image update command deletes the given ImageUpdateAutomation from the cluster.`),
Example: ` # Delete an image update automation Example: ` # Delete an image update automation
flux delete image update latest-images`, flux delete image update latest-images`,
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)), ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),

View File

@@ -93,7 +93,7 @@ func diffArtifactCmdRun(cmd *cobra.Command, args []string) error {
if diffArtifactArgs.provider.String() != sourcev1.GenericOCIProvider { if diffArtifactArgs.provider.String() != sourcev1.GenericOCIProvider {
logger.Actionf("logging in to registry with provider credentials") logger.Actionf("logging in to registry with provider credentials")
opt, _, err := loginWithProvider(ctx, url, diffArtifactArgs.provider.String()) opt, err := loginWithProvider(ctx, url, diffArtifactArgs.provider.String())
if err != nil { if err != nil {
return fmt.Errorf("error during login with provider: %w", err) return fmt.Errorf("error during login with provider: %w", err)
} }

View File

@@ -27,7 +27,6 @@ import (
"github.com/fluxcd/flux2/v2/internal/build" "github.com/fluxcd/flux2/v2/internal/build"
"github.com/fluxcd/pkg/ssa" "github.com/fluxcd/pkg/ssa"
"github.com/fluxcd/pkg/ssa/normalize"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
@@ -152,7 +151,7 @@ func createObjectFromFile(objectFile string, templateValues map[string]string, t
t.Fatalf("Error decoding yaml file '%s': %v", objectFile, err) t.Fatalf("Error decoding yaml file '%s': %v", objectFile, err)
} }
if err := normalize.UnstructuredList(clientObjects); err != nil { if err := ssa.SetNativeKindsDefaults(clientObjects); err != nil {
t.Fatalf("Error setting native kinds defaults for '%s': %v", objectFile, err) t.Fatalf("Error setting native kinds defaults for '%s': %v", objectFile, err)
} }

View File

@@ -20,6 +20,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"sort" "sort"
"strings" "strings"
"time" "time"
@@ -39,13 +40,12 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2" helmv2 "github.com/fluxcd/helm-controller/api/v2"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1" notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3" notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
swapi "github.com/fluxcd/source-watcher/api/v2/v1beta1"
"github.com/fluxcd/flux2/v2/internal/utils" "github.com/fluxcd/flux2/v2/internal/utils"
"github.com/fluxcd/flux2/v2/pkg/printers" "github.com/fluxcd/flux2/v2/pkg/printers"
@@ -112,12 +112,7 @@ func eventsCmdRun(cmd *cobra.Command, args []string) error {
} }
var diffRefNs bool var diffRefNs bool
// Build the base list options. When --all-namespaces is set we must NOT constrain the clientListOpts := []client.ListOption{client.InNamespace(*kubeconfigArgs.Namespace)}
// query to a single namespace, otherwise we silently return a partial result set.
clientListOpts := []client.ListOption{}
if !eventArgs.allNamespaces {
clientListOpts = append(clientListOpts, client.InNamespace(*kubeconfigArgs.Namespace))
}
var refListOpts [][]client.ListOption var refListOpts [][]client.ListOption
if eventArgs.forSelector != "" { if eventArgs.forSelector != "" {
kind, name := getKindNameFromSelector(eventArgs.forSelector) kind, name := getKindNameFromSelector(eventArgs.forSelector)
@@ -251,7 +246,7 @@ func eventsCmdWatchRun(ctx context.Context, kubeclient client.WithWatch, listOpt
hdr = getHeaders(showNs) hdr = getHeaders(showNs)
firstIteration = false firstIteration = false
} }
return printers.TablePrinter(hdr).Print(rootCmd.OutOrStdout(), [][]string{rows}) return printers.TablePrinter(hdr).Print(os.Stdout, [][]string{rows})
} }
for _, refOpts := range refListOpts { for _, refOpts := range refListOpts {
@@ -455,7 +450,6 @@ var fluxKindMap = refMap{
sourcev1.HelmRepositoryKind: {gvk: sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)}, sourcev1.HelmRepositoryKind: {gvk: sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)},
autov1.ImageUpdateAutomationKind: {gvk: autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)}, autov1.ImageUpdateAutomationKind: {gvk: autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)},
imagev1.ImageRepositoryKind: {gvk: imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)}, imagev1.ImageRepositoryKind: {gvk: imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)},
swapi.ArtifactGeneratorKind: {gvk: swapi.GroupVersion.WithKind(swapi.ArtifactGeneratorKind)},
} }
func ignoreEvent(e corev1.Event) bool { func ignoreEvent(e corev1.Event) bool {

View File

@@ -140,7 +140,7 @@ spec:
address: https://hooks.slack.com/services/mock address: https://hooks.slack.com/services/mock
type: slack type: slack
--- ---
apiVersion: image.toolkit.fluxcd.io/v1 apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy kind: ImagePolicy
metadata: metadata:
name: podinfo name: podinfo

View File

@@ -109,13 +109,13 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
return nil return nil
} }
func printExport(export any) error { func printExport(export interface{}) error {
data, err := yaml.Marshal(export) data, err := yaml.Marshal(export)
if err != nil { if err != nil {
return err return err
} }
printlnStdout("---") rootCmd.Println("---")
printlnStdout(resourceToString(data)) rootCmd.Println(resourceToString(data))
return nil return nil
} }

View File

@@ -1,31 +0,0 @@
/*
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.
*/
package main
import (
"github.com/spf13/cobra"
)
var exportArtifactCmd = &cobra.Command{
Use: "artifact",
Short: "Export artifact objects",
Long: `The export artifact sub-commands export artifacts objects in YAML format.`,
}
func init() {
exportCmd.AddCommand(exportArtifactCmd)
}

View File

@@ -1,72 +0,0 @@
/*
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.
*/
package main
import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
swapi "github.com/fluxcd/source-watcher/api/v2/v1beta1"
)
var exportArtifactGeneratorCmd = &cobra.Command{
Use: "generator [name]",
Short: "Export ArtifactGenerator resources in YAML format",
Long: "The export artifact generator command exports one or all ArtifactGenerator resources in YAML format.",
Example: ` # Export all ArtifactGenerator resources
flux export artifact generator --all > artifact-generators.yaml
# Export a specific generator
flux export artifact generator my-generator > my-generator.yaml`,
ValidArgsFunction: resourceNamesCompletionFunc(swapi.GroupVersion.WithKind(swapi.ArtifactGeneratorKind)),
RunE: exportCommand{
object: artifactGeneratorAdapter{&swapi.ArtifactGenerator{}},
list: artifactGeneratorListAdapter{&swapi.ArtifactGeneratorList{}},
}.run,
}
func init() {
exportArtifactCmd.AddCommand(exportArtifactGeneratorCmd)
}
// Export returns an ArtifactGenerator value which has
// extraneous information stripped out.
func exportArtifactGenerator(item *swapi.ArtifactGenerator) interface{} {
gvk := swapi.GroupVersion.WithKind(swapi.ArtifactGeneratorKind)
export := swapi.ArtifactGenerator{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: item.Name,
Namespace: item.Namespace,
Labels: item.Labels,
Annotations: item.Annotations,
},
Spec: item.Spec,
}
return export
}
func (ex artifactGeneratorAdapter) export() interface{} {
return exportArtifactGenerator(ex.ArtifactGenerator)
}
func (ex artifactGeneratorListAdapter) exportItem(i int) interface{} {
return exportArtifactGenerator(&ex.ArtifactGeneratorList.Items[i])
}

View File

@@ -20,13 +20,13 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var exportImagePolicyCmd = &cobra.Command{ var exportImagePolicyCmd = &cobra.Command{
Use: "policy [name]", Use: "policy [name]",
Short: "Export ImagePolicy resources in YAML format", Short: "Export ImagePolicy resources in YAML format",
Long: "The export image policy command exports one or all ImagePolicy resources in YAML format.", Long: withPreviewNote("The export image policy command exports one or all ImagePolicy resources in YAML format."),
Example: ` # Export all ImagePolicy resources Example: ` # Export all ImagePolicy resources
flux export image policy --all > image-policies.yaml flux export image policy --all > image-policies.yaml

View File

@@ -20,13 +20,13 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var exportImageRepositoryCmd = &cobra.Command{ var exportImageRepositoryCmd = &cobra.Command{
Use: "repository [name]", Use: "repository [name]",
Short: "Export ImageRepository resources in YAML format", Short: "Export ImageRepository resources in YAML format",
Long: "The export image repository command exports one or all ImageRepository resources in YAML format.", Long: withPreviewNote("The export image repository command exports one or all ImageRepository resources in YAML format."),
Example: ` # Export all ImageRepository resources Example: ` # Export all ImageRepository resources
flux export image repository --all > image-repositories.yaml flux export image repository --all > image-repositories.yaml

View File

@@ -20,13 +20,13 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
) )
var exportImageUpdateCmd = &cobra.Command{ var exportImageUpdateCmd = &cobra.Command{
Use: "update [name]", Use: "update [name]",
Short: "Export ImageUpdateAutomation resources in YAML format", Short: "Export ImageUpdateAutomation resources in YAML format",
Long: "The export image update command exports one or all ImageUpdateAutomation resources in YAML format.", Long: withPreviewNote("The export image update command exports one or all ImageUpdateAutomation resources in YAML format."),
Example: ` # Export all ImageUpdateAutomation resources Example: ` # Export all ImageUpdateAutomation resources
flux export image update --all > updates.yaml flux export image update --all > updates.yaml

View File

@@ -1,32 +0,0 @@
/*
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.
*/
package main
import (
"github.com/spf13/cobra"
)
var getArtifactCmd = &cobra.Command{
Use: "artifacts",
Aliases: []string{"artifact"},
Short: "Get artifact object status",
Long: `The get artifact sub-commands print the status of artifact objects.`,
}
func init() {
getCmd.AddCommand(getArtifactCmd)
}

View File

@@ -1,93 +0,0 @@
/*
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.
*/
package main
import (
"fmt"
"strconv"
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"k8s.io/apimachinery/pkg/runtime"
swapi "github.com/fluxcd/source-watcher/api/v2/v1beta1"
)
var getArtifactGeneratorCmd = &cobra.Command{
Use: "generators",
Aliases: []string{"generator"},
Short: "Get artifact generator statuses",
Long: `The get artifact generator command prints the statuses of the resources.`,
Example: ` # List all ArtifactGenerators and their status
flux get artifact generators`,
ValidArgsFunction: resourceNamesCompletionFunc(swapi.GroupVersion.WithKind(swapi.ArtifactGeneratorKind)),
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: receiverType,
list: artifactGeneratorListAdapter{&swapi.ArtifactGeneratorList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*swapi.ArtifactGenerator)
if !ok {
return nil, fmt.Errorf("impossible to cast type %#v generator", obj)
}
sink := artifactGeneratorListAdapter{&swapi.ArtifactGeneratorList{
Items: []swapi.ArtifactGenerator{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
getArtifactCmd.AddCommand(getArtifactGeneratorCmd)
}
func (s artifactGeneratorListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind),
cases.Title(language.English).String(strconv.FormatBool(item.IsDisabled())), status, msg)
}
func (s artifactGeneratorListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Suspended", "Ready", "Message"}
if includeNamespace {
return append(namespaceHeader, headers...)
}
return headers
}
func (s artifactGeneratorListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -19,14 +19,14 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var getImageAllCmd = &cobra.Command{ var getImageAllCmd = &cobra.Command{
Use: "all", Use: "all",
Short: "Get all image statuses", Short: "Get all image statuses",
Long: "The get image sub-commands print the statuses of all image objects.", Long: withPreviewNote("The get image sub-commands print the statuses of all image objects."),
Example: ` # List all image objects in a namespace Example: ` # List all image objects in a namespace
flux get images all --namespace=flux-system flux get images all --namespace=flux-system

View File

@@ -22,13 +22,13 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var getImagePolicyCmd = &cobra.Command{ var getImagePolicyCmd = &cobra.Command{
Use: "policy", Use: "policy",
Short: "Get ImagePolicy status", Short: "Get ImagePolicy status",
Long: "The get image policy command prints the status of ImagePolicy objects.", Long: withPreviewNote("The get image policy command prints the status of ImagePolicy objects."),
Example: ` # List all image policies and their status Example: ` # List all image policies and their status
flux get image policy flux get image policy
@@ -74,16 +74,11 @@ func init() {
func (s imagePolicyListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string { func (s imagePolicyListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
var image, tag string return append(nameColumns(&item, includeNamespace, includeKind), item.Status.LatestImage, status, msg)
if ref := item.Status.LatestRef; ref != nil {
image = ref.Name
tag = ref.Tag
}
return append(nameColumns(&item, includeNamespace, includeKind), image, tag, status, msg)
} }
func (s imagePolicyListAdapter) headers(includeNamespace bool) []string { func (s imagePolicyListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Image", "Tag", "Ready", "Message"} headers := []string{"Name", "Latest image", "Ready", "Message"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }

View File

@@ -26,13 +26,13 @@ import (
"golang.org/x/text/language" "golang.org/x/text/language"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var getImageRepositoryCmd = &cobra.Command{ var getImageRepositoryCmd = &cobra.Command{
Use: "repository", Use: "repository",
Short: "Get ImageRepository status", Short: "Get ImageRepository status",
Long: "The get image repository command prints the status of ImageRepository objects.", Long: withPreviewNote("The get image repository command prints the status of ImageRepository objects."),
Example: ` # List all image repositories and their status Example: ` # List all image repositories and their status
flux get image repository flux get image repository

View File

@@ -26,13 +26,13 @@ import (
"golang.org/x/text/language" "golang.org/x/text/language"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
) )
var getImageUpdateCmd = &cobra.Command{ var getImageUpdateCmd = &cobra.Command{
Use: "update", Use: "update",
Short: "Get ImageUpdateAutomation status", Short: "Get ImageUpdateAutomation status",
Long: "The get image update command prints the status of ImageUpdateAutomation objects.", Long: withPreviewNote("The get image update command prints the status of ImageUpdateAutomation objects."),
Example: ` # List all image update automation object and their status Example: ` # List all image update automation object and their status
flux get image update flux get image update

View File

@@ -17,12 +17,10 @@ limitations under the License.
package main package main
import ( import (
"fmt"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
// These are general-purpose adapters for attaching methods to, for // These are general-purpose adapters for attaching methods to, for
@@ -79,34 +77,6 @@ func (a imagePolicyAdapter) asClientObject() client.Object {
return a.ImagePolicy return a.ImagePolicy
} }
func (a imagePolicyAdapter) deepCopyClientObject() client.Object {
return a.ImagePolicy.DeepCopy()
}
func (a imagePolicyAdapter) isStatic() bool {
return false
}
func (a imagePolicyAdapter) lastHandledReconcileRequest() string {
return a.Status.GetLastHandledReconcileRequest()
}
func (a imagePolicyAdapter) isSuspended() bool {
return a.Spec.Suspend
}
func (a imagePolicyAdapter) setSuspended() {
a.Spec.Suspend = true
}
func (a imagePolicyAdapter) successMessage() string {
return fmt.Sprintf("selected ref %s", a.Status.LatestRef.String())
}
func (a imagePolicyAdapter) setUnsuspended() {
a.Spec.Suspend = false
}
// imagev1.ImagePolicyList // imagev1.ImagePolicyList
type imagePolicyListAdapter struct { type imagePolicyListAdapter struct {
@@ -121,18 +91,6 @@ func (a imagePolicyListAdapter) len() int {
return len(a.ImagePolicyList.Items) return len(a.ImagePolicyList.Items)
} }
func (a imagePolicyListAdapter) resumeItem(i int) resumable {
return &imagePolicyAdapter{&a.ImagePolicyList.Items[i]}
}
func (obj imagePolicyAdapter) getObservedGeneration() int64 {
return obj.ImagePolicy.Status.ObservedGeneration
}
func (a imagePolicyListAdapter) item(i int) suspendable {
return &imagePolicyAdapter{&a.ImagePolicyList.Items[i]}
}
// autov1.ImageUpdateAutomation // autov1.ImageUpdateAutomation
var imageUpdateAutomationType = apiType{ var imageUpdateAutomationType = apiType{

View File

@@ -53,18 +53,6 @@ func TestImageScanning(t *testing.T) {
"get image policy podinfo-regex", "get image policy podinfo-regex",
"testdata/image/get_image_policy_regex.golden", "testdata/image/get_image_policy_regex.golden",
}, },
{
"suspend image policy podinfo-semver",
"testdata/image/suspend_image_policy.golden",
},
{
"resume image policy podinfo-semver",
"testdata/image/resume_image_policy.golden",
},
{
"reconcile image policy podinfo-semver",
"testdata/image/reconcile_image_policy.golden",
},
} }
for _, tc := range cases { for _, tc := range cases {

View File

@@ -83,7 +83,7 @@ type installFlags struct {
force bool force bool
} }
var installArgs = newInstallFlags() var installArgs = NewInstallFlags()
func init() { func init() {
installCmd.Flags().BoolVar(&installArgs.export, "export", false, installCmd.Flags().BoolVar(&installArgs.export, "export", false,
@@ -93,7 +93,7 @@ func init() {
installCmd.Flags().StringSliceVar(&installArgs.defaultComponents, "components", rootArgs.defaults.Components, installCmd.Flags().StringSliceVar(&installArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
installCmd.Flags().StringSliceVar(&installArgs.extraComponents, "components-extra", nil, installCmd.Flags().StringSliceVar(&installArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts values such as 'image-reflector-controller,image-automation-controller,source-watcher'") "list of components in addition to those supplied or defaulted, accepts values such as 'image-reflector-controller,image-automation-controller'")
installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory") installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory")
installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry, installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry,
"container registry where the toolkit images are published") "container registry where the toolkit images are published")
@@ -115,14 +115,9 @@ func init() {
rootCmd.AddCommand(installCmd) rootCmd.AddCommand(installCmd)
} }
func newInstallFlags() installFlags { func NewInstallFlags() installFlags {
return installFlags{ return installFlags{
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel), logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
defaultComponents: rootArgs.defaults.Components,
registry: rootArgs.defaults.Registry,
watchAllNamespaces: rootArgs.defaults.WatchAllNamespaces,
networkPolicy: rootArgs.defaults.NetworkPolicy,
clusterDomain: rootArgs.defaults.ClusterDomain,
} }
} }
@@ -200,13 +195,10 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
} }
if installArgs.export { if installArgs.export {
_, err = rootCmd.OutOrStdout().Write([]byte(manifest.Content)) fmt.Print(manifest.Content)
return err return nil
} else if rootArgs.verbose { } else if rootArgs.verbose {
_, err = rootCmd.OutOrStdout().Write([]byte(manifest.Content)) fmt.Print(manifest.Content)
if err != nil {
return err
}
} }
logger.Successf("manifests build completed") logger.Successf("manifests build completed")
@@ -246,7 +238,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }
rootCmd.Println(applyOutput) fmt.Fprintln(os.Stderr, applyOutput)
if opts.ImagePullSecret != "" && opts.RegistryCredential != "" { if opts.ImagePullSecret != "" && opts.RegistryCredential != "" {
logger.Actionf("generating image pull secret %s", opts.ImagePullSecret) logger.Actionf("generating image pull secret %s", opts.ImagePullSecret)
@@ -258,7 +250,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
Username: credentials[0], Username: credentials[0],
Password: credentials[1], Password: credentials[1],
} }
imagePullSecret, err := sourcesecret.GenerateOCI(secretOpts) imagePullSecret, err := sourcesecret.Generate(secretOpts)
if err != nil { if err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2025 The Flux authors Copyright 2022 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,17 +16,7 @@ limitations under the License.
package main package main
import ( import "testing"
"strings"
"testing"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
ssautil "github.com/fluxcd/pkg/ssa/utils"
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
)
func TestInstall(t *testing.T) { func TestInstall(t *testing.T) {
// The pointer to kubeconfigArgs.Namespace is shared across // The pointer to kubeconfigArgs.Namespace is shared across
@@ -69,43 +59,3 @@ func TestInstall(t *testing.T) {
}) })
} }
} }
func TestInstall_ComponentsExtra(t *testing.T) {
g := NewWithT(t)
command := "install --export --components-extra=" +
strings.Join(install.MakeDefaultOptions().ComponentsExtra, ",")
output, err := executeCommand(command)
g.Expect(err).NotTo(HaveOccurred())
manifests, err := ssautil.ReadObjects(strings.NewReader(output))
g.Expect(err).NotTo(HaveOccurred())
foundImageAutomation := false
foundImageReflector := false
foundSourceWatcher := false
foundExternalArtifact := false
for _, obj := range manifests {
if obj.GetKind() == "Deployment" && obj.GetName() == "image-automation-controller" {
foundImageAutomation = true
}
if obj.GetKind() == "Deployment" && obj.GetName() == "image-reflector-controller" {
foundImageReflector = true
}
if obj.GetKind() == "Deployment" && obj.GetName() == "source-watcher" {
foundSourceWatcher = true
}
if obj.GetKind() == "Deployment" &&
(obj.GetName() == "kustomize-controller" || obj.GetName() == "helm-controller") {
containers, _, _ := unstructured.NestedSlice(obj.Object, "spec", "template", "spec", "containers")
g.Expect(containers).ToNot(BeEmpty())
args, _, _ := unstructured.NestedSlice(containers[0].(map[string]any), "args")
g.Expect(args).To(ContainElement("--feature-gates=ExternalArtifact=true"))
foundExternalArtifact = true
}
}
g.Expect(foundImageAutomation).To(BeTrue(), "image-automation-controller deployment not found")
g.Expect(foundImageReflector).To(BeTrue(), "image-reflector-controller deployment not found")
g.Expect(foundSourceWatcher).To(BeTrue(), "source-watcher deployment not found")
g.Expect(foundExternalArtifact).To(BeTrue(), "ExternalArtifact feature gate not found")
}

View File

@@ -52,7 +52,7 @@ var listArtifactsCmd = &cobra.Command{
Long: `The list command fetches the tags and their metadata from a remote OCI repository. Long: `The list command fetches the tags and their metadata from a remote OCI repository.
The command can read the credentials from '~/.docker/config.json' but they can also be passed with --creds. It can also login to a supported provider with the --provider flag.`, The command can read the credentials from '~/.docker/config.json' but they can also be passed with --creds. It can also login to a supported provider with the --provider flag.`,
Example: ` # List the artifacts stored in an OCI repository Example: ` # List the artifacts stored in an OCI repository
flux list artifacts oci://ghcr.io/org/config/app flux list artifact oci://ghcr.io/org/config/app
`, `,
RunE: listArtifactsCmdRun, RunE: listArtifactsCmdRun,
} }
@@ -85,7 +85,7 @@ func listArtifactsCmdRun(cmd *cobra.Command, args []string) error {
if listArtifactArgs.provider.String() != sourcev1.GenericOCIProvider { if listArtifactArgs.provider.String() != sourcev1.GenericOCIProvider {
logger.Actionf("logging in to registry with provider credentials") logger.Actionf("logging in to registry with provider credentials")
ociOpt, _, err := loginWithProvider(ctx, url, listArtifactArgs.provider.String()) ociOpt, err := loginWithProvider(ctx, url, listArtifactArgs.provider.String())
if err != nil { if err != nil {
return fmt.Errorf("error during login with provider: %w", err) return fmt.Errorf("error during login with provider: %w", err)
} }

View File

@@ -247,8 +247,3 @@ While we try our best to not introduce breaking changes, they may occur when
we adapt to new features and/or find better ways to facilitate what it does.` we adapt to new features and/or find better ways to facilitate what it does.`
return fmt.Sprintf("%s\n\n%s", strings.TrimSpace(desc), previewNote) return fmt.Sprintf("%s\n\n%s", strings.TrimSpace(desc), previewNote)
} }
// printlnStdout prints the given text to stdout with a newline.
func printlnStdout(txt string) {
_, _ = rootCmd.OutOrStdout().Write([]byte(txt + "\n"))
}

View File

@@ -447,7 +447,6 @@ func resetCmdArgs() {
imagePolicyArgs = imagePolicyFlags{} imagePolicyArgs = imagePolicyFlags{}
imageRepoArgs = imageRepoFlags{} imageRepoArgs = imageRepoFlags{}
imageUpdateArgs = imageUpdateFlags{} imageUpdateArgs = imageUpdateFlags{}
installArgs = newInstallFlags()
kustomizationArgs = NewKustomizationFlags() kustomizationArgs = NewKustomizationFlags()
receiverArgs = receiverFlags{} receiverArgs = receiverFlags{}
resumeArgs = ResumeFlags{} resumeArgs = ResumeFlags{}

View File

@@ -1,691 +0,0 @@
/*
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.
*/
package main
import (
"context"
"encoding/json"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"github.com/fluxcd/pkg/ssa"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2"
imageautov1 "github.com/fluxcd/image-automation-controller/api/v1"
imageautov1b2 "github.com/fluxcd/image-automation-controller/api/v1beta2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1"
imagev1b2 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
swv1b1 "github.com/fluxcd/source-watcher/api/v2/v1beta1"
"github.com/fluxcd/flux2/v2/internal/utils"
)
// APIVersions holds the mapping of GroupKinds to their respective
// latest API versions for a specific Flux version.
type APIVersions struct {
FluxVersion string
LatestVersions map[schema.GroupKind]string
}
// TODO: Update this mapping when new Flux minor versions are released!
// latestAPIVersions contains the latest API versions for each GroupKind
// for each supported Flux version. We maintain the latest two minor versions.
var latestAPIVersions = []APIVersions{
{
FluxVersion: "2.7",
LatestVersions: map[schema.GroupKind]string{
// source-controller
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.BucketKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.GitRepositoryKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.OCIRepositoryKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.HelmRepositoryKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.HelmChartKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.ExternalArtifactKind}: sourcev1.GroupVersion.Version,
// kustomize-controller
{Group: kustomizev1.GroupVersion.Group, Kind: kustomizev1.KustomizationKind}: kustomizev1.GroupVersion.Version,
// helm-controller
{Group: helmv2.GroupVersion.Group, Kind: helmv2.HelmReleaseKind}: helmv2.GroupVersion.Version,
// notification-controller
{Group: notificationv1.GroupVersion.Group, Kind: notificationv1.ReceiverKind}: notificationv1.GroupVersion.Version,
{Group: notificationv1b3.GroupVersion.Group, Kind: notificationv1b3.AlertKind}: notificationv1b3.GroupVersion.Version,
{Group: notificationv1b3.GroupVersion.Group, Kind: notificationv1b3.ProviderKind}: notificationv1b3.GroupVersion.Version,
// image-reflector-controller
{Group: imagev1.GroupVersion.Group, Kind: imagev1.ImageRepositoryKind}: imagev1.GroupVersion.Version,
{Group: imagev1.GroupVersion.Group, Kind: imagev1.ImagePolicyKind}: imagev1.GroupVersion.Version,
// image-automation-controller
{Group: imageautov1.GroupVersion.Group, Kind: imageautov1.ImageUpdateAutomationKind}: imageautov1.GroupVersion.Version,
// source-watcher
{Group: swv1b1.GroupVersion.Group, Kind: swv1b1.ArtifactGeneratorKind}: swv1b1.GroupVersion.Version,
},
},
{
FluxVersion: "2.6",
LatestVersions: map[schema.GroupKind]string{
// source-controller
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.BucketKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.GitRepositoryKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.OCIRepositoryKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.HelmRepositoryKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.HelmChartKind}: sourcev1.GroupVersion.Version,
{Group: sourcev1.GroupVersion.Group, Kind: sourcev1.ExternalArtifactKind}: sourcev1.GroupVersion.Version,
// kustomize-controller
{Group: kustomizev1.GroupVersion.Group, Kind: kustomizev1.KustomizationKind}: kustomizev1.GroupVersion.Version,
// helm-controller
{Group: helmv2.GroupVersion.Group, Kind: helmv2.HelmReleaseKind}: helmv2.GroupVersion.Version,
// notification-controller
{Group: notificationv1.GroupVersion.Group, Kind: notificationv1.ReceiverKind}: notificationv1.GroupVersion.Version,
{Group: notificationv1b3.GroupVersion.Group, Kind: notificationv1b3.AlertKind}: notificationv1b3.GroupVersion.Version,
{Group: notificationv1b3.GroupVersion.Group, Kind: notificationv1b3.ProviderKind}: notificationv1b3.GroupVersion.Version,
// image-reflector-controller
{Group: imagev1b2.GroupVersion.Group, Kind: imagev1b2.ImageRepositoryKind}: imagev1b2.GroupVersion.Version,
{Group: imagev1b2.GroupVersion.Group, Kind: imagev1b2.ImagePolicyKind}: imagev1b2.GroupVersion.Version,
// image-automation-controller
{Group: imageautov1b2.GroupVersion.Group, Kind: imageautov1b2.ImageUpdateAutomationKind}: imageautov1b2.GroupVersion.Version,
},
},
}
var migrateCmd = &cobra.Command{
Use: "migrate",
Args: cobra.NoArgs,
Short: "Migrate the Flux custom resources to their latest API version",
Long: `The migrate command must be run before a Flux minor version upgrade.
The command has two modes of operation:
- Cluster mode (default): migrates all the Flux custom resources stored in Kubernetes etcd to their latest API version.
- File system mode (-f): migrates the Flux custom resources defined in the manifests located in the specified path.
`,
Example: ` # Migrate all the Flux custom resources in the cluster.
# This uses the current kubeconfig context and requires cluster-admin permissions.
flux migrate
# Migrate all the Flux custom resources in a Git repository
# checked out in the current working directory.
flux migrate -f .
# Migrate all Flux custom resources defined in YAML and Helm YAML template files.
flux migrate -f . --extensions=.yml,.yaml,.tpl
# Migrate the Flux custom resources to the latest API versions of Flux 2.6.
flux migrate -f . --version=2.6
# Migrate the Flux custom resources defined in a multi-document YAML manifest file.
flux migrate -f path/to/manifest.yaml
# Simulate the migration without making any changes.
flux migrate -f . --dry-run
# Run the migration skipping confirmation prompts.
flux migrate -f . --yes
`,
RunE: runMigrateCmd,
}
var migrateFlags struct {
yes bool
dryRun bool
path string
version string
extensions []string
}
func init() {
rootCmd.AddCommand(migrateCmd)
migrateCmd.Flags().StringVarP(&migrateFlags.path, "path", "f", "",
"the path to the directory containing the manifests to migrate")
migrateCmd.Flags().StringSliceVarP(&migrateFlags.extensions, "extensions", "e", []string{".yaml", ".yml"},
"the file extensions to consider when migrating manifests, only applicable with --path")
migrateCmd.Flags().StringVarP(&migrateFlags.version, "version", "v", "",
"the target Flux minor version to migrate manifests to, only applicable with --path (defaults to the version of the CLI)")
migrateCmd.Flags().BoolVarP(&migrateFlags.yes, "yes", "y", false,
"skip confirmation prompts when migrating manifests, only applicable with --path")
migrateCmd.Flags().BoolVar(&migrateFlags.dryRun, "dry-run", false,
"simulate the migration of manifests without making any changes, only applicable with --path")
}
func runMigrateCmd(*cobra.Command, []string) error {
if migrateFlags.path == "" {
return migrateCluster()
}
return migrateFileSystem()
}
func migrateCluster() error {
logger.Actionf("starting migration of custom resources")
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
cfg, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
if err != nil {
return fmt.Errorf("the Kubernetes client initialization failed: %w", err)
}
kubeClient, err := client.New(cfg, client.Options{Scheme: utils.NewScheme()})
if err != nil {
return err
}
migrator := NewClusterMigrator(kubeClient, client.MatchingLabels{
"app.kubernetes.io/part-of": "flux",
})
if err := migrator.Run(ctx); err != nil {
return err
}
logger.Successf("custom resources migrated successfully")
return nil
}
func migrateFileSystem() error {
pathRoot, err := os.OpenRoot(".")
if err != nil {
return fmt.Errorf("failed to open filesystem at the current working directory: %w", err)
}
defer pathRoot.Close()
fileSystem := &osFS{pathRoot.FS()}
yes := migrateFlags.yes
dryRun := migrateFlags.dryRun
path := migrateFlags.path
extensions := migrateFlags.extensions
var latestVersions map[schema.GroupKind]string
// Determine latest API versions based on the Flux version.
if migrateFlags.version == "" {
latestVersions = latestAPIVersions[0].LatestVersions
} else {
supportedVersions := make([]string, 0, len(latestAPIVersions))
for _, v := range latestAPIVersions {
if v.FluxVersion == migrateFlags.version {
latestVersions = v.LatestVersions
break
}
supportedVersions = append(supportedVersions, v.FluxVersion)
}
if latestVersions == nil {
return fmt.Errorf("version %s is not supported, supported versions are: %s",
migrateFlags.version, strings.Join(supportedVersions, ", "))
}
}
return NewFileSystemMigrator(fileSystem, yes, dryRun, path, extensions, latestVersions).Run()
}
// ClusterMigrator migrates all the CRs in the cluster for the CRDs matching the label selector.
type ClusterMigrator struct {
labelSelector client.MatchingLabels
kubeClient client.Client
}
// NewClusterMigrator creates a new ClusterMigrator instance with the specified label selector.
func NewClusterMigrator(kubeClient client.Client, labelSelector client.MatchingLabels) *ClusterMigrator {
return &ClusterMigrator{
labelSelector: labelSelector,
kubeClient: kubeClient,
}
}
func (c *ClusterMigrator) Run(ctx context.Context) error {
crdList := &apiextensionsv1.CustomResourceDefinitionList{}
if err := c.kubeClient.List(ctx, crdList, c.labelSelector); err != nil {
return fmt.Errorf("failed to list CRDs: %w", err)
}
for _, crd := range crdList.Items {
if err := c.migrateCRD(ctx, crd.Name); err != nil {
return err
}
}
return nil
}
func (c *ClusterMigrator) migrateCRD(ctx context.Context, name string) error {
crd := &apiextensionsv1.CustomResourceDefinition{}
if err := c.kubeClient.Get(ctx, client.ObjectKey{Name: name}, crd); err != nil {
return fmt.Errorf("failed to get CRD %s: %w", name, err)
}
// get the latest storage version for the CRD
storageVersion := c.getStorageVersion(crd)
if storageVersion == "" {
return fmt.Errorf("no storage version found for CRD %s", name)
}
// migrate all the resources for the CRD
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
return c.migrateCR(ctx, crd, storageVersion)
})
if err != nil {
return fmt.Errorf("failed to migrate resources for CRD %s: %w", name, err)
}
// set the CRD status to contain only the latest storage version
if len(crd.Status.StoredVersions) > 1 || crd.Status.StoredVersions[0] != storageVersion {
crd.Status.StoredVersions = []string{storageVersion}
if err := c.kubeClient.Status().Update(ctx, crd); err != nil {
return fmt.Errorf("failed to update CRD %s status: %w", crd.Name, err)
}
logger.Successf("%s migrated to storage version %s", crd.Name, storageVersion)
}
return nil
}
// migrateCR migrates all CRs for the given CRD to the specified version by patching them.
func (c *ClusterMigrator) migrateCR(ctx context.Context, crd *apiextensionsv1.CustomResourceDefinition, version string) error {
list := &unstructured.UnstructuredList{}
apiVersion := crd.Spec.Group + "/" + version
listKind := crd.Spec.Names.ListKind
list.SetAPIVersion(apiVersion)
list.SetKind(listKind)
err := c.kubeClient.List(ctx, list, client.InNamespace(""))
if err != nil {
return fmt.Errorf("failed to list resources for CRD %s: %w", crd.Name, err)
}
if len(list.Items) == 0 {
return nil
}
for _, item := range list.Items {
patches, err := ssa.PatchMigrateToVersion(&item, apiVersion)
if err != nil {
return fmt.Errorf("failed to create migration patch for %s/%s/%s: %w",
item.GetKind(), item.GetNamespace(), item.GetName(), err)
}
if len(patches) == 0 {
// patch the resource with an empty patch to update the version
if err := c.kubeClient.Patch(
ctx,
&item,
client.RawPatch(client.Merge.Type(), []byte("{}")),
); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf(" %s/%s/%s failed to migrate: %w",
item.GetKind(), item.GetNamespace(), item.GetName(), err)
}
} else {
// patch the resource to migrate the managed fields to the latest apiVersion
rawPatch, err := json.Marshal(patches)
if err != nil {
return fmt.Errorf("failed to marshal migration patch for %s/%s/%s: %w",
item.GetKind(), item.GetNamespace(), item.GetName(), err)
}
if err := c.kubeClient.Patch(
ctx,
&item,
client.RawPatch(types.JSONPatchType, rawPatch),
); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf(" %s/%s/%s failed to migrate managed fields: %w",
item.GetKind(), item.GetNamespace(), item.GetName(), err)
}
}
logger.Successf("%s/%s/%s migrated to version %s",
item.GetKind(), item.GetNamespace(), item.GetName(), version)
}
return nil
}
// getStorageVersion retrieves the storage version of a CustomResourceDefinition.
func (c *ClusterMigrator) getStorageVersion(crd *apiextensionsv1.CustomResourceDefinition) string {
var version string
for _, v := range crd.Spec.Versions {
if v.Storage {
version = v.Name
break
}
}
return version
}
// WritableFS extends fs.FS with a WriteFile method.
type WritableFS interface {
fs.FS
WriteFile(name string, data []byte, perm os.FileMode) error
}
// osFS is a WritableFS implementation that uses the file system of the OS.
type osFS struct {
fs.FS
}
func (o *osFS) WriteFile(name string, data []byte, perm os.FileMode) error {
return os.WriteFile(name, data, perm)
}
// FileSystemMigrator migrates all the CRs found in the manifests located in the specified path.
type FileSystemMigrator struct {
fileSystem WritableFS
yes bool
dryRun bool
path string
extensions []string
latestVersions map[schema.GroupKind]string
}
// FileAPIUpgrades represents the API upgrades detected in a specific manifest file.
type FileAPIUpgrades struct {
File string
Upgrades []APIUpgrade
}
// APIUpgrade represents an upgrade of a specific API version in a manifest file.
type APIUpgrade struct {
Line int
Kind string
OldVersion string
NewVersion string
}
// NewFileSystemMigrator creates a new FileSystemMigrator instance with the specified flags.
func NewFileSystemMigrator(fileSystem WritableFS, yes, dryRun bool, path string,
extensions []string, latestVersions map[schema.GroupKind]string) *FileSystemMigrator {
return &FileSystemMigrator{
fileSystem: fileSystem,
yes: yes,
dryRun: dryRun,
path: filepath.Clean(path), // convert dir/ to dir to avoid error when walking
extensions: extensions,
latestVersions: latestVersions,
}
}
func (f *FileSystemMigrator) Run() error {
logger.Actionf("starting migration of custom resources")
// List and filter files.
files, err := f.listFiles()
if err != nil {
return err
}
// Detect upgrades.
upgrades, err := f.detectUpgrades(files)
if err != nil {
return err
}
if len(upgrades) == 0 {
logger.Successf("no custom resources found that require migration")
return nil
}
if f.dryRun {
logger.Successf("dry-run mode enabled, no changes will be made")
return nil
}
// Confirm upgrades.
if !f.yes {
prompt := promptui.Prompt{
Label: "Are you sure you want to proceed with the above upgrades", // Already prints "? [y/N]"
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return err
}
}
// Migrate files.
for _, fileUpgrades := range upgrades {
if err := f.migrateFile(&fileUpgrades); err != nil {
return err
}
logger.Successf("file %s migrated successfully", fileUpgrades.File)
}
logger.Successf("custom resources migrated successfully")
return nil
}
func (f *FileSystemMigrator) listFiles() ([]string, error) {
fileInfo, err := fs.Stat(f.fileSystem, f.path)
if err != nil {
return nil, fmt.Errorf("failed to stat path %s: %w", f.path, err)
}
if fileInfo.IsDir() {
return f.listDirectoryFiles()
}
if err := f.validateSingleFile(); err != nil {
return nil, err
}
return []string{f.path}, nil
}
func (f *FileSystemMigrator) listDirectoryFiles() ([]string, error) {
var files []string
err := fs.WalkDir(f.fileSystem, f.path, func(path string, dirEntry fs.DirEntry, err error) error {
if err != nil {
return err
}
if !f.matchesExtensions(path) {
return nil
}
fileInfo, err := dirEntry.Info()
if err != nil {
return err
}
if fileInfo.Mode().IsRegular() {
files = append(files, path)
} else if !fileInfo.IsDir() {
logger.Warningf("skipping irregular file %s", path)
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to walk directory %s: %w", f.path, err)
}
return files, nil
}
func (f *FileSystemMigrator) validateSingleFile() error {
if !f.matchesExtensions(f.path) {
return fmt.Errorf("file %s does not match the specified extensions: %v",
f.path, strings.Join(f.extensions, ", "))
}
// Check if it's irregular by walking the parent directory.
var irregular bool
err := fs.WalkDir(f.fileSystem, filepath.Dir(f.path), func(path string, dirEntry fs.DirEntry, err error) error {
if err != nil {
return err
}
if path != f.path {
return nil
}
fileInfo, err := dirEntry.Info()
if err != nil {
return err
}
if !fileInfo.Mode().IsRegular() {
irregular = true
}
return nil
})
if err != nil {
return fmt.Errorf("failed to validate file %s: %w", f.path, err)
}
if irregular {
return fmt.Errorf("file %s is irregular", f.path)
}
return nil
}
func (f *FileSystemMigrator) matchesExtensions(file string) bool {
for _, ext := range f.extensions {
if strings.HasSuffix(file, ext) {
return true
}
}
return false
}
func (f *FileSystemMigrator) detectUpgrades(files []string) ([]FileAPIUpgrades, error) {
var upgrades []FileAPIUpgrades
for _, file := range files {
fileUpgrades, err := f.detectFileUpgrades(file)
if err != nil {
return nil, err
}
if len(fileUpgrades) == 0 {
continue
}
fu := FileAPIUpgrades{
File: file,
Upgrades: fileUpgrades,
}
upgrades = append(upgrades, fu)
f.printDetectedUpgrades(&fu)
}
return upgrades, nil
}
func (f *FileSystemMigrator) detectFileUpgrades(file string) ([]APIUpgrade, error) {
b, err := fs.ReadFile(f.fileSystem, file)
if err != nil {
return nil, fmt.Errorf("failed to read file %s: %w", file, err)
}
lines := strings.Split(string(b), "\n")
var fileUpgrades []APIUpgrade
for line, apiVersionLine := range lines {
// Parse apiVersion.
const apiVersionPrefix = "apiVersion: "
idx := strings.Index(apiVersionLine, apiVersionPrefix)
if idx == -1 {
continue
}
apiVersionValuePrefix := strings.TrimSpace(apiVersionLine[idx+len(apiVersionPrefix):])
apiVersion := strings.Split(apiVersionValuePrefix, " ")[0]
gv, err := schema.ParseGroupVersion(apiVersion)
if err != nil {
logger.Warningf("%s:%d: %v", file, line+1, err)
continue
}
// Parse kind.
if line+1 >= len(lines) {
continue
}
kindLine := lines[line+1]
const kindPrefix = "kind: "
idx = strings.Index(kindLine, kindPrefix)
if idx == -1 {
continue
}
kindValuePrefix := strings.TrimSpace(kindLine[idx+len(kindPrefix):])
kind := strings.Split(kindValuePrefix, " ")[0]
// Build GroupKind.
gk := schema.GroupKind{
Group: gv.Group,
Kind: kind,
}
// Check if there's a newer version for the GroupKind.
latestVersion, ok := f.latestVersions[gk]
if !ok || latestVersion == gv.Version {
continue
}
// Record the upgrade.
fileUpgrades = append(fileUpgrades, APIUpgrade{
Line: line,
Kind: kind,
OldVersion: gv.Version,
NewVersion: latestVersion,
})
}
return fileUpgrades, nil
}
func (f *FileSystemMigrator) printDetectedUpgrades(fileUpgrades *FileAPIUpgrades) {
for _, upgrade := range fileUpgrades.Upgrades {
logger.Generatef("%s:%d: %s %s -> %s",
fileUpgrades.File,
upgrade.Line+1,
upgrade.Kind,
upgrade.OldVersion,
upgrade.NewVersion)
}
}
func (f *FileSystemMigrator) migrateFile(fileUpgrades *FileAPIUpgrades) error {
// Read file and map lines.
b, err := fs.ReadFile(f.fileSystem, fileUpgrades.File)
if err != nil {
return fmt.Errorf("failed to read file %s: %w", fileUpgrades.File, err)
}
lines := strings.Split(string(b), "\n")
// Apply upgrades to lines.
for _, upgrade := range fileUpgrades.Upgrades {
line := lines[upgrade.Line]
line = strings.Replace(line, upgrade.OldVersion, upgrade.NewVersion, 1)
lines[upgrade.Line] = line
}
// Read file info to preserve permissions.
fileInfo, err := fs.Stat(f.fileSystem, fileUpgrades.File)
if err != nil {
return fmt.Errorf("failed to stat file %s: %w", fileUpgrades.File, err)
}
// Write file with preserved permissions.
b = []byte(strings.Join(lines, "\n"))
if err := f.fileSystem.WriteFile(fileUpgrades.File, b, fileInfo.Mode()); err != nil {
return fmt.Errorf("failed to write file %s: %w", fileUpgrades.File, err)
}
return nil
}

View File

@@ -1,161 +0,0 @@
/*
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.
*/
package main
import (
"bytes"
"io/fs"
"os"
"testing"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type writeToMemoryFS struct {
fs.FS
writtenFiles map[string][]byte
}
func (m *writeToMemoryFS) WriteFile(name string, data []byte, perm os.FileMode) error {
m.writtenFiles[name] = data
return nil
}
type writtenFile struct {
file string
goldenFile string
}
func TestFileSystemMigrator(t *testing.T) {
for _, tt := range []struct {
name string
path string
outputGolden string
writtenFiles []writtenFile
err string
}{
{
name: "errors out for single file that is a symlink",
path: "testdata/migrate/file-system/single-file-link.yaml",
err: "file testdata/migrate/file-system/single-file-link.yaml is irregular",
},
{
name: "errors out for single file with wrong extension",
path: "testdata/migrate/file-system/single-file-wrong-ext.json",
err: "file testdata/migrate/file-system/single-file-wrong-ext.json does not match the specified extensions: .yaml, .yml",
},
{
name: "migrate single file",
path: "testdata/migrate/file-system/single-file.yaml",
outputGolden: "testdata/migrate/file-system/single-file.yaml.output.golden",
writtenFiles: []writtenFile{
{
file: "testdata/migrate/file-system/single-file.yaml",
goldenFile: "testdata/migrate/file-system/single-file.yaml.golden",
},
},
},
{
name: "migrate files in directory",
path: "testdata/migrate/file-system/dir",
outputGolden: "testdata/migrate/file-system/dir.output.golden",
writtenFiles: []writtenFile{
{
file: "testdata/migrate/file-system/dir/some-dir/another-file.yaml",
goldenFile: "testdata/migrate/file-system/dir.golden/some-dir/another-file.yaml",
},
{
file: "testdata/migrate/file-system/dir/some-dir/another-file.yml",
goldenFile: "testdata/migrate/file-system/dir.golden/some-dir/another-file.yml",
},
{
file: "testdata/migrate/file-system/dir/some-file.yaml",
goldenFile: "testdata/migrate/file-system/dir.golden/some-file.yaml",
},
{
file: "testdata/migrate/file-system/dir/some-file.yml",
goldenFile: "testdata/migrate/file-system/dir.golden/some-file.yml",
},
},
},
} {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
// Store logger, replace with test logger, and restore at the end of the test.
var testLogger bytes.Buffer
oldLogger := logger
logger = stderrLogger{&testLogger}
t.Cleanup(func() { logger = oldLogger })
// Open current working directory as root and build write-to-memory filesystem.
pathRoot, err := os.OpenRoot(".")
g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(func() { pathRoot.Close() })
fileSystem := &writeToMemoryFS{
FS: pathRoot.FS(),
writtenFiles: make(map[string][]byte),
}
// Prepare other inputs.
const yes = true
const dryRun = false
extensions := []string{".yaml", ".yml"}
latestVersions := map[schema.GroupKind]string{
{Group: "image.toolkit.fluxcd.io", Kind: "ImageRepository"}: "v1",
{Group: "image.toolkit.fluxcd.io", Kind: "ImagePolicy"}: "v1",
{Group: "image.toolkit.fluxcd.io", Kind: "ImageUpdateAutomation"}: "v1",
}
// Run migration.
err = NewFileSystemMigrator(fileSystem, yes, dryRun, tt.path, extensions, latestVersions).Run()
if tt.err != "" {
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(Equal(tt.err))
return
}
g.Expect(err).ToNot(HaveOccurred())
// Assert logger output.
b, err := os.ReadFile(tt.outputGolden)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(string(b)).To(Equal(testLogger.String()),
"logger output does not match golden file %s", tt.outputGolden)
// Assert which files were written.
writtenFiles := make([]string, 0, len(fileSystem.writtenFiles))
for name := range fileSystem.writtenFiles {
writtenFiles = append(writtenFiles, name)
}
expectedWrittenFiles := make([]string, 0, len(tt.writtenFiles))
for _, wf := range tt.writtenFiles {
expectedWrittenFiles = append(expectedWrittenFiles, wf.file)
}
g.Expect(writtenFiles).To(ConsistOf(expectedWrittenFiles))
// Assert contents of written files.
for _, wf := range tt.writtenFiles {
b, err := os.ReadFile(wf.goldenFile)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(string(fileSystem.writtenFiles[wf.file])).To(Equal(string(b)),
"file %s does not match golden file %s", wf.file, wf.goldenFile)
}
})
}
}

View File

@@ -18,9 +18,9 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/crane"
"github.com/fluxcd/pkg/auth" "github.com/fluxcd/pkg/auth"
@@ -29,14 +29,17 @@ import (
) )
// loginWithProvider gets a crane authentication option for the given provider and URL. // loginWithProvider gets a crane authentication option for the given provider and URL.
func loginWithProvider(ctx context.Context, url, provider string) (crane.Option, authn.Authenticator, error) { func loginWithProvider(ctx context.Context, url, provider string) (crane.Option, error) {
var opts []auth.Option var opts []auth.Option
if provider == azure.ProviderName { if provider == azure.ProviderName {
opts = append(opts, auth.WithAllowShellOut()) opts = append(opts, auth.WithAllowShellOut())
} }
authenticator, err := authutils.GetArtifactRegistryCredentials(ctx, provider, url, opts...) authenticator, err := authutils.GetArtifactRegistryCredentials(ctx, provider, url, opts...)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("could not login to provider %s with url %s: %w", provider, url, err) return nil, fmt.Errorf("could not login to provider %s with url %s: %w", provider, url, err)
} }
return crane.WithAuth(authenticator), authenticator, nil if authenticator == nil {
return nil, errors.New("unsupported provider")
}
return crane.WithAuth(authenticator), nil
} }

View File

@@ -94,7 +94,7 @@ func pullArtifactCmdRun(cmd *cobra.Command, args []string) error {
if pullArtifactArgs.provider.String() != sourcev1.GenericOCIProvider { if pullArtifactArgs.provider.String() != sourcev1.GenericOCIProvider {
logger.Actionf("logging in to registry with provider credentials") logger.Actionf("logging in to registry with provider credentials")
opt, _, err := loginWithProvider(ctx, url, pullArtifactArgs.provider.String()) opt, err := loginWithProvider(ctx, url, pullArtifactArgs.provider.String())
if err != nil { if err != nil {
return fmt.Errorf("error during login with provider: %w", err) return fmt.Errorf("error during login with provider: %w", err)
} }

View File

@@ -19,6 +19,7 @@ package main
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
@@ -33,6 +34,9 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/pkg/auth"
"github.com/fluxcd/pkg/auth/azure"
authutils "github.com/fluxcd/pkg/auth/utils"
"github.com/fluxcd/pkg/oci" "github.com/fluxcd/pkg/oci"
sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
@@ -225,12 +229,18 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
if provider := pushArtifactArgs.provider.String(); provider != sourcev1.GenericOCIProvider { if provider := pushArtifactArgs.provider.String(); provider != sourcev1.GenericOCIProvider {
logger.Actionf("logging in to registry with provider credentials") logger.Actionf("logging in to registry with provider credentials")
var opt crane.Option var authOpts []auth.Option
opt, authenticator, err = loginWithProvider(ctx, url, provider) if provider == azure.ProviderName {
authOpts = append(authOpts, auth.WithAllowShellOut())
}
authenticator, err = authutils.GetArtifactRegistryCredentials(ctx, provider, url, authOpts...)
if err != nil { if err != nil {
return fmt.Errorf("error during login with provider: %w", err) return fmt.Errorf("error during login with provider: %w", err)
} }
opts = append(opts, opt) if authenticator == nil {
return errors.New("unsupported provider")
}
opts = append(opts, crane.WithAuth(authenticator))
} }
if rootArgs.timeout != 0 { if rootArgs.timeout != 0 {
@@ -251,13 +261,7 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
} }
transportOpts, err := oci.WithRetryTransport(ctx, transportOpts, err := oci.WithRetryTransport(ctx, ref, authenticator, backoff, []string{ref.Context().Scope(transport.PushScope)})
ref,
authenticator,
backoff,
[]string{ref.Context().Scope(transport.PushScope)},
pushArtifactArgs.insecure,
)
if err != nil { if err != nil {
return fmt.Errorf("error setting up transport: %w", err) return fmt.Errorf("error setting up transport: %w", err)
} }

View File

@@ -1,40 +0,0 @@
/*
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.
*/
package main
import (
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1"
"github.com/spf13/cobra"
)
var reconcileImagePolicyCmd = &cobra.Command{
Use: "policy [name]",
Short: "Reconcile an ImagePolicy",
Long: `The reconcile image policy command triggers a reconciliation of an ImagePolicy resource and waits for it to finish.`,
Example: `
# Trigger a reconciliation for an existing image policy called 'alpine'
flux reconcile image policy alpine`,
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
RunE: reconcileCommand{
apiType: imagePolicyType,
object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
}.run,
}
func init() {
reconcileImageCmd.AddCommand(reconcileImagePolicyCmd)
}

View File

@@ -21,7 +21,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var reconcileImageRepositoryCmd = &cobra.Command{ var reconcileImageRepositoryCmd = &cobra.Command{

View File

@@ -22,7 +22,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta" apimeta "k8s.io/apimachinery/pkg/api/meta"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
meta "github.com/fluxcd/pkg/apis/meta" meta "github.com/fluxcd/pkg/apis/meta"
) )

View File

@@ -1,40 +0,0 @@
/*
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.
*/
package main
import (
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1"
"github.com/spf13/cobra"
)
var resumeImagePolicyCmd = &cobra.Command{
Use: "policy [name]",
Short: "Resume an ImagePolicy",
Long: `The resume image policy command resumes a suspended ImagePolicy resource.`,
Example: `
# Resume a suspended image policy called 'alpine'
flux resume image policy alpine`,
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
RunE: resumeCommand{
apiType: imagePolicyType,
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}.run,
}
func init() {
resumeImageCmd.AddCommand(resumeImagePolicyCmd)
}

View File

@@ -19,7 +19,7 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var resumeImageRepositoryCmd = &cobra.Command{ var resumeImageRepositoryCmd = &cobra.Command{

View File

@@ -19,7 +19,7 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
) )
var resumeImageUpdateCmd = &cobra.Command{ var resumeImageUpdateCmd = &cobra.Command{

View File

@@ -28,8 +28,8 @@ import (
"github.com/fluxcd/cli-utils/pkg/kstatus/status" "github.com/fluxcd/cli-utils/pkg/kstatus/status"
helmv2 "github.com/fluxcd/helm-controller/api/v2" helmv2 "github.com/fluxcd/helm-controller/api/v2"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1" notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3" notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"

View File

@@ -1,37 +0,0 @@
/*
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.
*/
package main
import (
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1"
"github.com/spf13/cobra"
)
var suspendImagePolicyCmd = &cobra.Command{
Use: "policy [name]",
Short: "Suspend an ImagePolicy",
Long: `The suspend image policy command suspends the reconciliation of an ImagePolicy resource.`,
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
RunE: suspendCommand{
apiType: imagePolicyType,
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}.run,
}
func init() {
suspendImageCmd.AddCommand(suspendImagePolicyCmd)
}

View File

@@ -19,7 +19,7 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
) )
var suspendImageRepositoryCmd = &cobra.Command{ var suspendImageRepositoryCmd = &cobra.Command{

View File

@@ -19,7 +19,7 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
autov1 "github.com/fluxcd/image-automation-controller/api/v1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta2"
) )
var suspendImageUpdateCmd = &cobra.Command{ var suspendImageUpdateCmd = &cobra.Command{

View File

@@ -82,7 +82,7 @@ func tagArtifactCmdRun(cmd *cobra.Command, args []string) error {
if tagArtifactArgs.provider.String() != sourcev1.GenericOCIProvider { if tagArtifactArgs.provider.String() != sourcev1.GenericOCIProvider {
logger.Actionf("logging in to registry with provider credentials") logger.Actionf("logging in to registry with provider credentials")
opt, _, err := loginWithProvider(ctx, url, tagArtifactArgs.provider.String()) opt, err := loginWithProvider(ctx, url, tagArtifactArgs.provider.String())
if err != nil { if err != nil {
return fmt.Errorf("error during login with provider: %w", err) return fmt.Errorf("error during login with provider: %w", err)
} }

View File

@@ -1,3 +1,3 @@
► checking prerequisites ► checking prerequisites
✔ Kubernetes {{ .serverVersion }} >=1.32.0-0 ✔ Kubernetes {{ .serverVersion }} >=1.31.0-0
✔ prerequisites checks passed ✔ prerequisites checks passed

View File

@@ -36,5 +36,4 @@ stringData:
lbD102oXw9lUefVI0McyQIN9J58ewDC79AG7gU/fTSt6F75OeFLOJmoedQo33Y+s lbD102oXw9lUefVI0McyQIN9J58ewDC79AG7gU/fTSt6F75OeFLOJmoedQo33Y+s
bUytJtOhHbLRNxwgalhjBUNWICrDktqJmumNOEOOPBqVz7RGwUg= bUytJtOhHbLRNxwgalhjBUNWICrDktqJmumNOEOOPBqVz7RGwUg=
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
type: Opaque

View File

@@ -35,5 +35,4 @@ stringData:
lbD102oXw9lUefVI0McyQIN9J58ewDC79AG7gU/fTSt6F75OeFLOJmoedQo33Y+s lbD102oXw9lUefVI0McyQIN9J58ewDC79AG7gU/fTSt6F75OeFLOJmoedQo33Y+s
bUytJtOhHbLRNxwgalhjBUNWICrDktqJmumNOEOOPBqVz7RGwUg= bUytJtOhHbLRNxwgalhjBUNWICrDktqJmumNOEOOPBqVz7RGwUg=
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
type: Opaque

View File

@@ -7,5 +7,4 @@ metadata:
stringData: stringData:
password: my-password password: my-password
username: my-username username: my-username
type: kubernetes.io/basic-auth

View File

@@ -5,15 +5,6 @@ metadata:
name: ghcr name: ghcr
namespace: my-namespace namespace: my-namespace
stringData: stringData:
.dockerconfigjson: |- .dockerconfigjson: '{"auths":{"ghcr.io":{"username":"stefanprodan","password":"password","auth":"c3RlZmFucHJvZGFuOnBhc3N3b3Jk"}}}'
{
"auths": {
"ghcr.io": {
"username": "stefanprodan",
"password": "password",
"auth": "c3RlZmFucHJvZGFuOnBhc3N3b3Jk"
}
}
}
type: kubernetes.io/dockerconfigjson type: kubernetes.io/dockerconfigjson

View File

@@ -8,5 +8,4 @@ stringData:
address: https://my-proxy.com address: https://my-proxy.com
password: my-password password: my-password
username: my-username username: my-username
type: Opaque

View File

@@ -11,7 +11,4 @@ spec:
interval: 1m0s interval: 1m0s
ref: ref:
branch: master branch: master
sparseCheckout:
- .cosign
- non-existent-dir/
url: https://github.com/stefanprodan/podinfo url: https://github.com/stefanprodan/podinfo

View File

@@ -1,34 +0,0 @@
---
apiVersion: v1
kind: Namespace
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: apps
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: dev-team
namespace: apps
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: dev-team-reconciler
namespace: apps
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: gotk:apps:reconciler
- kind: ServiceAccount
name: dev-team
namespace: apps

View File

@@ -1,34 +0,0 @@
---
apiVersion: v1
kind: Namespace
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: apps
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: dev-team
namespace: apps
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: dev-team-reconciler
namespace: apps
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: custom-role
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: gotk:apps:reconciler
- kind: ServiceAccount
name: dev-team
namespace: apps

View File

@@ -1,34 +0,0 @@
---
apiVersion: v1
kind: Namespace
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: apps
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: flux-tenant
namespace: apps
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
toolkit.fluxcd.io/tenant: dev-team
name: dev-team-reconciler
namespace: apps
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: gotk:apps:reconciler
- kind: ServiceAccount
name: flux-tenant
namespace: apps

View File

@@ -1,2 +0,0 @@
# History documentation: https://fluxcd.io/flux/components/helm/helmreleases/#history
[]

View File

@@ -1,25 +0,0 @@
# History documentation: https://fluxcd.io/flux/components/helm/helmreleases/#history
- appVersion: 6.0.0
chartName: podinfo
chartVersion: 6.0.0
configDigest: sha256:abc123
deleted: "2024-01-01T10:00:00Z"
digest: sha256:def456
firstDeployed: "2024-01-01T09:00:00Z"
lastDeployed: "2024-01-01T10:00:00Z"
name: test-with-history
namespace: {{ .fluxns }}
status: superseded
version: 1
- appVersion: 6.1.0
chartName: podinfo
chartVersion: 6.1.0
configDigest: sha256:xyz789
deleted: null
digest: sha256:ghi012
firstDeployed: "2024-01-01T11:00:00Z"
lastDeployed: "2024-01-01T11:00:00Z"
name: test-with-history
namespace: {{ .fluxns }}
status: deployed
version: 2

View File

@@ -46,47 +46,6 @@ spec:
name: none name: none
optional: true optional: true
--- ---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: test-with-history
namespace: {{ .fluxns }}
spec:
chartRef:
kind: OCIRepository
name: podinfo
interval: 5m0s
values:
image:
repository: stefanprodan/podinfo
tag: 5.0.0
status:
observedGeneration: 1
history:
- name: test-with-history
namespace: {{ .fluxns }}
version: 1
configDigest: sha256:abc123
chartName: podinfo
chartVersion: 6.0.0
appVersion: 6.0.0
deleted: "2024-01-01T10:00:00Z"
digest: sha256:def456
firstDeployed: "2024-01-01T09:00:00Z"
lastDeployed: "2024-01-01T10:00:00Z"
status: superseded
- name: test-with-history
namespace: {{ .fluxns }}
version: 2
configDigest: sha256:xyz789
chartName: podinfo
chartVersion: 6.1.0
appVersion: 6.1.0
digest: sha256:ghi012
firstDeployed: "2024-01-01T11:00:00Z"
lastDeployed: "2024-01-01T11:00:00Z"
status: deployed
---
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:

View File

@@ -1,2 +0,0 @@
# History documentation: https://fluxcd.io/flux/components/kustomize/kustomizations/#history
[]

View File

@@ -1,17 +0,0 @@
# History documentation: https://fluxcd.io/flux/components/kustomize/kustomizations/#history
- digest: sha256:def456
firstReconciled: "2024-01-01T09:00:00Z"
lastReconciled: "2024-01-01T10:00:00Z"
lastReconciledDuration: 300ms
lastReconciledStatus: success
metadata:
originRevision: abc123
totalReconciliations: 1
- digest: sha256:ghi012
firstReconciled: "2024-02-01T09:00:00Z"
lastReconciled: "2024-02-01T10:00:00Z"
lastReconciledDuration: 500ms
lastReconciledStatus: failure
metadata:
originRevision: xyz789
totalReconciliations: 10

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