Compare commits
206 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff2833c4d1 | ||
|
|
45ba845f23 | ||
|
|
771a14fcf6 | ||
|
|
c8ff861d00 | ||
|
|
0f05ce3605 | ||
|
|
38a3f3ba11 | ||
|
|
659a19cd80 | ||
|
|
baaa466c0f | ||
|
|
168c65bb6e | ||
|
|
6003d11156 | ||
|
|
1f16b6d639 | ||
|
|
54bb4b2efd | ||
|
|
f54770c21a | ||
|
|
1244a62deb | ||
|
|
2fe55bcdde | ||
|
|
9943690855 | ||
|
|
89c46a6379 | ||
|
|
a0d4530cc0 | ||
|
|
6db84269af | ||
|
|
2cd3c32ca7 | ||
|
|
9740fecc35 | ||
|
|
433492791b | ||
|
|
7d3c63ad74 | ||
|
|
a6538b117e | ||
|
|
d54e7559a5 | ||
|
|
bb9eca7232 | ||
|
|
b5027d8f3f | ||
|
|
00a134e23f | ||
|
|
aab3452773 | ||
|
|
5903dfc627 | ||
|
|
a5272b2b39 | ||
|
|
771fc20bb2 | ||
|
|
b5f48bee78 | ||
|
|
ff78af5808 | ||
|
|
7eab649abc | ||
|
|
1b5db157b1 | ||
|
|
0fa9aebf7a | ||
|
|
9fb9965ffe | ||
|
|
02c9f29499 | ||
|
|
c6243ad002 | ||
|
|
f4650c7a84 | ||
|
|
6df691598e | ||
|
|
26964b7f88 | ||
|
|
71712ae1c3 | ||
|
|
0b6c979e47 | ||
|
|
cc2cbc2519 | ||
|
|
8043ae961a | ||
|
|
b1d1d30cdb | ||
|
|
28d606cb0e | ||
|
|
e9c5b6e917 | ||
|
|
68f96235fc | ||
|
|
12f22ddeba | ||
|
|
9db5c644a6 | ||
|
|
ef6a1fb09b | ||
|
|
99002f92f4 | ||
|
|
298d6a1a15 | ||
|
|
5a21f50230 | ||
|
|
5263dabd22 | ||
|
|
9b649f6c72 | ||
|
|
b903cd5b68 | ||
|
|
890b5c5202 | ||
|
|
a14e88d04d | ||
|
|
58e09b4c68 | ||
|
|
631201d541 | ||
|
|
0fbeb6d2cd | ||
|
|
11f8e2ffde | ||
|
|
055eb4a61a | ||
|
|
30c1c5c3d3 | ||
|
|
e034ec3207 | ||
|
|
8edc4bd24b | ||
|
|
6e1672f73c | ||
|
|
5e1f6f7317 | ||
|
|
386780ba12 | ||
|
|
e785971ba8 | ||
|
|
daaae07649 | ||
|
|
6cd567dc66 | ||
|
|
a541a7ee85 | ||
|
|
43572bba04 | ||
|
|
2a3a4456c1 | ||
|
|
15f8e6369b | ||
|
|
cfad9a19eb | ||
|
|
e4c3136433 | ||
|
|
73b8a26850 | ||
|
|
aa533b28fb | ||
|
|
9d70e09a57 | ||
|
|
17e18985e6 | ||
|
|
7c39aaf463 | ||
|
|
bae5c125e8 | ||
|
|
1c84fa0d97 | ||
|
|
6f583f9f0e | ||
|
|
217574b75c | ||
|
|
1378530aeb | ||
|
|
0b10ed4d88 | ||
|
|
a2887f5776 | ||
|
|
0f1d27f1e6 | ||
|
|
850ab0942b | ||
|
|
f5ae8f44b4 | ||
|
|
7f98cfd506 | ||
|
|
bc45a79b92 | ||
|
|
5003cf674d | ||
|
|
bc9cbc387c | ||
|
|
60a1e78869 | ||
|
|
37f5587085 | ||
|
|
fa6e3d3706 | ||
|
|
bb8bc875b4 | ||
|
|
b3dca737be | ||
|
|
9094f85487 | ||
|
|
1256bbfbaf | ||
|
|
24fe74f2f6 | ||
|
|
908f501e03 | ||
|
|
35507c7854 | ||
|
|
eb7102ecac | ||
|
|
ade6bfcbca | ||
|
|
fa98403aa8 | ||
|
|
3f0cb1637c | ||
|
|
42011d028e | ||
|
|
307bb0dea1 | ||
|
|
ec2a8347d4 | ||
|
|
e99b1c3ed8 | ||
|
|
99825f2663 | ||
|
|
afffdfbc5c | ||
|
|
cd874acfd5 | ||
|
|
34edbf469e | ||
|
|
d9ed30e436 | ||
|
|
30008de400 | ||
|
|
a5fa731545 | ||
|
|
493ee3c956 | ||
|
|
3dd574ee51 | ||
|
|
5416c19b2e | ||
|
|
2f31d80c7a | ||
|
|
27d1833854 | ||
|
|
84ed716908 | ||
|
|
6c9c9c7578 | ||
|
|
cc7b7b0689 | ||
|
|
5df8e05d1a | ||
|
|
b3b224b0ca | ||
|
|
75ab28ee5d | ||
|
|
aa9ea2b4ab | ||
|
|
1e6be99c36 | ||
|
|
49fb396bf8 | ||
|
|
e055c9ddc1 | ||
|
|
c708e390a7 | ||
|
|
d5ad26c934 | ||
|
|
144b7cd922 | ||
|
|
9e86fbb311 | ||
|
|
b528428d02 | ||
|
|
b3d7730e79 | ||
|
|
f2ba567ca4 | ||
|
|
8342f77087 | ||
|
|
7cade1b98f | ||
|
|
ee4c1fb36c | ||
|
|
dbc4e537fe | ||
|
|
e28990b96c | ||
|
|
408cf92c04 | ||
|
|
425af2e0dc | ||
|
|
22df860eca | ||
|
|
f395044d65 | ||
|
|
afe0ddcd84 | ||
|
|
2c0323684c | ||
|
|
6d5ffdea57 | ||
|
|
648af6e645 | ||
|
|
e1895a4e21 | ||
|
|
d5f45800ae | ||
|
|
51f9d249ff | ||
|
|
6f525356cb | ||
|
|
5008f9064e | ||
|
|
cff96ed7ca | ||
|
|
4e8a600f34 | ||
|
|
4fd5684277 | ||
|
|
06bf469ba7 | ||
|
|
b8a215230c | ||
|
|
2460cfcf1c | ||
|
|
364242c857 | ||
|
|
29e2900f59 | ||
|
|
61e1fb770e | ||
|
|
2d3fcbdea3 | ||
|
|
47e15cee3d | ||
|
|
adeb3e3f42 | ||
|
|
fb1278285b | ||
|
|
e371610849 | ||
|
|
424de63bd1 | ||
|
|
832c925d39 | ||
|
|
378f118d51 | ||
|
|
d651777122 | ||
|
|
65d8ebabb8 | ||
|
|
9195ed9a1b | ||
|
|
5df8f7313c | ||
|
|
25ed6ca0a4 | ||
|
|
9f972995bd | ||
|
|
29c46a9892 | ||
|
|
ef579fe596 | ||
|
|
5b268f62a3 | ||
|
|
1f1c8286a5 | ||
|
|
5401e1ace4 | ||
|
|
69294ef56d | ||
|
|
a685ed8029 | ||
|
|
68d0be3818 | ||
|
|
84e2cb4c1f | ||
|
|
263c664acd | ||
|
|
b12c4c22fb | ||
|
|
9f39fadb9e | ||
|
|
4c29a1ca27 | ||
|
|
f4db124d50 | ||
|
|
8f8c7cccc6 | ||
|
|
e2097c28bd | ||
|
|
871eb444fc |
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve Flux v2
|
||||
title: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
Find out more about your support options and getting help at
|
||||
|
||||
https://fluxcd.io/support/
|
||||
|
||||
-->
|
||||
|
||||
### Describe the bug
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
### To Reproduce
|
||||
|
||||
Steps to reproduce the behaviour:
|
||||
|
||||
1. Provide Flux install instructions
|
||||
2. Provide a GitHub repository with Kubernetes manifests
|
||||
|
||||
### Expected behavior
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
### Additional context
|
||||
|
||||
- Kubernetes version:
|
||||
- Git provider:
|
||||
- Container registry provider:
|
||||
|
||||
Below please provide the output of the following commands:
|
||||
|
||||
```cli
|
||||
flux --version
|
||||
flux check
|
||||
kubectl -n <namespace> get all
|
||||
kubectl -n <namespace> logs deploy/source-controller
|
||||
kubectl -n <namespace> logs deploy/kustomize-controller
|
||||
```
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Ask a question
|
||||
url: https://github.com/fluxcd/flux2/discussions
|
||||
about: Please ask and answer questions here.
|
||||
5
.github/kind/config.yaml
vendored
Normal file
5
.github/kind/config.yaml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
networking:
|
||||
disableDefaultCNI: true # disable kindnet
|
||||
podSubnet: 192.168.0.0/16 # set to Calico's default subnet
|
||||
31
.github/workflows/bootstrap.yaml
vendored
31
.github/workflows/bootstrap.yaml
vendored
@@ -2,12 +2,14 @@ name: bootstrap
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
github:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -15,23 +17,27 @@ jobs:
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
${{ runner.os }}-go1.16-
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15.x
|
||||
go-version: 1.16.x
|
||||
- name: Setup Kubernetes
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg//actions/kustomize@main
|
||||
- name: Build
|
||||
run: |
|
||||
make build-manifests
|
||||
go build -o /tmp/flux ./cmd/flux
|
||||
- name: Set outputs
|
||||
id: vars
|
||||
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
|
||||
- name: Build
|
||||
run: sudo go build -o ./bin/flux ./cmd/flux
|
||||
- name: bootstrap init
|
||||
run: |
|
||||
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
||||
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||
--owner=fluxcd-testing \
|
||||
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \
|
||||
--branch=main \
|
||||
@@ -40,7 +46,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||
- name: bootstrap no-op
|
||||
run: |
|
||||
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
||||
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||
--owner=fluxcd-testing \
|
||||
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \
|
||||
--branch=main \
|
||||
@@ -49,10 +55,11 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||
- name: uninstall
|
||||
run: |
|
||||
./bin/flux uninstall --resources --crds -s --timeout=10m
|
||||
/tmp/flux uninstall -s --keep-namespace
|
||||
kubectl delete ns flux-system --timeout=10m --wait=true
|
||||
- name: bootstrap reinstall
|
||||
run: |
|
||||
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
||||
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||
--owner=fluxcd-testing \
|
||||
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \
|
||||
--branch=main \
|
||||
@@ -61,7 +68,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||
- name: delete repository
|
||||
run: |
|
||||
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
||||
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||
--owner=fluxcd-testing \
|
||||
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \
|
||||
--branch=main \
|
||||
|
||||
7
.github/workflows/docs.yaml
vendored
7
.github/workflows/docs.yaml
vendored
@@ -1,9 +1,8 @@
|
||||
name: Publish docs via GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- docs*
|
||||
- main
|
||||
branches: [ 'docs*', main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -17,7 +16,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
controller_version() {
|
||||
sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml
|
||||
sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
116
.github/workflows/e2e.yaml
vendored
116
.github/workflows/e2e.yaml
vendored
@@ -1,10 +1,10 @@
|
||||
name: e2e
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
kind:
|
||||
@@ -16,17 +16,25 @@ jobs:
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
${{ runner.os }}-go1.16-
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15.x
|
||||
go-version: 1.16.x
|
||||
- name: Setup Kubernetes
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
image: kindest/node:v1.16.9
|
||||
version: "v0.10.0"
|
||||
image: kindest/node:v1.20.2@sha256:8f7ea6e7642c0da54f04a7ee10431549c0257315b3a634f6ef2fecaaedb19bab
|
||||
config: .github/kind/config.yaml # disable KIND-net
|
||||
- name: Setup Calico for network policy
|
||||
run: |
|
||||
kubectl apply -f https://docs.projectcalico.org/v3.16/manifests/calico.yaml
|
||||
kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg//actions/kustomize@main
|
||||
- name: Run test
|
||||
run: make test
|
||||
- name: Check if working tree is dirty
|
||||
@@ -37,34 +45,44 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Build
|
||||
run: sudo go build -o ./bin/flux ./cmd/flux
|
||||
run: |
|
||||
go build -o /tmp/flux ./cmd/flux
|
||||
- name: flux check --pre
|
||||
run: |
|
||||
./bin/flux check --pre
|
||||
/tmp/flux check --pre
|
||||
- name: flux install --manifests
|
||||
run: |
|
||||
./bin/flux install --manifests ./manifests/install/
|
||||
/tmp/flux install --manifests ./manifests/install/
|
||||
- name: flux create secret
|
||||
run: |
|
||||
/tmp/flux create secret git git-ssh-test \
|
||||
--url ssh://git@github.com/stefanprodan/podinfo
|
||||
/tmp/flux create secret git git-https-test \
|
||||
--url https://github.com/stefanprodan/podinfo \
|
||||
--username=test --password=test
|
||||
/tmp/flux create secret helm helm-test \
|
||||
--username=test --password=test
|
||||
- name: flux create source git
|
||||
run: |
|
||||
./bin/flux create source git podinfo \
|
||||
/tmp/flux create source git podinfo \
|
||||
--url https://github.com/stefanprodan/podinfo \
|
||||
--tag-semver=">=3.2.3"
|
||||
- name: flux create source git export apply
|
||||
run: |
|
||||
./bin/flux create source git podinfo-export \
|
||||
/tmp/flux create source git podinfo-export \
|
||||
--url https://github.com/stefanprodan/podinfo \
|
||||
--tag-semver=">=3.2.3" \
|
||||
--export | kubectl apply -f -
|
||||
./bin/flux delete source git podinfo-export --silent
|
||||
/tmp/flux delete source git podinfo-export --silent
|
||||
- name: flux get sources git
|
||||
run: |
|
||||
./bin/flux get sources git
|
||||
/tmp/flux get sources git
|
||||
- name: flux get sources git --all-namespaces
|
||||
run: |
|
||||
./bin/flux get sources git --all-namespaces
|
||||
/tmp/flux get sources git --all-namespaces
|
||||
- name: flux create kustomization
|
||||
run: |
|
||||
./bin/flux create kustomization podinfo \
|
||||
/tmp/flux create kustomization podinfo \
|
||||
--source=podinfo \
|
||||
--path="./deploy/overlays/dev" \
|
||||
--prune=true \
|
||||
@@ -75,106 +93,112 @@ jobs:
|
||||
--health-check-timeout=3m
|
||||
- name: flux reconcile kustomization --with-source
|
||||
run: |
|
||||
./bin/flux reconcile kustomization podinfo --with-source
|
||||
/tmp/flux reconcile kustomization podinfo --with-source
|
||||
- name: flux get kustomizations
|
||||
run: |
|
||||
./bin/flux get kustomizations
|
||||
/tmp/flux get kustomizations
|
||||
- name: flux get kustomizations --all-namespaces
|
||||
run: |
|
||||
./bin/flux get kustomizations --all-namespaces
|
||||
/tmp/flux get kustomizations --all-namespaces
|
||||
- name: flux suspend kustomization
|
||||
run: |
|
||||
./bin/flux suspend kustomization podinfo
|
||||
/tmp/flux suspend kustomization podinfo
|
||||
- name: flux resume kustomization
|
||||
run: |
|
||||
./bin/flux resume kustomization podinfo
|
||||
/tmp/flux resume kustomization podinfo
|
||||
- name: flux export
|
||||
run: |
|
||||
./bin/flux export source git --all
|
||||
./bin/flux export kustomization --all
|
||||
/tmp/flux export source git --all
|
||||
/tmp/flux export kustomization --all
|
||||
- name: flux delete kustomization
|
||||
run: |
|
||||
./bin/flux delete kustomization podinfo --silent
|
||||
/tmp/flux delete kustomization podinfo --silent
|
||||
- name: flux create source helm
|
||||
run: |
|
||||
./bin/flux create source helm podinfo \
|
||||
/tmp/flux create source helm podinfo \
|
||||
--url https://stefanprodan.github.io/podinfo
|
||||
- name: flux create helmrelease --source=HelmRepository/podinfo
|
||||
run: |
|
||||
./bin/flux create hr podinfo-helm \
|
||||
/tmp/flux create hr podinfo-helm \
|
||||
--target-namespace=default \
|
||||
--source=HelmRepository/podinfo \
|
||||
--chart=podinfo \
|
||||
--chart-version=">4.0.0 <5.0.0"
|
||||
- name: flux create helmrelease --source=GitRepository/podinfo
|
||||
run: |
|
||||
./bin/flux create hr podinfo-git \
|
||||
/tmp/flux create hr podinfo-git \
|
||||
--target-namespace=default \
|
||||
--source=GitRepository/podinfo \
|
||||
--chart=./charts/podinfo
|
||||
- name: flux reconcile helmrelease --with-source
|
||||
run: |
|
||||
./bin/flux reconcile helmrelease podinfo-git --with-source
|
||||
/tmp/flux reconcile helmrelease podinfo-git --with-source
|
||||
- name: flux get helmreleases
|
||||
run: |
|
||||
./bin/flux get helmreleases
|
||||
/tmp/flux get helmreleases
|
||||
- name: flux get helmreleases --all-namespaces
|
||||
run: |
|
||||
./bin/flux get helmreleases --all-namespaces
|
||||
/tmp/flux get helmreleases --all-namespaces
|
||||
- name: flux export helmrelease
|
||||
run: |
|
||||
./bin/flux export hr --all
|
||||
/tmp/flux export hr --all
|
||||
- name: flux delete helmrelease podinfo-helm
|
||||
run: |
|
||||
./bin/flux delete hr podinfo-helm --silent
|
||||
/tmp/flux delete hr podinfo-helm --silent
|
||||
- name: flux delete helmrelease podinfo-git
|
||||
run: |
|
||||
./bin/flux delete hr podinfo-git --silent
|
||||
/tmp/flux delete hr podinfo-git --silent
|
||||
- name: flux delete source helm
|
||||
run: |
|
||||
./bin/flux delete source helm podinfo --silent
|
||||
/tmp/flux delete source helm podinfo --silent
|
||||
- name: flux delete source git
|
||||
run: |
|
||||
./bin/flux delete source git podinfo --silent
|
||||
/tmp/flux delete source git podinfo --silent
|
||||
- name: flux create tenant
|
||||
run: |
|
||||
./bin/flux create tenant dev-team --with-namespace=apps
|
||||
./bin/flux -n apps create source helm podinfo \
|
||||
/tmp/flux create tenant dev-team --with-namespace=apps
|
||||
/tmp/flux -n apps create source helm podinfo \
|
||||
--url https://stefanprodan.github.io/podinfo
|
||||
./bin/flux -n apps create hr podinfo-helm \
|
||||
/tmp/flux -n apps create hr podinfo-helm \
|
||||
--source=HelmRepository/podinfo \
|
||||
--chart=podinfo \
|
||||
--chart-version="5.0.x" \
|
||||
--service-account=dev-team
|
||||
- name: flux create image repository
|
||||
run: |
|
||||
./bin/flux create image repository podinfo \
|
||||
/tmp/flux create image repository podinfo \
|
||||
--image=ghcr.io/stefanprodan/podinfo \
|
||||
--interval=1m
|
||||
- name: flux create image policy
|
||||
run: |
|
||||
./bin/flux create image policy podinfo \
|
||||
/tmp/flux create image policy podinfo \
|
||||
--image-ref=podinfo \
|
||||
--interval=1m \
|
||||
--semver=5.0.x
|
||||
--select-semver=5.0.x
|
||||
- name: flux create image policy podinfo-select-alpha
|
||||
run: |
|
||||
/tmp/flux create image policy podinfo-alpha \
|
||||
--image-ref=podinfo \
|
||||
--interval=1m \
|
||||
--select-alpha=desc
|
||||
- name: flux get image policy
|
||||
run: |
|
||||
./bin/flux get image policy podinfo | grep '5.0.3'
|
||||
/tmp/flux get image policy podinfo | grep '5.0.3'
|
||||
- name: flux2-kustomize-helm-example
|
||||
run: |
|
||||
./bin/flux create source git flux-system \
|
||||
/tmp/flux create source git flux-system \
|
||||
--url=https://github.com/fluxcd/flux2-kustomize-helm-example \
|
||||
--branch=main
|
||||
./bin/flux create kustomization flux-system \
|
||||
/tmp/flux create kustomization flux-system \
|
||||
--source=flux-system \
|
||||
--path=./clusters/staging
|
||||
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=2m
|
||||
- name: flux check
|
||||
run: |
|
||||
./bin/flux check
|
||||
/tmp/flux check
|
||||
- name: flux uninstall
|
||||
run: |
|
||||
./bin/flux uninstall --crds --silent --timeout=10m
|
||||
/tmp/flux uninstall --silent
|
||||
- name: Debug failure
|
||||
if: failure()
|
||||
run: |
|
||||
|
||||
25
.github/workflows/fossa.yml
vendored
25
.github/workflows/fossa.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: FOSSA
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "^1.14.x"
|
||||
- name: Add GOPATH to GITHUB_ENV
|
||||
run: echo "GOPATH=$(go env GOPATH)" >>"$GITHUB_ENV"
|
||||
- name: Add GOPATH to GITHUB_PATH
|
||||
run: echo "$GOPATH/bin" >>"$GITHUB_PATH"
|
||||
- name: Run FOSSA scan and upload build data
|
||||
uses: fossa-contrib/fossa-action@v1
|
||||
with:
|
||||
# FOSSA Push-Only API Token
|
||||
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
|
||||
github-token: ${{ github.token }}
|
||||
@@ -2,9 +2,9 @@ name: rebase
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
types: [ opened ]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
types: [ created ]
|
||||
|
||||
jobs:
|
||||
rebase:
|
||||
39
.github/workflows/release.yaml
vendored
39
.github/workflows/release.yaml
vendored
@@ -2,8 +2,7 @@ name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
tags: [ 'v*' ]
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
@@ -16,7 +15,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15.x
|
||||
go-version: 1.16.x
|
||||
- name: Download release notes utility
|
||||
env:
|
||||
GH_REL_URL: https://github.com/buchanae/github-release-notes/releases/download/0.2.0/github-release-notes-linux-amd64-0.2.0.tar.gz
|
||||
@@ -29,38 +28,10 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg//actions/kustomize@main
|
||||
- name: Generate manifests tarball
|
||||
run: |
|
||||
mkdir -p ./output
|
||||
files=""
|
||||
|
||||
# build controllers
|
||||
for controller in ./manifests/bases/*/; do
|
||||
output_path="./output/$(basename $controller).yaml"
|
||||
echo "building $controller to $output_path"
|
||||
|
||||
kustomize build $controller > $output_path
|
||||
files+=" $(basename $output_path)"
|
||||
done
|
||||
|
||||
# build rbac
|
||||
rbac_path="./manifests/rbac"
|
||||
rbac_output_path="./output/rbac.yaml"
|
||||
echo "building $rbac_path to $rbac_output_path"
|
||||
kustomize build $rbac_path > $rbac_output_path
|
||||
files+=" $(basename $rbac_output_path)"
|
||||
|
||||
# build policies
|
||||
policies_path="./manifests/policies"
|
||||
policies_output_path="./output/policies.yaml"
|
||||
echo "building $policies_path to $policies_output_path"
|
||||
kustomize build $policies_path > $policies_output_path
|
||||
files+=" $(basename $policies_output_path)"
|
||||
|
||||
# create tarball
|
||||
cd ./output && tar -cvzf manifests.tar.gz $files
|
||||
- name: Generate install manifest
|
||||
- name: Generate manifests
|
||||
run: |
|
||||
make build-manifests
|
||||
./manifests/scripts/bundle.sh ./output manifests.tar.gz
|
||||
kustomize build ./manifests/install > ./output/install.yaml
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v1
|
||||
|
||||
60
.github/workflows/scan.yaml
vendored
Normal file
60
.github/workflows/scan.yaml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '18 10 * * 3'
|
||||
|
||||
jobs:
|
||||
fossa:
|
||||
name: FOSSA
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run FOSSA scan and upload build data
|
||||
uses: fossa-contrib/fossa-action@v1
|
||||
with:
|
||||
# FOSSA Push-Only API Token
|
||||
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
snyk:
|
||||
name: Snyk
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg//actions/kustomize@main
|
||||
- name: Build manifests
|
||||
run: |
|
||||
make build-manifests
|
||||
- name: Run Snyk to check for vulnerabilities
|
||||
uses: snyk/actions/golang@master
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
with:
|
||||
args: --sarif-file-output=snyk.sarif
|
||||
- name: Upload result to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: snyk.sarif
|
||||
|
||||
codeql:
|
||||
name: CodeQL
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: go
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -13,7 +13,10 @@ jobs:
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16.x
|
||||
- name: Update component versions
|
||||
id: update
|
||||
run: |
|
||||
@@ -21,18 +24,19 @@ jobs:
|
||||
|
||||
bump_version() {
|
||||
local RELEASE_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
|
||||
local CURRENT_VERSION=$(sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
|
||||
local CURRENT_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
|
||||
|
||||
if [[ "${RELEASE_VERSION}" != "${CURRENT_VERSION}" ]]; then
|
||||
# bump kustomize
|
||||
sed -i "s/\($1\/archive\/\)v.*\(.zip\/\/$1-\).*\(\/config.*\)/\1${RELEASE_VERSION}\2${RELEASE_VERSION/v}\3/g" "manifests/bases/$1/kustomization.yaml"
|
||||
sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${RELEASE_VERSION}\2/g" "manifests/bases/$1/kustomization.yaml"
|
||||
|
||||
if [[ ! -z $(go list -m all | grep "github.com/fluxcd/$1/api" | awk '{print $2}') ]]; then
|
||||
# bump go mod
|
||||
go mod edit -require="github.com/fluxcd/$1/api@${RELEASE_VERSION}"
|
||||
fi
|
||||
|
||||
PR_BODY="$PR_BODY- $1 to ${RELEASE_VERSION}%0A"
|
||||
# NB: special URL encoded formatting required for newlines
|
||||
PR_BODY="$PR_BODY- $1 to ${RELEASE_VERSION}%0A https://github.com/fluxcd/$1/blob/${RELEASE_VERSION}/CHANGELOG.md%0A"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -51,7 +55,7 @@ jobs:
|
||||
# diff change
|
||||
git diff
|
||||
|
||||
# export PR_BODY for PR
|
||||
# export PR_BODY for PR and commit
|
||||
echo "::set-output name=pr_body::$PR_BODY"
|
||||
}
|
||||
|
||||
@@ -60,19 +64,22 @@ jobs:
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
commit-message: Update toolkit components
|
||||
commit-message: |
|
||||
Update toolkit components
|
||||
|
||||
${{ steps.update.outputs.pr_body }}
|
||||
committer: GitHub <noreply@github.com>
|
||||
author: fluxcdbot <fluxcdbot@users.noreply.github.com>
|
||||
signoff: true
|
||||
branch: update-components
|
||||
title: Update toolkit components
|
||||
body: |
|
||||
${{ steps.update.outputs.pr_body }}
|
||||
|
||||
Auto-generated by [create-pull-request][1]
|
||||
|
||||
[1]: https://github.com/peter-evans/create-pull-request
|
||||
branch: update-components
|
||||
labels: |
|
||||
area/build
|
||||
reviewers: ${{ secrets.ASSIGNEES }}
|
||||
|
||||
- name: Check output
|
||||
run: |
|
||||
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
|
||||
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -14,4 +14,5 @@
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
bin/
|
||||
output/
|
||||
output/
|
||||
cmd/flux/manifests/
|
||||
|
||||
@@ -20,6 +20,9 @@ builds:
|
||||
id: darwin
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- <<: *build_defaults
|
||||
id: windows
|
||||
goos:
|
||||
|
||||
5
Makefile
5
Makefile
@@ -11,9 +11,12 @@ fmt:
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
test: tidy fmt vet docs
|
||||
test: build-manifests tidy fmt vet docs
|
||||
go test ./... -coverprofile cover.out
|
||||
|
||||
build-manifests:
|
||||
./manifests/scripts/bundle.sh
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 go build -o ./bin/flux ./cmd/flux
|
||||
|
||||
|
||||
41
README.md
41
README.md
@@ -60,11 +60,12 @@ To get started with Flux, start [browsing the
|
||||
documentation](https://toolkit.fluxcd.io) or get started with one of
|
||||
the following guides:
|
||||
|
||||
- [Get started with Flux (deep dive)](https://toolkit.fluxcd.io/get-started/)
|
||||
- [Installation](https://toolkit.fluxcd.io/guides/installation/)
|
||||
- [Get started with Flux](https://toolkit.fluxcd.io/get-started/)
|
||||
- [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/)
|
||||
- [Setup Notifications](https://toolkit.fluxcd.io/guides/notifications/)
|
||||
- [Setup Webhook Receivers](https://toolkit.fluxcd.io/guides/webhook-receivers/)
|
||||
- [Automate image updates to Git](https://toolkit.fluxcd.io/guides/image-update/)
|
||||
- [Manage Kubernetes secrets with Mozilla SOPS](https://toolkit.fluxcd.io/guides/mozilla-sops/)
|
||||
|
||||
If you need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
||||
|
||||
## GitOps Toolkit
|
||||
|
||||
@@ -94,21 +95,33 @@ guides](https://toolkit.fluxcd.io/dev-guides/source-watcher/).
|
||||
- [Provider CRD](https://toolkit.fluxcd.io/components/notification/provider/)
|
||||
- [Alert CRD](https://toolkit.fluxcd.io/components/notification/alert/)
|
||||
- [Receiver CRD](https://toolkit.fluxcd.io/components/notification/receiver/)
|
||||
|
||||
- [Image Automation Controllers](https://toolkit.fluxcd.io/components/image/controller/)
|
||||
- [ImageRepository CRD](https://toolkit.fluxcd.io/components/image/imagerepositories/)
|
||||
- [ImagePolicy CRD](https://toolkit.fluxcd.io/components/image/imagepolicies/)
|
||||
- [ImageUpdateAutomation CRD](https://toolkit.fluxcd.io/components/image/imageupdateautomations/)
|
||||
|
||||
## Community
|
||||
|
||||
The Flux project is always looking for new contributors and there are a multitude of ways to get involved.
|
||||
Depending on what you want to do, some of the following bits might be your first steps:
|
||||
Need help or want to contribute? Please see the links below. The Flux project is always looking for
|
||||
new contributors and there are a multitude of ways to get involved.
|
||||
|
||||
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
|
||||
- Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
||||
- Ask questions and propose features on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||
- And if you are completely new to Flux and the GitOps Toolkit, take a look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
|
||||
- To be part of the conversation about Flux's development, [join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
|
||||
- Check out [how to contribute](CONTRIBUTING.md) to the project
|
||||
- Getting Started?
|
||||
- Look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
|
||||
- Need help?
|
||||
- First: Ask questions on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||
- Second: Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
||||
- Please follow our [Support Guidelines](https://fluxcd.io/support/)
|
||||
(in short: be nice, be respectful of volunteers' time, understand that maintainers and
|
||||
contributors cannot respond to all DMs, and keep discussions in the public #flux channel as much as possible).
|
||||
- Have feature proposals or want to contribute?
|
||||
- Propose features on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
|
||||
- [Join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
|
||||
- Check out [how to contribute](CONTRIBUTING.md) to the project
|
||||
|
||||
### Events
|
||||
|
||||
Check out our **[events calendar](https://fluxcd.io/community/#talks)**, both with upcoming talks you can attend or past events videos you can watch.
|
||||
Check out our **[events calendar](https://fluxcd.io/community/#talks)**,
|
||||
both with upcoming talks you can attend or past events videos you can watch.
|
||||
|
||||
We look forward to seeing you with us!
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM stefanprodan/alpine-base:latest
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -10,19 +10,24 @@ Usage:
|
||||
run: flux -v
|
||||
```
|
||||
|
||||
This action places the `flux` binary inside your repository root under `bin/flux`.
|
||||
You should add `bin/flux` to your `.gitignore` file, as in the following example:
|
||||
|
||||
```gitignore
|
||||
# ignore flux binary
|
||||
bin/flux
|
||||
```
|
||||
|
||||
Note that this action can only be used on GitHub **Linux AMD64** runners.
|
||||
The latest stable version of the `flux` binary is downloaded from
|
||||
GitHub [releases](https://github.com/fluxcd/flux2/releases)
|
||||
and placed at `/usr/local/bin/flux`.
|
||||
|
||||
You can download a specific version with:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: Setup Flux CLI
|
||||
uses: fluxcd/flux2/action@main
|
||||
with:
|
||||
version: 0.8.0
|
||||
```
|
||||
|
||||
### Automate Flux updates
|
||||
|
||||
Example workflow for updating Flux's components generated with `flux bootstrap --arch=amd64 --path=clusters/production`:
|
||||
Example workflow for updating Flux's components generated with `flux bootstrap --path=clusters/production`:
|
||||
|
||||
```yaml
|
||||
name: update-flux
|
||||
@@ -43,7 +48,7 @@ jobs:
|
||||
- name: Check for updates
|
||||
id: update
|
||||
run: |
|
||||
flux install --arch=amd64 \
|
||||
flux install \
|
||||
--export > ./clusters/production/flux-system/gotk-components.yaml
|
||||
|
||||
VERSION="$(flux -v)"
|
||||
|
||||
@@ -1,15 +1,38 @@
|
||||
name: 'kustomize'
|
||||
description: 'A GitHub Action for running Flux commands'
|
||||
author: 'Flux project'
|
||||
name: Setup Flux CLI
|
||||
description: A GitHub Action for running Flux commands
|
||||
author: Stefan Prodan
|
||||
branding:
|
||||
icon: 'command'
|
||||
color: 'blue'
|
||||
color: blue
|
||||
icon: command
|
||||
inputs:
|
||||
version:
|
||||
description: 'strict semver'
|
||||
description: "Flux version e.g. 0.8.0 (defaults to latest stable release)"
|
||||
required: false
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- ${{ inputs.version }}
|
||||
using: composite
|
||||
steps:
|
||||
- name: "Download flux binary to tmp"
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION=${{ inputs.version }}
|
||||
|
||||
if [ -z $VERSION ]; then
|
||||
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
|
||||
fi
|
||||
|
||||
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_amd64.tar.gz"
|
||||
curl -sL ${BIN_URL} -o /tmp/flux.tar.gz
|
||||
mkdir -p /tmp/flux
|
||||
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz
|
||||
- name: "Add flux binary to /usr/local/bin"
|
||||
shell: bash
|
||||
run: |
|
||||
sudo cp /tmp/flux/flux /usr/local/bin
|
||||
- name: "Cleanup tmp"
|
||||
shell: bash
|
||||
run: |
|
||||
rm -rf /tmp/flux/ /tmp/flux.tar.gz
|
||||
- name: "Verify correct installation of binary"
|
||||
shell: bash
|
||||
run: |
|
||||
flux -v
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2020 The Flux authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=$1
|
||||
|
||||
if [ -z $VERSION ]; then
|
||||
# Find latest release if no version is specified
|
||||
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
|
||||
fi
|
||||
|
||||
# Download linux binary
|
||||
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_amd64.tar.gz"
|
||||
curl -sL $BIN_URL | tar xz
|
||||
|
||||
# Copy binary to GitHub runner
|
||||
mkdir -p $GITHUB_WORKSPACE/bin
|
||||
mv ./flux $GITHUB_WORKSPACE/bin
|
||||
chmod +x $GITHUB_WORKSPACE/bin/flux
|
||||
|
||||
# Print version
|
||||
$GITHUB_WORKSPACE/bin/flux -v
|
||||
|
||||
# Add binary to GitHub runner path
|
||||
echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH
|
||||
echo "$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)/bin" >> $GITHUB_PATH
|
||||
@@ -60,6 +60,7 @@ type bootstrapFlags struct {
|
||||
requiredComponents []string
|
||||
tokenAuth bool
|
||||
clusterDomain string
|
||||
tolerationKeys []string
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -69,8 +70,8 @@ const (
|
||||
var bootstrapArgs = NewBootstrapFlags()
|
||||
|
||||
func init() {
|
||||
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", rootArgs.defaults.Version,
|
||||
"toolkit version")
|
||||
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "",
|
||||
"toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
|
||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
|
||||
"list of components, accepts comma-separated values")
|
||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
|
||||
@@ -91,6 +92,8 @@ func init() {
|
||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description())
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
|
||||
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
|
||||
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
||||
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
||||
rootCmd.AddCommand(bootstrapCmd)
|
||||
@@ -123,10 +126,24 @@ func bootstrapValidate() error {
|
||||
}
|
||||
|
||||
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
|
||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
bootstrapArgs.version = ver
|
||||
}
|
||||
|
||||
manifestsBase := ""
|
||||
if isEmbeddedVersion(bootstrapArgs.version) {
|
||||
if err := writeEmbeddedManifests(tmpDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
manifestsBase = tmpDir
|
||||
}
|
||||
|
||||
opts := install.Options{
|
||||
BaseURL: localManifests,
|
||||
Version: bootstrapArgs.version,
|
||||
Namespace: rootArgs.namespace,
|
||||
Namespace: namespace,
|
||||
Components: bootstrapComponents(),
|
||||
Registry: bootstrapArgs.registry,
|
||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||
@@ -138,13 +155,14 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
||||
Timeout: rootArgs.timeout,
|
||||
TargetPath: targetPath,
|
||||
ClusterDomain: bootstrapArgs.clusterDomain,
|
||||
TolerationKeys: bootstrapArgs.tolerationKeys,
|
||||
}
|
||||
|
||||
if localManifests == "" {
|
||||
opts.BaseURL = rootArgs.defaults.BaseURL
|
||||
}
|
||||
|
||||
output, err := install.Generate(opts)
|
||||
output, err := install.Generate(opts, manifestsBase)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
||||
}
|
||||
@@ -162,12 +180,16 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
|
||||
return fmt.Errorf("install failed")
|
||||
}
|
||||
|
||||
for _, deployment := range components {
|
||||
kubectlArgs = []string{"-n", rootArgs.namespace, "rollout", "status", "deployment", deployment, "--timeout", rootArgs.timeout.String()}
|
||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
}
|
||||
statusChecker, err := NewStatusChecker(time.Second, rootArgs.timeout)
|
||||
if err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
}
|
||||
|
||||
logger.Waitingf("verifying installation")
|
||||
if err := statusChecker.Assess(components...); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -248,7 +270,7 @@ func shouldCreateDeployKey(ctx context.Context, kubeClient client.Client, namesp
|
||||
}
|
||||
|
||||
func generateDeployKey(ctx context.Context, kubeClient client.Client, url *url.URL, namespace string) (string, error) {
|
||||
pair, err := generateKeyPair(ctx, sourceArgs.GitKeyAlgorithm, sourceArgs.GitRSABits, sourceArgs.GitECDSACurve)
|
||||
pair, err := generateKeyPair(ctx, sourceGitArgs.keyAlgorithm, sourceGitArgs.keyRSABits, sourceGitArgs.keyECDSACurve)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ func init() {
|
||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository name")
|
||||
bootstrapGitHubCmd.Flags().StringArrayVar(&githubArgs.teams, "team", []string{}, "GitHub team to be given maintainer access")
|
||||
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "is personal repository")
|
||||
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "is private repository")
|
||||
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "if true, the owner is assumed to be a GitHub user; otherwise an org")
|
||||
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "if true, the repository is assumed to be private")
|
||||
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||
@@ -125,13 +125,25 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(githubArgs.path.String()))
|
||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(
|
||||
ctx,
|
||||
kubeClient,
|
||||
rootArgs.namespace,
|
||||
filepath.ToSlash(githubArgs.path.String()),
|
||||
)
|
||||
|
||||
if bootstrapPathDiffers {
|
||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
||||
}
|
||||
|
||||
repository, err := git.NewRepository(githubArgs.repository, githubArgs.owner, githubArgs.hostname, ghToken, "flux", githubArgs.owner+"@users.noreply.github.com")
|
||||
repository, err := git.NewRepository(
|
||||
githubArgs.repository,
|
||||
githubArgs.owner,
|
||||
githubArgs.hostname,
|
||||
ghToken,
|
||||
"flux",
|
||||
githubArgs.owner+"@users.noreply.github.com",
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -190,13 +202,22 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// generate install manifests
|
||||
logger.Generatef("generating manifests")
|
||||
installManifest, err := generateInstallManifests(githubArgs.path.String(), rootArgs.namespace, tmpDir, bootstrapArgs.manifestsPath)
|
||||
installManifest, err := generateInstallManifests(
|
||||
githubArgs.path.String(),
|
||||
rootArgs.namespace,
|
||||
tmpDir,
|
||||
bootstrapArgs.manifestsPath,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage install manifests
|
||||
changed, err = repository.Commit(ctx, path.Join(githubArgs.path.String(), rootArgs.namespace), "Add manifests")
|
||||
changed, err = repository.Commit(
|
||||
ctx,
|
||||
path.Join(githubArgs.path.String(), rootArgs.namespace),
|
||||
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -270,13 +291,25 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// configure repo synchronization
|
||||
logger.Actionf("generating sync manifests")
|
||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapArgs.branch, rootArgs.namespace, rootArgs.namespace, filepath.ToSlash(githubArgs.path.String()), tmpDir, githubArgs.interval)
|
||||
syncManifests, err := generateSyncManifests(
|
||||
repoURL,
|
||||
bootstrapArgs.branch,
|
||||
rootArgs.namespace,
|
||||
rootArgs.namespace,
|
||||
filepath.ToSlash(githubArgs.path.String()),
|
||||
tmpDir,
|
||||
githubArgs.interval,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// commit and push manifests
|
||||
if changed, err = repository.Commit(ctx, path.Join(githubArgs.path.String(), rootArgs.namespace), "Add manifests"); err != nil {
|
||||
if changed, err = repository.Commit(
|
||||
ctx,
|
||||
path.Join(githubArgs.path.String(), rootArgs.namespace),
|
||||
fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version),
|
||||
); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
|
||||
@@ -89,8 +89,8 @@ var gitlabArgs gitlabFlags
|
||||
func init() {
|
||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name")
|
||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.personal, "personal", false, "is personal repository")
|
||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.private, "private", true, "is private repository")
|
||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.personal, "personal", false, "if true, the owner is assumed to be a GitLab user; otherwise a group")
|
||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.private, "private", true, "if true, the repository is assumed to be private")
|
||||
bootstrapGitLabCmd.Flags().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.sshHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||
@@ -131,7 +131,14 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
||||
}
|
||||
|
||||
repository, err := git.NewRepository(gitlabArgs.repository, gitlabArgs.owner, gitlabArgs.hostname, glToken, "flux", gitlabArgs.owner+"@users.noreply.gitlab.com")
|
||||
repository, err := git.NewRepository(
|
||||
gitlabArgs.repository,
|
||||
gitlabArgs.owner,
|
||||
gitlabArgs.hostname,
|
||||
glToken,
|
||||
"flux",
|
||||
gitlabArgs.owner+"@users.noreply.gitlab.com",
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -169,13 +176,22 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// generate install manifests
|
||||
logger.Generatef("generating manifests")
|
||||
installManifest, err := generateInstallManifests(gitlabArgs.path.String(), rootArgs.namespace, tmpDir, bootstrapArgs.manifestsPath)
|
||||
installManifest, err := generateInstallManifests(
|
||||
gitlabArgs.path.String(),
|
||||
rootArgs.namespace,
|
||||
tmpDir,
|
||||
bootstrapArgs.manifestsPath,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage install manifests
|
||||
changed, err = repository.Commit(ctx, path.Join(gitlabArgs.path.String(), rootArgs.namespace), "Add manifests")
|
||||
changed, err = repository.Commit(
|
||||
ctx,
|
||||
path.Join(gitlabArgs.path.String(), rootArgs.namespace),
|
||||
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -249,13 +265,25 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// configure repo synchronization
|
||||
logger.Actionf("generating sync manifests")
|
||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapArgs.branch, rootArgs.namespace, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String()), tmpDir, gitlabArgs.interval)
|
||||
syncManifests, err := generateSyncManifests(
|
||||
repoURL,
|
||||
bootstrapArgs.branch,
|
||||
rootArgs.namespace,
|
||||
rootArgs.namespace,
|
||||
filepath.ToSlash(gitlabArgs.path.String()),
|
||||
tmpDir,
|
||||
gitlabArgs.interval,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// commit and push manifests
|
||||
if changed, err = repository.Commit(ctx, path.Join(gitlabArgs.path.String(), rootArgs.namespace), "Add manifests"); err != nil {
|
||||
if changed, err = repository.Commit(
|
||||
ctx,
|
||||
path.Join(gitlabArgs.path.String(), rootArgs.namespace),
|
||||
fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version),
|
||||
); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
|
||||
@@ -21,13 +21,19 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver/v4"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/version"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
)
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
@@ -45,8 +51,9 @@ the local environment is configured correctly and if the installed components ar
|
||||
}
|
||||
|
||||
type checkFlags struct {
|
||||
pre bool
|
||||
components []string
|
||||
pre bool
|
||||
components []string
|
||||
extraComponents []string
|
||||
}
|
||||
|
||||
type kubectlVersion struct {
|
||||
@@ -60,6 +67,8 @@ func init() {
|
||||
"only run pre-installation checks")
|
||||
checkCmd.Flags().StringSliceVar(&checkArgs.components, "components", rootArgs.defaults.Components,
|
||||
"list of components, accepts comma-separated values")
|
||||
checkCmd.Flags().StringSliceVar(&checkArgs.extraComponents, "components-extra", nil,
|
||||
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
||||
rootCmd.AddCommand(checkCmd)
|
||||
}
|
||||
|
||||
@@ -70,11 +79,13 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
||||
logger.Actionf("checking prerequisites")
|
||||
checkFailed := false
|
||||
|
||||
if !kubectlCheck(ctx, ">=1.18.0") {
|
||||
fluxCheck()
|
||||
|
||||
if !kubectlCheck(ctx, ">=1.18.0-0") {
|
||||
checkFailed = true
|
||||
}
|
||||
|
||||
if !kubernetesCheck(">=1.16.0") {
|
||||
if !kubernetesCheck(">=1.16.0-0") {
|
||||
checkFailed = true
|
||||
}
|
||||
|
||||
@@ -97,7 +108,29 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func kubectlCheck(ctx context.Context, version string) bool {
|
||||
func fluxCheck() {
|
||||
curSv, err := version.ParseVersion(VERSION)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Exclude development builds.
|
||||
if curSv.Prerelease() != "" {
|
||||
return
|
||||
}
|
||||
latest, err := install.GetLatestVersion()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
latestSv, err := version.ParseVersion(latest)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if latestSv.GreaterThan(curSv) {
|
||||
logger.Failuref("flux %s <%s (new version is available, please upgrade)", curSv, latestSv)
|
||||
}
|
||||
}
|
||||
|
||||
func kubectlCheck(ctx context.Context, constraint string) bool {
|
||||
_, err := exec.LookPath("kubectl")
|
||||
if err != nil {
|
||||
logger.Failuref("kubectl not found")
|
||||
@@ -113,58 +146,58 @@ func kubectlCheck(ctx context.Context, version string) bool {
|
||||
|
||||
kv := &kubectlVersion{}
|
||||
if err = json.Unmarshal([]byte(output), kv); err != nil {
|
||||
logger.Failuref("kubectl version output can't be unmarshaled")
|
||||
logger.Failuref("kubectl version output can't be unmarshalled")
|
||||
return false
|
||||
}
|
||||
|
||||
v, err := semver.ParseTolerant(kv.ClientVersion.GitVersion)
|
||||
v, err := version.ParseVersion(kv.ClientVersion.GitVersion)
|
||||
if err != nil {
|
||||
logger.Failuref("kubectl version can't be parsed")
|
||||
return false
|
||||
}
|
||||
|
||||
rng, _ := semver.ParseRange(version)
|
||||
if !rng(v) {
|
||||
logger.Failuref("kubectl version must be %s", version)
|
||||
c, _ := semver.NewConstraint(constraint)
|
||||
if !c.Check(v) {
|
||||
logger.Failuref("kubectl version %s < %s", v.Original(), constraint)
|
||||
return false
|
||||
}
|
||||
|
||||
logger.Successf("kubectl %s %s", v.String(), version)
|
||||
logger.Successf("kubectl %s %s", v.String(), constraint)
|
||||
return true
|
||||
}
|
||||
|
||||
func kubernetesCheck(version string) bool {
|
||||
func kubernetesCheck(constraint string) bool {
|
||||
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
clientSet, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
ver, err := client.Discovery().ServerVersion()
|
||||
kv, err := clientSet.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
logger.Failuref("Kubernetes API call failed: %s", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
v, err := semver.ParseTolerant(ver.String())
|
||||
v, err := version.ParseVersion(kv.String())
|
||||
if err != nil {
|
||||
logger.Failuref("Kubernetes version can't be determined")
|
||||
return false
|
||||
}
|
||||
|
||||
rng, _ := semver.ParseRange(version)
|
||||
if !rng(v) {
|
||||
logger.Failuref("Kubernetes version must be %s", version)
|
||||
c, _ := semver.NewConstraint(constraint)
|
||||
if !c.Check(v) {
|
||||
logger.Failuref("Kubernetes version %s < %s", v.Original(), constraint)
|
||||
return false
|
||||
}
|
||||
|
||||
logger.Successf("Kubernetes %s %s", v.String(), version)
|
||||
logger.Successf("Kubernetes %s %s", v.String(), constraint)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -172,18 +205,29 @@ func componentsCheck() bool {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
statusChecker, err := NewStatusChecker(time.Second, rootArgs.timeout)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ok := true
|
||||
for _, deployment := range checkArgs.components {
|
||||
kubectlArgs := []string{"-n", rootArgs.namespace, "rollout", "status", "deployment", deployment, "--timeout", rootArgs.timeout.String()}
|
||||
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||
logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
|
||||
ok = false
|
||||
} else {
|
||||
logger.Successf("%s is healthy", deployment)
|
||||
}
|
||||
kubectlArgs = []string{"-n", rootArgs.namespace, "get", "deployment", deployment, "-o", "jsonpath=\"{..image}\""}
|
||||
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err == nil {
|
||||
logger.Actionf(strings.TrimPrefix(strings.TrimSuffix(output, "\""), "\""))
|
||||
selector := client.MatchingLabels{"app.kubernetes.io/instance": rootArgs.namespace}
|
||||
var list v1.DeploymentList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace), selector); err == nil {
|
||||
for _, d := range list.Items {
|
||||
if err := statusChecker.Assess(d.Name); err != nil {
|
||||
ok = false
|
||||
} else {
|
||||
logger.Successf("%s: healthy", d.Name)
|
||||
}
|
||||
for _, c := range d.Spec.Template.Spec.Containers {
|
||||
logger.Actionf(c.Image)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok
|
||||
|
||||
@@ -18,12 +18,14 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/transform"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
@@ -62,11 +64,12 @@ var createHelmReleaseCmd = &cobra.Command{
|
||||
--source=Bucket/podinfo \
|
||||
--chart=./charts/podinfo
|
||||
|
||||
# Create a HelmRelease with values from a local YAML file
|
||||
# Create a HelmRelease with values from local YAML files
|
||||
flux create hr podinfo \
|
||||
--source=HelmRepository/podinfo \
|
||||
--chart=podinfo \
|
||||
--values=./my-values.yaml
|
||||
--values=./my-values1.yaml \
|
||||
--values=./my-values2.yaml
|
||||
|
||||
# Create a HelmRelease with values from a Kubernetes secret
|
||||
kubectl -n app create secret generic my-secret-values \
|
||||
@@ -105,7 +108,7 @@ type helmReleaseFlags struct {
|
||||
chart string
|
||||
chartVersion string
|
||||
targetNamespace string
|
||||
valuesFile string
|
||||
valuesFile []string
|
||||
valuesFrom flags.HelmReleaseValuesFrom
|
||||
saName string
|
||||
}
|
||||
@@ -120,7 +123,7 @@ func init() {
|
||||
createHelmReleaseCmd.Flags().StringArrayVar(&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.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
|
||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.valuesFile, "values", "", "local path to the values.yaml file")
|
||||
createHelmReleaseCmd.Flags().StringArrayVar(&helmReleaseArgs.valuesFile, "values", nil, "local path to values.yaml files")
|
||||
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
|
||||
createCmd.AddCommand(createHelmReleaseCmd)
|
||||
}
|
||||
@@ -175,18 +178,37 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
||||
helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
|
||||
}
|
||||
|
||||
if helmReleaseArgs.valuesFile != "" {
|
||||
data, err := ioutil.ReadFile(helmReleaseArgs.valuesFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading values from %s failed: %w", helmReleaseArgs.valuesFile, err)
|
||||
if len(helmReleaseArgs.valuesFile) > 0 {
|
||||
var valuesMap map[string]interface{}
|
||||
for _, v := range helmReleaseArgs.valuesFile {
|
||||
data, err := ioutil.ReadFile(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading values from %s failed: %w", v, err)
|
||||
}
|
||||
|
||||
jsonBytes, err := yaml.YAMLToJSON(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("converting values to JSON from %s failed: %w", v, err)
|
||||
}
|
||||
|
||||
jsonMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal(jsonBytes, &jsonMap); err != nil {
|
||||
return fmt.Errorf("unmarshaling values from %s failed: %w", v, err)
|
||||
}
|
||||
|
||||
if valuesMap == nil {
|
||||
valuesMap = jsonMap
|
||||
} else {
|
||||
valuesMap = transform.MergeMaps(valuesMap, jsonMap)
|
||||
}
|
||||
}
|
||||
|
||||
json, err := yaml.YAMLToJSON(data)
|
||||
jsonRaw, err := json.Marshal(valuesMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("converting values to JSON from %s failed: %w", helmReleaseArgs.valuesFile, err)
|
||||
return fmt.Errorf("marshaling values failed: %w", err)
|
||||
}
|
||||
|
||||
helmRelease.Spec.Values = &apiextensionsv1.JSON{Raw: json}
|
||||
helmRelease.Spec.Values = &apiextensionsv1.JSON{Raw: jsonRaw}
|
||||
}
|
||||
|
||||
if helmReleaseArgs.valuesFrom.String() != "" {
|
||||
|
||||
@@ -18,6 +18,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp/syntax"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -39,9 +43,13 @@ the status of the object.`,
|
||||
RunE: createImagePolicyRun}
|
||||
|
||||
type imagePolicyFlags struct {
|
||||
imageRef string
|
||||
semver string
|
||||
filterRegex string
|
||||
imageRef string
|
||||
semver string
|
||||
alpha string
|
||||
numeric string
|
||||
filterRegex string
|
||||
filterExtract string
|
||||
filterNumerical string
|
||||
}
|
||||
|
||||
var imagePolicyArgs = imagePolicyFlags{}
|
||||
@@ -49,8 +57,11 @@ var imagePolicyArgs = imagePolicyFlags{}
|
||||
func init() {
|
||||
flags := createImagePolicyCmd.Flags()
|
||||
flags.StringVar(&imagePolicyArgs.imageRef, "image-ref", "", "the name of an image repository object")
|
||||
flags.StringVar(&imagePolicyArgs.semver, "semver", "", "a semver range to apply to tags; e.g., '1.x'")
|
||||
flags.StringVar(&imagePolicyArgs.filterRegex, "filter-regex", "", " regular expression pattern used to filter the image tags")
|
||||
flags.StringVar(&imagePolicyArgs.semver, "select-semver", "", "a semver range to apply to tags; e.g., '1.x'")
|
||||
flags.StringVar(&imagePolicyArgs.alpha, "select-alpha", "", "use alphabetical sorting to select image; either \"asc\" meaning select the last, or \"desc\" meaning select the first")
|
||||
flags.StringVar(&imagePolicyArgs.numeric, "select-numeric", "", "use numeric sorting to select image; either \"asc\" meaning select the last, or \"desc\" meaning select the first")
|
||||
flags.StringVar(&imagePolicyArgs.filterRegex, "filter-regex", "", "regular expression pattern used to filter the image tags")
|
||||
flags.StringVar(&imagePolicyArgs.filterExtract, "filter-extract", "", "replacement pattern (using capture groups from --filter-regex) to use for sorting")
|
||||
|
||||
createImageCmd.AddCommand(createImagePolicyCmd)
|
||||
}
|
||||
@@ -90,18 +101,49 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
switch {
|
||||
case imagePolicyArgs.semver != "" && imagePolicyArgs.alpha != "":
|
||||
case imagePolicyArgs.semver != "" && imagePolicyArgs.numeric != "":
|
||||
case imagePolicyArgs.alpha != "" && imagePolicyArgs.numeric != "":
|
||||
return fmt.Errorf("only one of --select-semver, --select-alpha or --select-numeric can be specified")
|
||||
case imagePolicyArgs.semver != "":
|
||||
policy.Spec.Policy.SemVer = &imagev1.SemVerPolicy{
|
||||
Range: imagePolicyArgs.semver,
|
||||
}
|
||||
case imagePolicyArgs.alpha != "":
|
||||
if imagePolicyArgs.alpha != "desc" && imagePolicyArgs.alpha != "asc" {
|
||||
return fmt.Errorf("--select-alpha must be one of [\"asc\", \"desc\"]")
|
||||
}
|
||||
policy.Spec.Policy.Alphabetical = &imagev1.AlphabeticalPolicy{
|
||||
Order: imagePolicyArgs.alpha,
|
||||
}
|
||||
case imagePolicyArgs.numeric != "":
|
||||
if imagePolicyArgs.numeric != "desc" && imagePolicyArgs.numeric != "asc" {
|
||||
return fmt.Errorf("--select-numeric must be one of [\"asc\", \"desc\"]")
|
||||
}
|
||||
policy.Spec.Policy.Numerical = &imagev1.NumericalPolicy{
|
||||
Order: imagePolicyArgs.numeric,
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("a policy must be provided with --semver")
|
||||
return fmt.Errorf("a policy must be provided with either --select-semver or --select-alpha")
|
||||
}
|
||||
|
||||
if imagePolicyArgs.filterRegex != "" {
|
||||
exp, err := syntax.Parse(imagePolicyArgs.filterRegex, syntax.Perl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("--filter-regex is an invalid regex pattern")
|
||||
}
|
||||
policy.Spec.FilterTags = &imagev1.TagFilter{
|
||||
Pattern: imagePolicyArgs.filterRegex,
|
||||
}
|
||||
|
||||
if imagePolicyArgs.filterExtract != "" {
|
||||
if err := validateExtractStr(imagePolicyArgs.filterExtract, exp.CapNames()); err != nil {
|
||||
return err
|
||||
}
|
||||
policy.Spec.FilterTags.Extract = imagePolicyArgs.filterExtract
|
||||
}
|
||||
} else if imagePolicyArgs.filterExtract != "" {
|
||||
return fmt.Errorf("cannot specify --filter-extract without specifying --filter-regex")
|
||||
}
|
||||
|
||||
if createArgs.export {
|
||||
@@ -117,3 +159,94 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Performs a dry-run of the extract function in Regexp to validate the template
|
||||
func validateExtractStr(template string, capNames []string) error {
|
||||
for len(template) > 0 {
|
||||
i := strings.Index(template, "$")
|
||||
if i < 0 {
|
||||
return nil
|
||||
}
|
||||
template = template[i:]
|
||||
if len(template) > 1 && template[1] == '$' {
|
||||
template = template[2:]
|
||||
continue
|
||||
}
|
||||
name, num, rest, ok := extract(template)
|
||||
if !ok {
|
||||
// Malformed extract string, assume user didn't want this
|
||||
template = template[1:]
|
||||
return fmt.Errorf("--filter-extract is malformed")
|
||||
}
|
||||
template = rest
|
||||
if num >= 0 {
|
||||
// we won't worry about numbers as we can't validate these
|
||||
continue
|
||||
} else {
|
||||
found := false
|
||||
for _, capName := range capNames {
|
||||
if name == capName {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("capture group $%s used in --filter-extract not found in --filter-regex", name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extract method from the regexp package
|
||||
// returns the name or number of the value prepended by $
|
||||
func extract(str string) (name string, num int, rest string, ok bool) {
|
||||
if len(str) < 2 || str[0] != '$' {
|
||||
return
|
||||
}
|
||||
brace := false
|
||||
if str[1] == '{' {
|
||||
brace = true
|
||||
str = str[2:]
|
||||
} else {
|
||||
str = str[1:]
|
||||
}
|
||||
i := 0
|
||||
for i < len(str) {
|
||||
rune, size := utf8.DecodeRuneInString(str[i:])
|
||||
if !unicode.IsLetter(rune) && !unicode.IsDigit(rune) && rune != '_' {
|
||||
break
|
||||
}
|
||||
i += size
|
||||
}
|
||||
if i == 0 {
|
||||
// empty name is not okay
|
||||
return
|
||||
}
|
||||
name = str[:i]
|
||||
if brace {
|
||||
if i >= len(str) || str[i] != '}' {
|
||||
// missing closing brace
|
||||
return
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// Parse number.
|
||||
num = 0
|
||||
for i := 0; i < len(name); i++ {
|
||||
if name[i] < '0' || '9' < name[i] || num >= 1e8 {
|
||||
num = -1
|
||||
break
|
||||
}
|
||||
num = num*10 + int(name[i]) - '0'
|
||||
}
|
||||
// Disallow leading zeros.
|
||||
if name[0] == '0' && len(name) > 1 {
|
||||
num = -1
|
||||
}
|
||||
|
||||
rest = str[i:]
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -34,13 +34,39 @@ var createImageRepositoryCmd = &cobra.Command{
|
||||
Short: "Create or update an ImageRepository object",
|
||||
Long: `The create image repository command generates an ImageRepository resource.
|
||||
An ImageRepository object specifies an image repository to scan.`,
|
||||
Example: ` # Create an ImageRepository object to scan the alpine image repository:
|
||||
flux create image repository alpine-repo --image alpine --interval 20m
|
||||
|
||||
# Create an image repository that uses an image pull secret (assumed to
|
||||
# have been created already):
|
||||
flux create image repository myapp-repo \
|
||||
--secret-ref image-pull \
|
||||
--image ghcr.io/example.com/myapp --interval 5m
|
||||
|
||||
# Create a TLS secret for a local image registry using a self-signed
|
||||
# host certificate, and use it to scan an image. ca.pem is a file
|
||||
# containing the CA certificate used to sign the host certificate.
|
||||
flux create secret tls local-registry-cert --ca-file ./ca.pem
|
||||
flux create image repository app-repo \
|
||||
--cert-secret-ref local-registry-cert \
|
||||
--image local-registry:5000/app --interval 5m
|
||||
|
||||
# Create a TLS secret with a client certificate and key, and use it
|
||||
# to scan a private image registry.
|
||||
flux create secret tls client-cert \
|
||||
--cert-file client.crt --key-file client.key
|
||||
flux create image repository app-repo \
|
||||
--cert-secret-ref client-cert \
|
||||
--image registry.example.com/private/app --interval 5m
|
||||
`,
|
||||
RunE: createImageRepositoryRun,
|
||||
}
|
||||
|
||||
type imageRepoFlags struct {
|
||||
image string
|
||||
secretRef string
|
||||
timeout time.Duration
|
||||
image string
|
||||
secretRef string
|
||||
certSecretRef string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
var imageRepoArgs = imageRepoFlags{}
|
||||
@@ -49,6 +75,7 @@ func init() {
|
||||
flags := createImageRepositoryCmd.Flags()
|
||||
flags.StringVar(&imageRepoArgs.image, "image", "", "the image repository to scan; e.g., library/alpine")
|
||||
flags.StringVar(&imageRepoArgs.secretRef, "secret-ref", "", "the name of a docker-registry secret to use for credentials")
|
||||
flags.StringVar(&imageRepoArgs.certSecretRef, "cert-ref", "", "the name of a secret to use for TLS certificates")
|
||||
// NB there is already a --timeout in the global flags, for
|
||||
// controlling timeout on operations while e.g., creating objects.
|
||||
flags.DurationVar(&imageRepoArgs.timeout, "scan-timeout", 0, "a timeout for scanning; this defaults to the interval if not set")
|
||||
@@ -94,6 +121,11 @@ func createImageRepositoryRun(cmd *cobra.Command, args []string) error {
|
||||
Name: imageRepoArgs.secretRef,
|
||||
}
|
||||
}
|
||||
if imageRepoArgs.certSecretRef != "" {
|
||||
repo.Spec.CertSecretRef = &meta.LocalObjectReference{
|
||||
Name: imageRepoArgs.certSecretRef,
|
||||
}
|
||||
}
|
||||
|
||||
if createArgs.export {
|
||||
return printExport(exportImageRepository(&repo))
|
||||
|
||||
@@ -70,7 +70,7 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if imageUpdateArgs.branch == "" {
|
||||
return fmt.Errorf("the Git repoistory branch is required (--branch)")
|
||||
return fmt.Errorf("the Git repository branch is required (--branch)")
|
||||
}
|
||||
|
||||
labels, err := parseLabels()
|
||||
|
||||
@@ -39,6 +39,23 @@ func init() {
|
||||
createCmd.AddCommand(createSecretCmd)
|
||||
}
|
||||
|
||||
func makeSecret(name string) (corev1.Secret, error) {
|
||||
secretLabels, err := parseLabels()
|
||||
if err != nil {
|
||||
return corev1.Secret{}, err
|
||||
}
|
||||
|
||||
return corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: rootArgs.namespace,
|
||||
Labels: secretLabels,
|
||||
},
|
||||
StringData: map[string]string{},
|
||||
Data: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.Secret) error {
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: secret.GetNamespace(),
|
||||
|
||||
@@ -20,12 +20,11 @@ import (
|
||||
"context"
|
||||
"crypto/elliptic"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
@@ -78,6 +77,7 @@ type secretGitFlags struct {
|
||||
keyAlgorithm flags.PublicKeyAlgorithm
|
||||
rsaBits flags.RSAKeyBits
|
||||
ecdsaCurve flags.ECDSACurve
|
||||
caFile string
|
||||
}
|
||||
|
||||
var secretGitArgs = NewSecretGitFlags()
|
||||
@@ -89,6 +89,7 @@ func init() {
|
||||
createSecretGitCmd.Flags().Var(&secretGitArgs.keyAlgorithm, "ssh-key-algorithm", secretGitArgs.keyAlgorithm.Description())
|
||||
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
|
||||
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
|
||||
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
||||
|
||||
createSecretCmd.AddCommand(createSecretGitCmd)
|
||||
}
|
||||
@@ -106,6 +107,10 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("secret name is required")
|
||||
}
|
||||
name := args[0]
|
||||
secret, err := makeSecret(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if secretGitArgs.url == "" {
|
||||
return fmt.Errorf("url is required")
|
||||
@@ -116,22 +121,9 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("git URL parse failed: %w", err)
|
||||
}
|
||||
|
||||
secretLabels, err := parseLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
secret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: rootArgs.namespace,
|
||||
Labels: secretLabels,
|
||||
},
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "ssh":
|
||||
pair, err := generateKeyPair(ctx, secretGitArgs.keyAlgorithm, secretGitArgs.rsaBits, secretGitArgs.ecdsaCurve)
|
||||
@@ -144,10 +136,10 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
secret.Data = map[string][]byte{
|
||||
"identity": pair.PrivateKey,
|
||||
"identity.pub": pair.PublicKey,
|
||||
"known_hosts": hostKey,
|
||||
secret.StringData = map[string]string{
|
||||
"identity": string(pair.PrivateKey),
|
||||
"identity.pub": string(pair.PublicKey),
|
||||
"known_hosts": string(hostKey),
|
||||
}
|
||||
|
||||
if !createArgs.export {
|
||||
@@ -158,11 +150,19 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
||||
}
|
||||
|
||||
// TODO: add cert data when it's implemented in source-controller
|
||||
secret.Data = map[string][]byte{
|
||||
"username": []byte(secretGitArgs.username),
|
||||
"password": []byte(secretGitArgs.password),
|
||||
secret.StringData = map[string]string{
|
||||
"username": secretGitArgs.username,
|
||||
"password": secretGitArgs.password,
|
||||
}
|
||||
|
||||
if secretGitArgs.caFile != "" {
|
||||
ca, err := ioutil.ReadFile(secretGitArgs.caFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read CA file '%s': %w", secretGitArgs.caFile, err)
|
||||
}
|
||||
secret.StringData["caFile"] = string(ca)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
||||
}
|
||||
|
||||
@@ -19,11 +19,8 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
@@ -58,9 +55,7 @@ The create secret helm command generates a Kubernetes secret with basic authenti
|
||||
type secretHelmFlags struct {
|
||||
username string
|
||||
password string
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
secretTLSFlags
|
||||
}
|
||||
|
||||
var secretHelmArgs secretHelmFlags
|
||||
@@ -68,10 +63,7 @@ var secretHelmArgs secretHelmFlags
|
||||
func init() {
|
||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.username, "username", "u", "", "basic authentication username")
|
||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.password, "password", "p", "", "basic authentication password")
|
||||
createSecretHelmCmd.Flags().StringVar(&secretHelmArgs.certFile, "cert-file", "", "TLS authentication cert file path")
|
||||
createSecretHelmCmd.Flags().StringVar(&secretHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
|
||||
createSecretHelmCmd.Flags().StringVar(&secretHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
|
||||
|
||||
initSecretTLSFlags(createSecretHelmCmd.Flags(), &secretHelmArgs.secretTLSFlags)
|
||||
createSecretCmd.AddCommand(createSecretHelmCmd)
|
||||
}
|
||||
|
||||
@@ -80,46 +72,18 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("secret name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
secretLabels, err := parseLabels()
|
||||
secret, err := makeSecret(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: rootArgs.namespace,
|
||||
Labels: secretLabels,
|
||||
},
|
||||
StringData: map[string]string{},
|
||||
}
|
||||
|
||||
if secretHelmArgs.username != "" && secretHelmArgs.password != "" {
|
||||
secret.StringData["username"] = secretHelmArgs.username
|
||||
secret.StringData["password"] = secretHelmArgs.password
|
||||
}
|
||||
|
||||
if secretHelmArgs.certFile != "" && secretHelmArgs.keyFile != "" {
|
||||
cert, err := ioutil.ReadFile(secretHelmArgs.certFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository cert file '%s': %w", secretHelmArgs.certFile, err)
|
||||
}
|
||||
secret.StringData["certFile"] = string(cert)
|
||||
|
||||
key, err := ioutil.ReadFile(secretHelmArgs.keyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository key file '%s': %w", secretHelmArgs.keyFile, err)
|
||||
}
|
||||
secret.StringData["keyFile"] = string(key)
|
||||
}
|
||||
|
||||
if secretHelmArgs.caFile != "" {
|
||||
ca, err := ioutil.ReadFile(secretHelmArgs.caFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository CA file '%s': %w", secretHelmArgs.caFile, err)
|
||||
}
|
||||
secret.StringData["caFile"] = string(ca)
|
||||
if err = populateSecretTLS(&secret, secretHelmArgs.secretTLSFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if createArgs.export {
|
||||
|
||||
128
cmd/flux/create_secret_tls.go
Normal file
128
cmd/flux/create_secret_tls.go
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright 2020, 2021 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var createSecretTLSCmd = &cobra.Command{
|
||||
Use: "tls [name]",
|
||||
Short: "Create or update a Kubernetes secret with TLS certificates",
|
||||
Long: `
|
||||
The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`,
|
||||
Example: `
|
||||
# Create a TLS secret on disk and encrypt it with Mozilla SOPS.
|
||||
# Files are expected to be PEM-encoded.
|
||||
flux create secret tls certs \
|
||||
--namespace=my-namespace \
|
||||
--cert-file=./client.crt \
|
||||
--key-file=./client.key \
|
||||
--export > certs.yaml
|
||||
|
||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||
--in-place certs.yaml
|
||||
`,
|
||||
RunE: createSecretTLSCmdRun,
|
||||
}
|
||||
|
||||
type secretTLSFlags struct {
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
}
|
||||
|
||||
var secretTLSArgs secretTLSFlags
|
||||
|
||||
func initSecretTLSFlags(flags *pflag.FlagSet, args *secretTLSFlags) {
|
||||
flags.StringVar(&args.certFile, "cert-file", "", "TLS authentication cert file path")
|
||||
flags.StringVar(&args.keyFile, "key-file", "", "TLS authentication key file path")
|
||||
flags.StringVar(&args.caFile, "ca-file", "", "TLS authentication CA file path")
|
||||
}
|
||||
|
||||
func init() {
|
||||
flags := createSecretTLSCmd.Flags()
|
||||
initSecretTLSFlags(flags, &secretTLSArgs)
|
||||
createSecretCmd.AddCommand(createSecretTLSCmd)
|
||||
}
|
||||
|
||||
func populateSecretTLS(secret *corev1.Secret, args secretTLSFlags) error {
|
||||
if args.certFile != "" && args.keyFile != "" {
|
||||
cert, err := ioutil.ReadFile(args.certFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository cert file '%s': %w", args.certFile, err)
|
||||
}
|
||||
secret.StringData["certFile"] = string(cert)
|
||||
|
||||
key, err := ioutil.ReadFile(args.keyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository key file '%s': %w", args.keyFile, err)
|
||||
}
|
||||
secret.StringData["keyFile"] = string(key)
|
||||
}
|
||||
|
||||
if args.caFile != "" {
|
||||
ca, err := ioutil.ReadFile(args.caFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository CA file '%s': %w", args.caFile, err)
|
||||
}
|
||||
secret.StringData["caFile"] = string(ca)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("secret name is required")
|
||||
}
|
||||
name := args[0]
|
||||
secret, err := makeSecret(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = populateSecretTLS(&secret, secretTLSArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if createArgs.export {
|
||||
return exportSecret(secret)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -41,19 +41,19 @@ import (
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
type SourceGitFlags struct {
|
||||
GitURL string
|
||||
GitBranch string
|
||||
GitTag string
|
||||
GitSemver string
|
||||
GitUsername string
|
||||
GitPassword string
|
||||
|
||||
GitKeyAlgorithm flags.PublicKeyAlgorithm
|
||||
GitRSABits flags.RSAKeyBits
|
||||
GitECDSACurve flags.ECDSACurve
|
||||
GitSecretRef string
|
||||
GitImplementation flags.GitImplementation
|
||||
type sourceGitFlags struct {
|
||||
url string
|
||||
branch string
|
||||
tag string
|
||||
semver string
|
||||
username string
|
||||
password string
|
||||
caFile string
|
||||
keyAlgorithm flags.PublicKeyAlgorithm
|
||||
keyRSABits flags.RSAKeyBits
|
||||
keyECDSACurve flags.ECDSACurve
|
||||
secretRef string
|
||||
gitImplementation flags.GitImplementation
|
||||
}
|
||||
|
||||
var createSourceGitCmd = &cobra.Command{
|
||||
@@ -100,29 +100,30 @@ For private Git repositories, the basic authentication credentials are stored in
|
||||
RunE: createSourceGitCmdRun,
|
||||
}
|
||||
|
||||
var sourceArgs = NewSourceGitFlags()
|
||||
var sourceGitArgs = newSourceGitFlags()
|
||||
|
||||
func init() {
|
||||
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitURL, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
||||
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitBranch, "branch", "master", "git branch")
|
||||
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitTag, "tag", "", "git tag")
|
||||
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitSemver, "tag-semver", "", "git tag semver range")
|
||||
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitUsername, "username", "u", "", "basic authentication username")
|
||||
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitPassword, "password", "p", "", "basic authentication password")
|
||||
createSourceGitCmd.Flags().Var(&sourceArgs.GitKeyAlgorithm, "ssh-key-algorithm", sourceArgs.GitKeyAlgorithm.Description())
|
||||
createSourceGitCmd.Flags().Var(&sourceArgs.GitRSABits, "ssh-rsa-bits", sourceArgs.GitRSABits.Description())
|
||||
createSourceGitCmd.Flags().Var(&sourceArgs.GitECDSACurve, "ssh-ecdsa-curve", sourceArgs.GitECDSACurve.Description())
|
||||
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitSecretRef, "secret-ref", "", "", "the name of an existing secret containing SSH or basic credentials")
|
||||
createSourceGitCmd.Flags().Var(&sourceArgs.GitImplementation, "git-implementation", sourceArgs.GitImplementation.Description())
|
||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.url, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.branch, "branch", "master", "git branch")
|
||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.tag, "tag", "", "git tag")
|
||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.semver, "tag-semver", "", "git tag semver range")
|
||||
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.username, "username", "u", "", "basic authentication username")
|
||||
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.password, "password", "p", "", "basic authentication password")
|
||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyAlgorithm, "ssh-key-algorithm", sourceGitArgs.keyAlgorithm.Description())
|
||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyRSABits, "ssh-rsa-bits", sourceGitArgs.keyRSABits.Description())
|
||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyECDSACurve, "ssh-ecdsa-curve", sourceGitArgs.keyECDSACurve.Description())
|
||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials")
|
||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.gitImplementation, "git-implementation", sourceGitArgs.gitImplementation.Description())
|
||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates, requires libgit2")
|
||||
|
||||
createSourceCmd.AddCommand(createSourceGitCmd)
|
||||
}
|
||||
|
||||
func NewSourceGitFlags() SourceGitFlags {
|
||||
return SourceGitFlags{
|
||||
GitKeyAlgorithm: "rsa",
|
||||
GitRSABits: 2048,
|
||||
GitECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||
func newSourceGitFlags() sourceGitFlags {
|
||||
return sourceGitFlags{
|
||||
keyAlgorithm: "rsa",
|
||||
keyRSABits: 2048,
|
||||
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,17 +133,21 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
if sourceArgs.GitURL == "" {
|
||||
if sourceGitArgs.url == "" {
|
||||
return fmt.Errorf("url is required")
|
||||
}
|
||||
|
||||
if sourceGitArgs.gitImplementation.String() != sourcev1.LibGit2Implementation && sourceGitArgs.caFile != "" {
|
||||
return fmt.Errorf("specifing a CA file requires --git-implementation=%s", sourcev1.LibGit2Implementation)
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
u, err := url.Parse(sourceArgs.GitURL)
|
||||
u, err := url.Parse(sourceGitArgs.url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("git URL parse failed: %w", err)
|
||||
}
|
||||
@@ -159,7 +164,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
Labels: sourceLabels,
|
||||
},
|
||||
Spec: sourcev1.GitRepositorySpec{
|
||||
URL: sourceArgs.GitURL,
|
||||
URL: sourceGitArgs.url,
|
||||
Interval: metav1.Duration{
|
||||
Duration: createArgs.interval,
|
||||
},
|
||||
@@ -167,22 +172,22 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
},
|
||||
}
|
||||
|
||||
if sourceArgs.GitImplementation != "" {
|
||||
gitRepository.Spec.GitImplementation = sourceArgs.GitImplementation.String()
|
||||
if sourceGitArgs.gitImplementation != "" {
|
||||
gitRepository.Spec.GitImplementation = sourceGitArgs.gitImplementation.String()
|
||||
}
|
||||
|
||||
if sourceArgs.GitSemver != "" {
|
||||
gitRepository.Spec.Reference.SemVer = sourceArgs.GitSemver
|
||||
} else if sourceArgs.GitTag != "" {
|
||||
gitRepository.Spec.Reference.Tag = sourceArgs.GitTag
|
||||
if sourceGitArgs.semver != "" {
|
||||
gitRepository.Spec.Reference.SemVer = sourceGitArgs.semver
|
||||
} else if sourceGitArgs.tag != "" {
|
||||
gitRepository.Spec.Reference.Tag = sourceGitArgs.tag
|
||||
} else {
|
||||
gitRepository.Spec.Reference.Branch = sourceArgs.GitBranch
|
||||
gitRepository.Spec.Reference.Branch = sourceGitArgs.branch
|
||||
}
|
||||
|
||||
if createArgs.export {
|
||||
if sourceArgs.GitSecretRef != "" {
|
||||
if sourceGitArgs.secretRef != "" {
|
||||
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||
Name: sourceArgs.GitSecretRef,
|
||||
Name: sourceGitArgs.secretRef,
|
||||
}
|
||||
}
|
||||
return exportGit(gitRepository)
|
||||
@@ -198,11 +203,11 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
withAuth := false
|
||||
// TODO(hidde): move all auth prep to separate func?
|
||||
if sourceArgs.GitSecretRef != "" {
|
||||
if sourceGitArgs.secretRef != "" {
|
||||
withAuth = true
|
||||
} else if u.Scheme == "ssh" {
|
||||
logger.Generatef("generating deploy key pair")
|
||||
pair, err := generateKeyPair(ctx, sourceArgs.GitKeyAlgorithm, sourceArgs.GitRSABits, sourceArgs.GitECDSACurve)
|
||||
pair, err := generateKeyPair(ctx, sourceGitArgs.keyAlgorithm, sourceGitArgs.keyRSABits, sourceGitArgs.keyECDSACurve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -240,7 +245,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
withAuth = true
|
||||
} else if sourceArgs.GitUsername != "" && sourceArgs.GitPassword != "" {
|
||||
} else if sourceGitArgs.username != "" && sourceGitArgs.password != "" {
|
||||
logger.Actionf("applying secret with basic auth credentials")
|
||||
secret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -249,10 +254,19 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
Labels: sourceLabels,
|
||||
},
|
||||
StringData: map[string]string{
|
||||
"username": sourceArgs.GitUsername,
|
||||
"password": sourceArgs.GitPassword,
|
||||
"username": sourceGitArgs.username,
|
||||
"password": sourceGitArgs.password,
|
||||
},
|
||||
}
|
||||
|
||||
if sourceGitArgs.caFile != "" {
|
||||
ca, err := ioutil.ReadFile(sourceGitArgs.caFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read CA file '%s': %w", sourceGitArgs.caFile, err)
|
||||
}
|
||||
secret.StringData["caFile"] = string(ca)
|
||||
}
|
||||
|
||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -267,8 +281,8 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if withAuth {
|
||||
secretName := name
|
||||
if sourceArgs.GitSecretRef != "" {
|
||||
secretName = sourceArgs.GitSecretRef
|
||||
if sourceGitArgs.secretRef != "" {
|
||||
secretName = sourceGitArgs.secretRef
|
||||
}
|
||||
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||
Name: secretName,
|
||||
|
||||
@@ -17,15 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var deleteHelmReleaseCmd = &cobra.Command{
|
||||
@@ -36,57 +29,12 @@ var deleteHelmReleaseCmd = &cobra.Command{
|
||||
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
||||
flux delete hr podinfo
|
||||
`,
|
||||
RunE: deleteHelmReleaseCmdRun,
|
||||
RunE: deleteCommand{
|
||||
apiType: helmReleaseType,
|
||||
object: universalAdapter{&helmv2.HelmRelease{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteCmd.AddCommand(deleteHelmReleaseCmd)
|
||||
}
|
||||
|
||||
func deleteHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("release name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
var helmRelease helmv2.HelmRelease
|
||||
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !deleteArgs.silent {
|
||||
if !helmRelease.Spec.Suspend {
|
||||
logger.Waitingf("This action will remove the Kubernetes objects previously applied by the %s Helm release!", name)
|
||||
}
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Are you sure you want to delete this Helm release",
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err != nil {
|
||||
return fmt.Errorf("aborting")
|
||||
}
|
||||
}
|
||||
|
||||
logger.Actionf("deleting release %s in %s namespace", name, rootArgs.namespace)
|
||||
err = kubeClient.Delete(ctx, &helmRelease)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("release deleted")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var deleteAutoCmd = &cobra.Command{
|
||||
Use: "auto",
|
||||
Short: "Delete automation objects",
|
||||
Long: "The delete auto sub-commands delete automation objects.",
|
||||
var deleteImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Delete image automation objects",
|
||||
Long: "The delete image sub-commands delete image automation objects.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteCmd.AddCommand(deleteAutoCmd)
|
||||
deleteCmd.AddCommand(deleteImageCmd)
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ import (
|
||||
)
|
||||
|
||||
var deleteImagePolicyCmd = &cobra.Command{
|
||||
Use: "image-policy [name]",
|
||||
Use: "policy [name]",
|
||||
Short: "Delete an ImagePolicy object",
|
||||
Long: "The delete auto image-policy command deletes the given ImagePolicy from the cluster.",
|
||||
Long: "The delete image policy command deletes the given ImagePolicy from the cluster.",
|
||||
Example: ` # Delete an image policy
|
||||
flux delete auto image-policy alpine3.x
|
||||
flux delete image policy alpine3.x
|
||||
`,
|
||||
RunE: deleteCommand{
|
||||
apiType: imagePolicyType,
|
||||
@@ -36,5 +36,5 @@ var deleteImagePolicyCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteAutoCmd.AddCommand(deleteImagePolicyCmd)
|
||||
deleteImageCmd.AddCommand(deleteImagePolicyCmd)
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ import (
|
||||
)
|
||||
|
||||
var deleteImageRepositoryCmd = &cobra.Command{
|
||||
Use: "image-repository [name]",
|
||||
Use: "repository [name]",
|
||||
Short: "Delete an ImageRepository object",
|
||||
Long: "The delete auto image-repository command deletes the given ImageRepository from the cluster.",
|
||||
Long: "The delete image repository command deletes the given ImageRepository from the cluster.",
|
||||
Example: ` # Delete an image repository
|
||||
flux delete auto image-repository alpine
|
||||
flux delete image repository alpine
|
||||
`,
|
||||
RunE: deleteCommand{
|
||||
apiType: imageRepositoryType,
|
||||
@@ -36,5 +36,5 @@ var deleteImageRepositoryCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteAutoCmd.AddCommand(deleteImageRepositoryCmd)
|
||||
deleteImageCmd.AddCommand(deleteImageRepositoryCmd)
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ import (
|
||||
)
|
||||
|
||||
var deleteImageUpdateCmd = &cobra.Command{
|
||||
Use: "image-update [name]",
|
||||
Use: "update [name]",
|
||||
Short: "Delete an ImageUpdateAutomation object",
|
||||
Long: "The delete auto image-update command deletes the given ImageUpdateAutomation from the cluster.",
|
||||
Long: "The delete image update command deletes the given ImageUpdateAutomation from the cluster.",
|
||||
Example: ` # Delete an image update automation
|
||||
flux delete auto image-update latest-images
|
||||
flux delete image update latest-images
|
||||
`,
|
||||
RunE: deleteCommand{
|
||||
apiType: imageUpdateAutomationType,
|
||||
@@ -36,5 +36,5 @@ var deleteImageUpdateCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteAutoCmd.AddCommand(deleteImageUpdateCmd)
|
||||
deleteImageCmd.AddCommand(deleteImageUpdateCmd)
|
||||
}
|
||||
|
||||
@@ -17,14 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var deleteKsCmd = &cobra.Command{
|
||||
@@ -35,57 +29,12 @@ var deleteKsCmd = &cobra.Command{
|
||||
Example: ` # Delete a kustomization and the Kubernetes resources created by it
|
||||
flux delete kustomization podinfo
|
||||
`,
|
||||
RunE: deleteKsCmdRun,
|
||||
RunE: deleteCommand{
|
||||
apiType: kustomizationType,
|
||||
object: universalAdapter{&kustomizev1.Kustomization{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteCmd.AddCommand(deleteKsCmd)
|
||||
}
|
||||
|
||||
func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("kustomization name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
var kustomization kustomizev1.Kustomization
|
||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !deleteArgs.silent {
|
||||
if !kustomization.Spec.Suspend {
|
||||
logger.Waitingf("This action will remove the Kubernetes objects previously applied by the %s kustomization!", name)
|
||||
}
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Are you sure you want to delete this kustomization",
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err != nil {
|
||||
return fmt.Errorf("aborting")
|
||||
}
|
||||
}
|
||||
|
||||
logger.Actionf("deleting kustomization %s in %s namespace", name, rootArgs.namespace)
|
||||
err = kubeClient.Delete(ctx, &kustomization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("kustomization deleted")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,14 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var deleteSourceBucketCmd = &cobra.Command{
|
||||
@@ -34,54 +28,12 @@ var deleteSourceBucketCmd = &cobra.Command{
|
||||
Example: ` # Delete a Bucket source
|
||||
flux delete source bucket podinfo
|
||||
`,
|
||||
RunE: deleteSourceBucketCmdRun,
|
||||
RunE: deleteCommand{
|
||||
apiType: bucketType,
|
||||
object: universalAdapter{&sourcev1.Bucket{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteSourceCmd.AddCommand(deleteSourceBucketCmd)
|
||||
}
|
||||
|
||||
func deleteSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
var bucket sourcev1.Bucket
|
||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !deleteArgs.silent {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Are you sure you want to delete this source",
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err != nil {
|
||||
return fmt.Errorf("aborting")
|
||||
}
|
||||
}
|
||||
|
||||
logger.Actionf("deleting source %s in %s namespace", name, rootArgs.namespace)
|
||||
err = kubeClient.Delete(ctx, &bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source deleted")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,14 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var deleteSourceGitCmd = &cobra.Command{
|
||||
@@ -34,54 +28,12 @@ var deleteSourceGitCmd = &cobra.Command{
|
||||
Example: ` # Delete a Git repository
|
||||
flux delete source git podinfo
|
||||
`,
|
||||
RunE: deleteSourceGitCmdRun,
|
||||
RunE: deleteCommand{
|
||||
apiType: gitRepositoryType,
|
||||
object: universalAdapter{&sourcev1.GitRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteSourceCmd.AddCommand(deleteSourceGitCmd)
|
||||
}
|
||||
|
||||
func deleteSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("git name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
var git sourcev1.GitRepository
|
||||
err = kubeClient.Get(ctx, namespacedName, &git)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !deleteArgs.silent {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Are you sure you want to delete this source",
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err != nil {
|
||||
return fmt.Errorf("aborting")
|
||||
}
|
||||
}
|
||||
|
||||
logger.Actionf("deleting source %s in %s namespace", name, rootArgs.namespace)
|
||||
err = kubeClient.Delete(ctx, &git)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source deleted")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -34,7 +34,10 @@ var deleteSourceHelmCmd = &cobra.Command{
|
||||
Example: ` # Delete a Helm repository
|
||||
flux delete source helm podinfo
|
||||
`,
|
||||
RunE: deleteSourceHelmCmdRun,
|
||||
RunE: deleteCommand{
|
||||
apiType: helmRepositoryType,
|
||||
object: universalAdapter{&sourcev1.HelmRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
31
cmd/flux/embed.go
Normal file
31
cmd/flux/embed.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
//go:embed manifests/*.yaml
|
||||
var embeddedManifests embed.FS
|
||||
|
||||
func writeEmbeddedManifests(dir string) error {
|
||||
manifests, err := fs.ReadDir(embeddedManifests, "manifests")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, manifest := range manifests {
|
||||
data, err := fs.ReadFile(embeddedManifests, path.Join("manifests", manifest.Name()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading file failed: %w", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(path.Join(dir, manifest.Name()), data, 0666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing file failed: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -90,6 +90,11 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
||||
if !getArgs.allNamespaces {
|
||||
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
listOpts = append(listOpts, client.MatchingFields{"metadata.name": args[0]})
|
||||
}
|
||||
|
||||
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -33,9 +33,10 @@ import (
|
||||
)
|
||||
|
||||
var getAlertCmd = &cobra.Command{
|
||||
Use: "alerts",
|
||||
Short: "Get Alert statuses",
|
||||
Long: "The get alert command prints the statuses of the resources.",
|
||||
Use: "alerts",
|
||||
Aliases: []string{"alert"},
|
||||
Short: "Get Alert statuses",
|
||||
Long: "The get alert command prints the statuses of the resources.",
|
||||
Example: ` # List all Alerts and their status
|
||||
flux get alerts
|
||||
`,
|
||||
|
||||
@@ -31,9 +31,10 @@ import (
|
||||
)
|
||||
|
||||
var getAlertProviderCmd = &cobra.Command{
|
||||
Use: "alert-providers",
|
||||
Short: "Get Provider statuses",
|
||||
Long: "The get alert-provider command prints the statuses of the resources.",
|
||||
Use: "alert-providers",
|
||||
Aliases: []string{"alert-provider"},
|
||||
Short: "Get Provider statuses",
|
||||
Long: "The get alert-provider command prints the statuses of the resources.",
|
||||
Example: ` # List all Providers and their status
|
||||
flux get alert-providers
|
||||
`,
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
var getHelmReleaseCmd = &cobra.Command{
|
||||
Use: "helmreleases",
|
||||
Aliases: []string{"hr"},
|
||||
Aliases: []string{"hr", "helmrelease"},
|
||||
Short: "Get HelmRelease statuses",
|
||||
Long: "The get helmreleases command prints the statuses of the resources.",
|
||||
Example: ` # List all Helm releases and their status
|
||||
|
||||
@@ -21,9 +21,10 @@ import (
|
||||
)
|
||||
|
||||
var getImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Get image automation object status",
|
||||
Long: "The get image sub-commands print the status of image automation objects.",
|
||||
Use: "images",
|
||||
Aliases: []string{"image"},
|
||||
Short: "Get image automation object status",
|
||||
Long: "The get image sub-commands print the status of image automation objects.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
var getKsCmd = &cobra.Command{
|
||||
Use: "kustomizations",
|
||||
Aliases: []string{"ks"},
|
||||
Aliases: []string{"ks", "kustomization"},
|
||||
Short: "Get Kustomization statuses",
|
||||
Long: "The get kustomizations command prints the statuses of the resources.",
|
||||
Example: ` # List all kustomizations and their status
|
||||
|
||||
@@ -33,9 +33,10 @@ import (
|
||||
)
|
||||
|
||||
var getReceiverCmd = &cobra.Command{
|
||||
Use: "receivers",
|
||||
Short: "Get Receiver statuses",
|
||||
Long: "The get receiver command prints the statuses of the resources.",
|
||||
Use: "receivers",
|
||||
Aliases: []string{"receiver"},
|
||||
Short: "Get Receiver statuses",
|
||||
Long: "The get receiver command prints the statuses of the resources.",
|
||||
Example: ` # List all Receiver and their status
|
||||
flux get receivers
|
||||
`,
|
||||
|
||||
@@ -21,9 +21,10 @@ import (
|
||||
)
|
||||
|
||||
var getSourceCmd = &cobra.Command{
|
||||
Use: "sources",
|
||||
Short: "Get source statuses",
|
||||
Long: "The get source sub-commands print the statuses of the sources.",
|
||||
Use: "sources",
|
||||
Aliases: []string{"source"},
|
||||
Short: "Get source statuses",
|
||||
Long: "The get source sub-commands print the statuses of the sources.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -35,7 +35,7 @@ var getSourceHelmChartCmd = &cobra.Command{
|
||||
flux get sources chart --all-namespaces
|
||||
`,
|
||||
RunE: getCommand{
|
||||
apiType: bucketType,
|
||||
apiType: helmChartType,
|
||||
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ var getSourceGitCmd = &cobra.Command{
|
||||
flux get sources git --all-namespaces
|
||||
`,
|
||||
RunE: getCommand{
|
||||
apiType: bucketType,
|
||||
apiType: gitRepositoryType,
|
||||
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ var getSourceHelmCmd = &cobra.Command{
|
||||
flux get sources helm --all-namespaces
|
||||
`,
|
||||
RunE: getCommand{
|
||||
apiType: bucketType,
|
||||
apiType: helmRepositoryType,
|
||||
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -33,15 +34,18 @@ import (
|
||||
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Install the toolkit components",
|
||||
Long: `The install command deploys the toolkit components in the specified namespace.
|
||||
Short: "Install or upgrade Flux",
|
||||
Long: `The install command deploys Flux in the specified namespace.
|
||||
If a previous version is installed, then an in-place upgrade will be performed.`,
|
||||
Example: ` # Install the latest version in the flux-system namespace
|
||||
flux install --version=latest --namespace=flux-system
|
||||
|
||||
# Dry-run install for a specific version and a series of components
|
||||
# Install a specific version and a series of components
|
||||
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
|
||||
|
||||
# Install Flux onto tainted Kubernetes nodes
|
||||
flux install --toleration-keys=node.kubernetes.io/dedicated-to-flux
|
||||
|
||||
# Dry-run install with manifests preview
|
||||
flux install --dry-run --verbose
|
||||
|
||||
@@ -51,91 +55,119 @@ If a previous version is installed, then an in-place upgrade will be performed.`
|
||||
RunE: installCmdRun,
|
||||
}
|
||||
|
||||
var (
|
||||
installExport bool
|
||||
installDryRun bool
|
||||
installManifestsPath string
|
||||
installVersion string
|
||||
installDefaultComponents []string
|
||||
installExtraComponents []string
|
||||
installRegistry string
|
||||
installImagePullSecret string
|
||||
installWatchAllNamespaces bool
|
||||
installNetworkPolicy bool
|
||||
installArch flags.Arch
|
||||
installLogLevel = flags.LogLevel(rootArgs.defaults.LogLevel)
|
||||
installClusterDomain string
|
||||
)
|
||||
type installFlags struct {
|
||||
export bool
|
||||
dryRun bool
|
||||
version string
|
||||
defaultComponents []string
|
||||
extraComponents []string
|
||||
registry string
|
||||
imagePullSecret string
|
||||
branch string
|
||||
watchAllNamespaces bool
|
||||
networkPolicy bool
|
||||
manifestsPath string
|
||||
arch flags.Arch
|
||||
logLevel flags.LogLevel
|
||||
tokenAuth bool
|
||||
clusterDomain string
|
||||
tolerationKeys []string
|
||||
}
|
||||
|
||||
var installArgs = NewInstallFlags()
|
||||
|
||||
func init() {
|
||||
installCmd.Flags().BoolVar(&installExport, "export", false,
|
||||
installCmd.Flags().BoolVar(&installArgs.export, "export", false,
|
||||
"write the install manifests to stdout and exit")
|
||||
installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false,
|
||||
installCmd.Flags().BoolVarP(&installArgs.dryRun, "dry-run", "", false,
|
||||
"only print the object that would be applied")
|
||||
installCmd.Flags().StringVarP(&installVersion, "version", "v", rootArgs.defaults.Version,
|
||||
"toolkit version")
|
||||
installCmd.Flags().StringSliceVar(&installDefaultComponents, "components", rootArgs.defaults.Components,
|
||||
installCmd.Flags().StringVarP(&installArgs.version, "version", "v", "",
|
||||
"toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
|
||||
installCmd.Flags().StringSliceVar(&installArgs.defaultComponents, "components", rootArgs.defaults.Components,
|
||||
"list of components, accepts comma-separated values")
|
||||
installCmd.Flags().StringSliceVar(&installExtraComponents, "components-extra", nil,
|
||||
installCmd.Flags().StringSliceVar(&installArgs.extraComponents, "components-extra", nil,
|
||||
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
||||
installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory")
|
||||
installCmd.Flags().StringVar(&installRegistry, "registry", rootArgs.defaults.Registry,
|
||||
installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
||||
installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry,
|
||||
"container registry where the toolkit images are published")
|
||||
installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "",
|
||||
installCmd.Flags().StringVar(&installArgs.imagePullSecret, "image-pull-secret", "",
|
||||
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
||||
installCmd.Flags().Var(&installArch, "arch", installArch.Description())
|
||||
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces,
|
||||
installCmd.Flags().Var(&installArgs.arch, "arch", installArgs.arch.Description())
|
||||
installCmd.Flags().BoolVar(&installArgs.watchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces,
|
||||
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
|
||||
installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description())
|
||||
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy,
|
||||
installCmd.Flags().Var(&installArgs.logLevel, "log-level", installArgs.logLevel.Description())
|
||||
installCmd.Flags().BoolVar(&installArgs.networkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy,
|
||||
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
||||
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||
installCmd.Flags().StringVar(&installArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||
installCmd.Flags().StringSliceVar(&installArgs.tolerationKeys, "toleration-keys", nil,
|
||||
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
|
||||
installCmd.Flags().MarkHidden("manifests")
|
||||
installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
func NewInstallFlags() installFlags {
|
||||
return installFlags{
|
||||
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
|
||||
}
|
||||
}
|
||||
|
||||
func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
components := append(installArgs.defaultComponents, installArgs.extraComponents...)
|
||||
err := utils.ValidateComponents(components)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ver, err := getVersion(installArgs.version); err != nil {
|
||||
return err
|
||||
} else {
|
||||
installArgs.version = ver
|
||||
}
|
||||
|
||||
if !installArgs.export {
|
||||
logger.Generatef("generating manifests")
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
if !installExport {
|
||||
logger.Generatef("generating manifests")
|
||||
}
|
||||
|
||||
components := append(installDefaultComponents, installExtraComponents...)
|
||||
|
||||
if err := utils.ValidateComponents(components); err != nil {
|
||||
return err
|
||||
manifestsBase := ""
|
||||
if isEmbeddedVersion(installArgs.version) {
|
||||
if err := writeEmbeddedManifests(tmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
manifestsBase = tmpDir
|
||||
}
|
||||
|
||||
opts := install.Options{
|
||||
BaseURL: installManifestsPath,
|
||||
Version: installVersion,
|
||||
BaseURL: installArgs.manifestsPath,
|
||||
Version: installArgs.version,
|
||||
Namespace: rootArgs.namespace,
|
||||
Components: components,
|
||||
Registry: installRegistry,
|
||||
ImagePullSecret: installImagePullSecret,
|
||||
WatchAllNamespaces: installWatchAllNamespaces,
|
||||
NetworkPolicy: installNetworkPolicy,
|
||||
LogLevel: installLogLevel.String(),
|
||||
Registry: installArgs.registry,
|
||||
ImagePullSecret: installArgs.imagePullSecret,
|
||||
WatchAllNamespaces: installArgs.watchAllNamespaces,
|
||||
NetworkPolicy: installArgs.networkPolicy,
|
||||
LogLevel: installArgs.logLevel.String(),
|
||||
NotificationController: rootArgs.defaults.NotificationController,
|
||||
ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace),
|
||||
Timeout: rootArgs.timeout,
|
||||
ClusterDomain: installClusterDomain,
|
||||
ClusterDomain: installArgs.clusterDomain,
|
||||
TolerationKeys: installArgs.tolerationKeys,
|
||||
}
|
||||
|
||||
if installManifestsPath == "" {
|
||||
if installArgs.manifestsPath == "" {
|
||||
opts.BaseURL = install.MakeDefaultOptions().BaseURL
|
||||
}
|
||||
|
||||
manifest, err := install.Generate(opts)
|
||||
manifest, err := install.Generate(opts, manifestsBase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
}
|
||||
@@ -146,9 +178,9 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if rootArgs.verbose {
|
||||
fmt.Print(manifest.Content)
|
||||
} else if installExport {
|
||||
} else if installArgs.export {
|
||||
fmt.Println("---")
|
||||
fmt.Println("# GitOps Toolkit revision", installVersion)
|
||||
fmt.Println("# Flux version:", installArgs.version)
|
||||
fmt.Println("# Components:", strings.Join(components, ","))
|
||||
fmt.Print(manifest.Content)
|
||||
fmt.Println("---")
|
||||
@@ -163,7 +195,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
kubectlArgs := []string{"apply", "-f", filepath.Join(tmpDir, manifest.Path)}
|
||||
if installDryRun {
|
||||
if installArgs.dryRun {
|
||||
kubectlArgs = append(kubectlArgs, "--dry-run=client")
|
||||
applyOutput = utils.ModeOS
|
||||
}
|
||||
@@ -171,21 +203,19 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("install failed")
|
||||
}
|
||||
|
||||
if installDryRun {
|
||||
if installArgs.dryRun {
|
||||
logger.Successf("install dry-run finished")
|
||||
return nil
|
||||
} else {
|
||||
logger.Successf("install completed")
|
||||
}
|
||||
|
||||
statusChecker, err := NewStatusChecker(time.Second, time.Minute)
|
||||
if err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
}
|
||||
|
||||
logger.Waitingf("verifying installation")
|
||||
for _, deployment := range components {
|
||||
kubectlArgs = []string{"-n", rootArgs.namespace, "rollout", "status", "deployment", deployment, "--timeout", rootArgs.timeout.String()}
|
||||
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
} else {
|
||||
logger.Successf("%s ready", deployment)
|
||||
}
|
||||
if err := statusChecker.Assess(components...); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
}
|
||||
|
||||
logger.Successf("install finished")
|
||||
|
||||
@@ -41,7 +41,7 @@ var rootCmd = &cobra.Command{
|
||||
Example: ` # Check prerequisites
|
||||
flux check --pre
|
||||
|
||||
# Install the latest version of the toolkit
|
||||
# Install the latest version of Flux
|
||||
flux install --version=master
|
||||
|
||||
# Create a source from a public Git repository
|
||||
@@ -88,8 +88,8 @@ var rootCmd = &cobra.Command{
|
||||
# Delete a GitRepository source
|
||||
flux delete source git webapp-latest
|
||||
|
||||
# Uninstall the toolkit and delete CRDs
|
||||
flux uninstall --crds
|
||||
# Uninstall Flux and delete CRDs
|
||||
flux uninstall
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -115,10 +115,12 @@ func init() {
|
||||
}
|
||||
|
||||
func NewRootFlags() rootFlags {
|
||||
return rootFlags{
|
||||
rf := rootFlags{
|
||||
pollInterval: 2 * time.Second,
|
||||
defaults: install.MakeDefaultOptions(),
|
||||
}
|
||||
rf.defaults.Version = "v" + VERSION
|
||||
return rf
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -93,17 +93,31 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if rhrArgs.syncHrWithSource {
|
||||
nsCopy := rootArgs.namespace
|
||||
if helmRelease.Spec.Chart.Spec.SourceRef.Namespace != "" {
|
||||
rootArgs.namespace = helmRelease.Spec.Chart.Spec.SourceRef.Namespace
|
||||
}
|
||||
switch helmRelease.Spec.Chart.Spec.SourceRef.Kind {
|
||||
case sourcev1.HelmRepositoryKind:
|
||||
err = reconcileSourceHelmCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||
err = reconcileCommand{
|
||||
apiType: helmRepositoryType,
|
||||
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||
case sourcev1.GitRepositoryKind:
|
||||
err = reconcileSourceGitCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||
err = reconcileCommand{
|
||||
apiType: gitRepositoryType,
|
||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||
case sourcev1.BucketKind:
|
||||
err = reconcileSourceBucketCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||
err = reconcileCommand{
|
||||
apiType: bucketType,
|
||||
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootArgs.namespace = nsCopy
|
||||
}
|
||||
|
||||
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt
|
||||
|
||||
@@ -91,15 +91,26 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if rksArgs.syncKsWithSource {
|
||||
nsCopy := rootArgs.namespace
|
||||
if kustomization.Spec.SourceRef.Namespace != "" {
|
||||
rootArgs.namespace = kustomization.Spec.SourceRef.Namespace
|
||||
}
|
||||
switch kustomization.Spec.SourceRef.Kind {
|
||||
case sourcev1.GitRepositoryKind:
|
||||
err = reconcileSourceGitCmdRun(nil, []string{kustomization.Spec.SourceRef.Name})
|
||||
err = reconcileCommand{
|
||||
apiType: gitRepositoryType,
|
||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
|
||||
case sourcev1.BucketKind:
|
||||
err = reconcileSourceBucketCmdRun(nil, []string{kustomization.Spec.SourceRef.Name})
|
||||
err = reconcileCommand{
|
||||
apiType: bucketType,
|
||||
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootArgs.namespace = nsCopy
|
||||
}
|
||||
|
||||
lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
|
||||
|
||||
@@ -19,13 +19,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"k8s.io/client-go/util/retry"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -43,64 +37,16 @@ var reconcileSourceBucketCmd = &cobra.Command{
|
||||
Example: ` # Trigger a reconciliation for an existing source
|
||||
flux reconcile source bucket podinfo
|
||||
`,
|
||||
RunE: reconcileSourceBucketCmdRun,
|
||||
RunE: reconcileCommand{
|
||||
apiType: bucketType,
|
||||
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
reconcileSourceCmd.AddCommand(reconcileSourceBucketCmd)
|
||||
}
|
||||
|
||||
func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var bucket sourcev1.Bucket
|
||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if bucket.Spec.Suspend {
|
||||
return fmt.Errorf("resource is suspended")
|
||||
}
|
||||
|
||||
lastHandledReconcileAt := bucket.Status.LastHandledReconcileAt
|
||||
logger.Actionf("annotating Bucket source %s in %s namespace", name, rootArgs.namespace)
|
||||
if err := requestBucketReconciliation(ctx, kubeClient, namespacedName, &bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("Bucket source annotated")
|
||||
|
||||
logger.Waitingf("waiting for Bucket source reconciliation")
|
||||
if err := wait.PollImmediate(
|
||||
rootArgs.pollInterval, rootArgs.timeout,
|
||||
bucketReconciliationHandled(ctx, kubeClient, namespacedName, &bucket, lastHandledReconcileAt),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("Bucket source reconciliation completed")
|
||||
|
||||
if apimeta.IsStatusConditionFalse(bucket.Status.Conditions, meta.ReadyCondition) {
|
||||
return fmt.Errorf("Bucket source reconciliation failed")
|
||||
}
|
||||
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
|
||||
return nil
|
||||
}
|
||||
|
||||
func isBucketReady(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
@@ -126,30 +72,10 @@ func isBucketReady(ctx context.Context, kubeClient client.Client,
|
||||
}
|
||||
}
|
||||
|
||||
func bucketReconciliationHandled(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket, lastHandledReconcileAt string) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, bucket)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return bucket.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
|
||||
}
|
||||
func (obj bucketAdapter) lastHandledReconcileRequest() string {
|
||||
return obj.Status.GetLastHandledReconcileRequest()
|
||||
}
|
||||
|
||||
func requestBucketReconciliation(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) error {
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
||||
if err := kubeClient.Get(ctx, namespacedName, bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
if bucket.Annotations == nil {
|
||||
bucket.Annotations = map[string]string{
|
||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
||||
}
|
||||
} else {
|
||||
bucket.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
||||
}
|
||||
return kubeClient.Update(ctx, bucket)
|
||||
})
|
||||
func (obj bucketAdapter) successMessage() string {
|
||||
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
|
||||
}
|
||||
|
||||
@@ -17,21 +17,9 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var reconcileSourceGitCmd = &cobra.Command{
|
||||
@@ -41,86 +29,20 @@ var reconcileSourceGitCmd = &cobra.Command{
|
||||
Example: ` # Trigger a git pull for an existing source
|
||||
flux reconcile source git podinfo
|
||||
`,
|
||||
RunE: reconcileSourceGitCmdRun,
|
||||
RunE: reconcileCommand{
|
||||
apiType: gitRepositoryType,
|
||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
reconcileSourceCmd.AddCommand(reconcileSourceGitCmd)
|
||||
}
|
||||
|
||||
func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var repository sourcev1.GitRepository
|
||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if repository.Spec.Suspend {
|
||||
return fmt.Errorf("resource is suspended")
|
||||
}
|
||||
|
||||
logger.Actionf("annotating GitRepository source %s in %s namespace", name, rootArgs.namespace)
|
||||
if err := requestGitRepositoryReconciliation(ctx, kubeClient, namespacedName, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("GitRepository source annotated")
|
||||
|
||||
lastHandledReconcileAt := repository.Status.LastHandledReconcileAt
|
||||
logger.Waitingf("waiting for GitRepository source reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
gitRepositoryReconciliationHandled(ctx, kubeClient, namespacedName, &repository, lastHandledReconcileAt)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("GitRepository source reconciliation completed")
|
||||
|
||||
if apimeta.IsStatusConditionFalse(repository.Status.Conditions, meta.ReadyCondition) {
|
||||
return fmt.Errorf("GitRepository source reconciliation failed")
|
||||
}
|
||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
||||
return nil
|
||||
func (obj gitRepositoryAdapter) lastHandledReconcileRequest() string {
|
||||
return obj.Status.GetLastHandledReconcileRequest()
|
||||
}
|
||||
|
||||
func gitRepositoryReconciliationHandled(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, repository *sourcev1.GitRepository, lastHandledReconcileAt string) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, repository)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return repository.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
|
||||
}
|
||||
}
|
||||
|
||||
func requestGitRepositoryReconciliation(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, repository *sourcev1.GitRepository) error {
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
||||
if err := kubeClient.Get(ctx, namespacedName, repository); err != nil {
|
||||
return err
|
||||
}
|
||||
if repository.Annotations == nil {
|
||||
repository.Annotations = map[string]string{
|
||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
||||
}
|
||||
} else {
|
||||
repository.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
||||
}
|
||||
return kubeClient.Update(ctx, repository)
|
||||
})
|
||||
func (obj gitRepositoryAdapter) successMessage() string {
|
||||
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
|
||||
}
|
||||
|
||||
@@ -17,22 +17,9 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"k8s.io/client-go/util/retry"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var reconcileSourceHelmCmd = &cobra.Command{
|
||||
@@ -42,86 +29,20 @@ var reconcileSourceHelmCmd = &cobra.Command{
|
||||
Example: ` # Trigger a reconciliation for an existing source
|
||||
flux reconcile source helm podinfo
|
||||
`,
|
||||
RunE: reconcileSourceHelmCmdRun,
|
||||
RunE: reconcileCommand{
|
||||
apiType: helmRepositoryType,
|
||||
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
reconcileSourceCmd.AddCommand(reconcileSourceHelmCmd)
|
||||
}
|
||||
|
||||
func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("HelmRepository source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var repository sourcev1.HelmRepository
|
||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if repository.Spec.Suspend {
|
||||
return fmt.Errorf("resource is suspended")
|
||||
}
|
||||
|
||||
logger.Actionf("annotating HelmRepository source %s in %s namespace", name, rootArgs.namespace)
|
||||
if err := requestHelmRepositoryReconciliation(ctx, kubeClient, namespacedName, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("HelmRepository source annotated")
|
||||
|
||||
lastHandledReconcileAt := repository.Status.LastHandledReconcileAt
|
||||
logger.Waitingf("waiting for HelmRepository source reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
helmRepositoryReconciliationHandled(ctx, kubeClient, namespacedName, &repository, lastHandledReconcileAt)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("HelmRepository source reconciliation completed")
|
||||
|
||||
if apimeta.IsStatusConditionFalse(repository.Status.Conditions, meta.ReadyCondition) {
|
||||
return fmt.Errorf("HelmRepository source reconciliation failed")
|
||||
}
|
||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
||||
return nil
|
||||
func (obj helmRepositoryAdapter) lastHandledReconcileRequest() string {
|
||||
return obj.Status.GetLastHandledReconcileRequest()
|
||||
}
|
||||
|
||||
func helmRepositoryReconciliationHandled(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository, lastHandledReconcileAt string) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, repository)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return repository.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
|
||||
}
|
||||
}
|
||||
|
||||
func requestHelmRepositoryReconciliation(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository) error {
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
||||
if err := kubeClient.Get(ctx, namespacedName, repository); err != nil {
|
||||
return err
|
||||
}
|
||||
if repository.Annotations == nil {
|
||||
repository.Annotations = map[string]string{
|
||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
||||
}
|
||||
} else {
|
||||
repository.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
||||
}
|
||||
return kubeClient.Update(ctx, repository)
|
||||
})
|
||||
func (obj helmRepositoryAdapter) successMessage() string {
|
||||
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
|
||||
}
|
||||
|
||||
@@ -17,20 +17,9 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var resumeHrCmd = &cobra.Command{
|
||||
@@ -42,76 +31,24 @@ finish the apply.`,
|
||||
Example: ` # Resume reconciliation for an existing Helm release
|
||||
flux resume hr podinfo
|
||||
`,
|
||||
RunE: resumeHrCmdRun,
|
||||
RunE: resumeCommand{
|
||||
apiType: helmReleaseType,
|
||||
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeCmd.AddCommand(resumeHrCmd)
|
||||
}
|
||||
|
||||
func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("HelmRelease name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var helmRelease helmv2.HelmRelease
|
||||
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("resuming HelmRelease %s in %s namespace", name, rootArgs.namespace)
|
||||
helmRelease.Spec.Suspend = false
|
||||
if err := kubeClient.Update(ctx, &helmRelease); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("HelmRelease resumed")
|
||||
|
||||
logger.Waitingf("waiting for HelmRelease reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isHelmReleaseResumed(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("HelmRelease reconciliation completed")
|
||||
|
||||
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
|
||||
return nil
|
||||
func (obj helmReleaseAdapter) getObservedGeneration() int64 {
|
||||
return obj.HelmRelease.Status.ObservedGeneration
|
||||
}
|
||||
|
||||
func isHelmReleaseResumed(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, helmRelease)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Confirm the state we are observing is for the current generation
|
||||
if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if c := apimeta.FindStatusCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
|
||||
switch c.Status {
|
||||
case metav1.ConditionTrue:
|
||||
return true, nil
|
||||
case metav1.ConditionFalse:
|
||||
return false, fmt.Errorf(c.Message)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func (obj helmReleaseAdapter) setUnsuspended() {
|
||||
obj.HelmRelease.Spec.Suspend = false
|
||||
}
|
||||
|
||||
func (obj helmReleaseAdapter) successMessage() string {
|
||||
return fmt.Sprintf("applied revision %s", obj.Status.LastAppliedRevision)
|
||||
}
|
||||
|
||||
@@ -17,19 +17,10 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
var resumeKsCmd = &cobra.Command{
|
||||
@@ -41,76 +32,24 @@ finish the apply.`,
|
||||
Example: ` # Resume reconciliation for an existing Kustomization
|
||||
flux resume ks podinfo
|
||||
`,
|
||||
RunE: resumeKsCmdRun,
|
||||
RunE: resumeCommand{
|
||||
apiType: kustomizationType,
|
||||
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeCmd.AddCommand(resumeKsCmd)
|
||||
}
|
||||
|
||||
func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("Kustomization name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var kustomization kustomizev1.Kustomization
|
||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("resuming Kustomization %s in %s namespace", name, rootArgs.namespace)
|
||||
kustomization.Spec.Suspend = false
|
||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("Kustomization resumed")
|
||||
|
||||
logger.Waitingf("waiting for Kustomization reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isKustomizationResumed(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("Kustomization reconciliation completed")
|
||||
|
||||
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
|
||||
return nil
|
||||
func (obj kustomizationAdapter) getObservedGeneration() int64 {
|
||||
return obj.Kustomization.Status.ObservedGeneration
|
||||
}
|
||||
|
||||
func isKustomizationResumed(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, kustomization)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Confirm the state we are observing is for the current generation
|
||||
if kustomization.Generation != kustomization.Status.ObservedGeneration {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
|
||||
switch c.Status {
|
||||
case metav1.ConditionTrue:
|
||||
return true, nil
|
||||
case metav1.ConditionFalse:
|
||||
return false, fmt.Errorf(c.Message)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func (obj kustomizationAdapter) setUnsuspended() {
|
||||
obj.Kustomization.Spec.Suspend = false
|
||||
}
|
||||
|
||||
func (obj kustomizationAdapter) successMessage() string {
|
||||
return fmt.Sprintf("applied revision %s", obj.Status.LastAppliedRevision)
|
||||
}
|
||||
|
||||
@@ -17,20 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var resumeSourceBucketCmd = &cobra.Command{
|
||||
@@ -40,76 +28,20 @@ var resumeSourceBucketCmd = &cobra.Command{
|
||||
Example: ` # Resume reconciliation for an existing Bucket
|
||||
flux resume source bucket podinfo
|
||||
`,
|
||||
RunE: resumeSourceBucketCmdRun,
|
||||
RunE: resumeCommand{
|
||||
apiType: bucketType,
|
||||
object: &bucketAdapter{&sourcev1.Bucket{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeSourceCmd.AddCommand(resumeSourceBucketCmd)
|
||||
}
|
||||
|
||||
func resumeSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var bucket sourcev1.Bucket
|
||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("resuming source %s in %s namespace", name, rootArgs.namespace)
|
||||
bucket.Spec.Suspend = false
|
||||
if err := kubeClient.Update(ctx, &bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source resumed")
|
||||
|
||||
logger.Waitingf("waiting for Bucket reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isBucketResumed(ctx, kubeClient, namespacedName, &bucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("Bucket reconciliation completed")
|
||||
|
||||
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
|
||||
return nil
|
||||
func (obj bucketAdapter) getObservedGeneration() int64 {
|
||||
return obj.Bucket.Status.ObservedGeneration
|
||||
}
|
||||
|
||||
func isBucketResumed(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, bucket)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Confirm the state we are observing is for the current generation
|
||||
if bucket.Generation != bucket.Status.ObservedGeneration {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if c := apimeta.FindStatusCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil {
|
||||
switch c.Status {
|
||||
case metav1.ConditionTrue:
|
||||
return true, nil
|
||||
case metav1.ConditionFalse:
|
||||
return false, fmt.Errorf(c.Message)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func (obj bucketAdapter) setUnsuspended() {
|
||||
obj.Bucket.Spec.Suspend = false
|
||||
}
|
||||
|
||||
@@ -17,20 +17,10 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var resumeSourceHelmChartCmd = &cobra.Command{
|
||||
@@ -40,76 +30,24 @@ var resumeSourceHelmChartCmd = &cobra.Command{
|
||||
Example: ` # Resume reconciliation for an existing HelmChart
|
||||
flux resume source chart podinfo
|
||||
`,
|
||||
RunE: resumeSourceHelmChartCmdRun,
|
||||
RunE: resumeCommand{
|
||||
apiType: helmChartType,
|
||||
object: &helmChartAdapter{&sourcev1.HelmChart{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeSourceCmd.AddCommand(resumeSourceHelmChartCmd)
|
||||
}
|
||||
|
||||
func resumeSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var repository sourcev1.HelmChart
|
||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("resuming source %s in %s namespace", name, rootArgs.namespace)
|
||||
repository.Spec.Suspend = false
|
||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source resumed")
|
||||
|
||||
logger.Waitingf("waiting for HelmChart reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isHelmChartResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("HelmChart reconciliation completed")
|
||||
|
||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
||||
return nil
|
||||
func (obj helmChartAdapter) getObservedGeneration() int64 {
|
||||
return obj.HelmChart.Status.ObservedGeneration
|
||||
}
|
||||
|
||||
func isHelmChartResumed(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, chart *sourcev1.HelmChart) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, chart)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Confirm the state we are observing is for the current generation
|
||||
if chart.Generation != chart.Status.ObservedGeneration {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if c := apimeta.FindStatusCondition(chart.Status.Conditions, meta.ReadyCondition); c != nil {
|
||||
switch c.Status {
|
||||
case metav1.ConditionTrue:
|
||||
return true, nil
|
||||
case metav1.ConditionFalse:
|
||||
return false, fmt.Errorf(c.Message)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func (obj helmChartAdapter) setUnsuspended() {
|
||||
obj.HelmChart.Spec.Suspend = false
|
||||
}
|
||||
|
||||
func (obj helmChartAdapter) successMessage() string {
|
||||
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
|
||||
}
|
||||
|
||||
@@ -17,20 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var resumeSourceGitCmd = &cobra.Command{
|
||||
@@ -40,76 +28,20 @@ var resumeSourceGitCmd = &cobra.Command{
|
||||
Example: ` # Resume reconciliation for an existing GitRepository
|
||||
flux resume source git podinfo
|
||||
`,
|
||||
RunE: resumeSourceGitCmdRun,
|
||||
RunE: resumeCommand{
|
||||
apiType: gitRepositoryType,
|
||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeSourceCmd.AddCommand(resumeSourceGitCmd)
|
||||
}
|
||||
|
||||
func resumeSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var repository sourcev1.GitRepository
|
||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("resuming source %s in %s namespace", name, rootArgs.namespace)
|
||||
repository.Spec.Suspend = false
|
||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source resumed")
|
||||
|
||||
logger.Waitingf("waiting for GitRepository reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isGitRepositoryResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("GitRepository reconciliation completed")
|
||||
|
||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
||||
return nil
|
||||
func (obj gitRepositoryAdapter) getObservedGeneration() int64 {
|
||||
return obj.GitRepository.Status.ObservedGeneration
|
||||
}
|
||||
|
||||
func isGitRepositoryResumed(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, repository *sourcev1.GitRepository) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, repository)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Confirm the state we are observing is for the current generation
|
||||
if repository.Generation != repository.Status.ObservedGeneration {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if c := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); c != nil {
|
||||
switch c.Status {
|
||||
case metav1.ConditionTrue:
|
||||
return true, nil
|
||||
case metav1.ConditionFalse:
|
||||
return false, fmt.Errorf(c.Message)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func (obj gitRepositoryAdapter) setUnsuspended() {
|
||||
obj.GitRepository.Spec.Suspend = false
|
||||
}
|
||||
|
||||
@@ -17,20 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var resumeSourceHelmCmd = &cobra.Command{
|
||||
@@ -40,76 +28,20 @@ var resumeSourceHelmCmd = &cobra.Command{
|
||||
Example: ` # Resume reconciliation for an existing HelmRepository
|
||||
flux resume source helm bitnami
|
||||
`,
|
||||
RunE: resumeSourceHelmCmdRun,
|
||||
RunE: resumeCommand{
|
||||
apiType: helmRepositoryType,
|
||||
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeSourceCmd.AddCommand(resumeSourceHelmCmd)
|
||||
}
|
||||
|
||||
func resumeSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var repository sourcev1.HelmRepository
|
||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("resuming source %s in %s namespace", name, rootArgs.namespace)
|
||||
repository.Spec.Suspend = false
|
||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source resumed")
|
||||
|
||||
logger.Waitingf("waiting for HelmRepository reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isHelmRepositoryResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("HelmRepository reconciliation completed")
|
||||
|
||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
||||
return nil
|
||||
func (obj helmRepositoryAdapter) getObservedGeneration() int64 {
|
||||
return obj.HelmRepository.Status.ObservedGeneration
|
||||
}
|
||||
|
||||
func isHelmRepositoryResumed(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, repository)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Confirm the state we are observing is for the current generation
|
||||
if repository.Generation != repository.Status.ObservedGeneration {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if c := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); c != nil {
|
||||
switch c.Status {
|
||||
case metav1.ConditionTrue:
|
||||
return true, nil
|
||||
case metav1.ConditionFalse:
|
||||
return false, fmt.Errorf(c.Message)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func (obj helmRepositoryAdapter) setUnsuspended() {
|
||||
obj.HelmRepository.Spec.Suspend = false
|
||||
}
|
||||
|
||||
@@ -19,14 +19,26 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/cli-utils/pkg/kstatus/polling"
|
||||
"sigs.k8s.io/cli-utils/pkg/kstatus/polling/aggregator"
|
||||
"sigs.k8s.io/cli-utils/pkg/kstatus/polling/collector"
|
||||
"sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
|
||||
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
||||
"sigs.k8s.io/cli-utils/pkg/object"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
// statusable is used to see if a resource is considered ready in the usual way
|
||||
@@ -39,6 +51,13 @@ type statusable interface {
|
||||
GetStatusConditions() *[]metav1.Condition
|
||||
}
|
||||
|
||||
type StatusChecker struct {
|
||||
pollInterval time.Duration
|
||||
timeout time.Duration
|
||||
client client.Client
|
||||
statusPoller *polling.StatusPoller
|
||||
}
|
||||
|
||||
func isReady(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, object statusable) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
@@ -63,3 +82,99 @@ func isReady(ctx context.Context, kubeClient client.Client,
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewStatusChecker(pollInterval time.Duration, timeout time.Duration) (*StatusChecker, error) {
|
||||
kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
restMapper, err := apiutil.NewDynamicRESTMapper(kubeConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := client.New(kubeConfig, client.Options{Mapper: restMapper})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StatusChecker{
|
||||
pollInterval: pollInterval,
|
||||
timeout: timeout,
|
||||
client: client,
|
||||
statusPoller: polling.NewStatusPoller(client, restMapper),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sc *StatusChecker) Assess(components ...string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), sc.timeout)
|
||||
defer cancel()
|
||||
|
||||
objRefs, err := sc.getObjectRefs(components)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := polling.Options{PollInterval: sc.pollInterval, UseCache: true}
|
||||
eventsChan := sc.statusPoller.Poll(ctx, objRefs, opts)
|
||||
|
||||
coll := collector.NewResourceStatusCollector(objRefs)
|
||||
done := coll.ListenWithObserver(eventsChan, collector.ObserverFunc(
|
||||
func(statusCollector *collector.ResourceStatusCollector, e event.Event) {
|
||||
var rss []*event.ResourceStatus
|
||||
for _, rs := range statusCollector.ResourceStatuses {
|
||||
rss = append(rss, rs)
|
||||
}
|
||||
desired := status.CurrentStatus
|
||||
aggStatus := aggregator.AggregateStatus(rss, desired)
|
||||
if aggStatus == desired {
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
}),
|
||||
)
|
||||
<-done
|
||||
|
||||
if coll.Error != nil || ctx.Err() == context.DeadlineExceeded {
|
||||
for _, rs := range coll.ResourceStatuses {
|
||||
if rs.Status != status.CurrentStatus {
|
||||
if !sc.deploymentExists(rs.Identifier) {
|
||||
logger.Failuref("%s: deployment not found", rs.Identifier.Name)
|
||||
} else {
|
||||
logger.Failuref("%s: unhealthy (timed out waiting for rollout)", rs.Identifier.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("timed out waiting for condition")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *StatusChecker) getObjectRefs(components []string) ([]object.ObjMetadata, error) {
|
||||
var objRefs []object.ObjMetadata
|
||||
for _, deployment := range components {
|
||||
objMeta, err := object.CreateObjMetadata(rootArgs.namespace, deployment, schema.GroupKind{Group: "apps", Kind: "Deployment"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objRefs = append(objRefs, objMeta)
|
||||
}
|
||||
return objRefs, nil
|
||||
}
|
||||
|
||||
func (sc *StatusChecker) objMetadataToString(om object.ObjMetadata) string {
|
||||
return fmt.Sprintf("%s '%s/%s'", om.GroupKind.Kind, om.Namespace, om.Name)
|
||||
}
|
||||
|
||||
func (sc *StatusChecker) deploymentExists(om object.ObjMetadata) bool {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), sc.timeout)
|
||||
defer cancel()
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: om.Namespace,
|
||||
Name: om.Name,
|
||||
}
|
||||
var existing appsv1.Deployment
|
||||
err := sc.client.Get(ctx, namespacedName, &existing)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -17,14 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var suspendHrCmd = &cobra.Command{
|
||||
@@ -35,43 +29,20 @@ var suspendHrCmd = &cobra.Command{
|
||||
Example: ` # Suspend reconciliation for an existing Helm release
|
||||
flux suspend hr podinfo
|
||||
`,
|
||||
RunE: suspendHrCmdRun,
|
||||
RunE: suspendCommand{
|
||||
apiType: helmReleaseType,
|
||||
object: &helmReleaseAdapter{&helmv2.HelmRelease{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendCmd.AddCommand(suspendHrCmd)
|
||||
}
|
||||
|
||||
func suspendHrCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("HelmRelease name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var helmRelease helmv2.HelmRelease
|
||||
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("suspending HelmRelease %s in %s namespace", name, rootArgs.namespace)
|
||||
helmRelease.Spec.Suspend = true
|
||||
if err := kubeClient.Update(ctx, &helmRelease); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("HelmRelease suspended")
|
||||
|
||||
return nil
|
||||
func (obj helmReleaseAdapter) isSuspended() bool {
|
||||
return obj.HelmRelease.Spec.Suspend
|
||||
}
|
||||
|
||||
func (obj helmReleaseAdapter) setSuspended() {
|
||||
obj.HelmRelease.Spec.Suspend = true
|
||||
}
|
||||
|
||||
@@ -17,13 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var suspendKsCmd = &cobra.Command{
|
||||
@@ -34,43 +29,20 @@ var suspendKsCmd = &cobra.Command{
|
||||
Example: ` # Suspend reconciliation for an existing Kustomization
|
||||
flux suspend ks podinfo
|
||||
`,
|
||||
RunE: suspendKsCmdRun,
|
||||
RunE: suspendCommand{
|
||||
apiType: kustomizationType,
|
||||
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendCmd.AddCommand(suspendKsCmd)
|
||||
}
|
||||
|
||||
func suspendKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("kustomization name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var kustomization kustomizev1.Kustomization
|
||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("suspending kustomization %s in %s namespace", name, rootArgs.namespace)
|
||||
kustomization.Spec.Suspend = true
|
||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("kustomization suspended")
|
||||
|
||||
return nil
|
||||
func (obj kustomizationAdapter) isSuspended() bool {
|
||||
return obj.Kustomization.Spec.Suspend
|
||||
}
|
||||
|
||||
func (obj kustomizationAdapter) setSuspended() {
|
||||
obj.Kustomization.Spec.Suspend = true
|
||||
}
|
||||
|
||||
@@ -17,13 +17,8 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var suspendSourceBucketCmd = &cobra.Command{
|
||||
@@ -33,43 +28,20 @@ var suspendSourceBucketCmd = &cobra.Command{
|
||||
Example: ` # Suspend reconciliation for an existing Bucket
|
||||
flux suspend source bucket podinfo
|
||||
`,
|
||||
RunE: suspendSourceBucketCmdRun,
|
||||
RunE: suspendCommand{
|
||||
apiType: bucketType,
|
||||
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendSourceCmd.AddCommand(suspendSourceBucketCmd)
|
||||
}
|
||||
|
||||
func suspendSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var bucket sourcev1.Bucket
|
||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("suspending source %s in %s namespace", name, rootArgs.namespace)
|
||||
bucket.Spec.Suspend = true
|
||||
if err := kubeClient.Update(ctx, &bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source suspended")
|
||||
|
||||
return nil
|
||||
func (obj bucketAdapter) isSuspended() bool {
|
||||
return obj.Bucket.Spec.Suspend
|
||||
}
|
||||
|
||||
func (obj bucketAdapter) setSuspended() {
|
||||
obj.Bucket.Spec.Suspend = true
|
||||
}
|
||||
|
||||
@@ -17,13 +17,9 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var suspendSourceHelmChartCmd = &cobra.Command{
|
||||
@@ -33,43 +29,20 @@ var suspendSourceHelmChartCmd = &cobra.Command{
|
||||
Example: ` # Suspend reconciliation for an existing HelmChart
|
||||
flux suspend source chart podinfo
|
||||
`,
|
||||
RunE: suspendSourceHelmChartCmdRun,
|
||||
RunE: suspendCommand{
|
||||
apiType: helmChartType,
|
||||
object: helmChartAdapter{&sourcev1.HelmChart{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendSourceCmd.AddCommand(suspendSourceHelmChartCmd)
|
||||
}
|
||||
|
||||
func suspendSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var chart sourcev1.HelmChart
|
||||
err = kubeClient.Get(ctx, namespacedName, &chart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("suspending source %s in %s namespace", name, rootArgs.namespace)
|
||||
chart.Spec.Suspend = true
|
||||
if err := kubeClient.Update(ctx, &chart); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source suspended")
|
||||
|
||||
return nil
|
||||
func (obj helmChartAdapter) isSuspended() bool {
|
||||
return obj.HelmChart.Spec.Suspend
|
||||
}
|
||||
|
||||
func (obj helmChartAdapter) setSuspended() {
|
||||
obj.HelmChart.Spec.Suspend = true
|
||||
}
|
||||
|
||||
@@ -17,13 +17,9 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var suspendSourceGitCmd = &cobra.Command{
|
||||
@@ -33,43 +29,20 @@ var suspendSourceGitCmd = &cobra.Command{
|
||||
Example: ` # Suspend reconciliation for an existing GitRepository
|
||||
flux suspend source git podinfo
|
||||
`,
|
||||
RunE: suspendSourceGitCmdRun,
|
||||
RunE: suspendCommand{
|
||||
apiType: gitRepositoryType,
|
||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendSourceCmd.AddCommand(suspendSourceGitCmd)
|
||||
}
|
||||
|
||||
func suspendSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var repository sourcev1.GitRepository
|
||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("suspending source %s in %s namespace", name, rootArgs.namespace)
|
||||
repository.Spec.Suspend = true
|
||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source suspended")
|
||||
|
||||
return nil
|
||||
func (obj gitRepositoryAdapter) isSuspended() bool {
|
||||
return obj.GitRepository.Spec.Suspend
|
||||
}
|
||||
|
||||
func (obj gitRepositoryAdapter) setSuspended() {
|
||||
obj.GitRepository.Spec.Suspend = true
|
||||
}
|
||||
|
||||
@@ -17,13 +17,9 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var suspendSourceHelmCmd = &cobra.Command{
|
||||
@@ -33,43 +29,20 @@ var suspendSourceHelmCmd = &cobra.Command{
|
||||
Example: ` # Suspend reconciliation for an existing HelmRepository
|
||||
flux suspend source helm bitnami
|
||||
`,
|
||||
RunE: suspendSourceHelmCmdRun,
|
||||
RunE: suspendCommand{
|
||||
apiType: helmRepositoryType,
|
||||
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendSourceCmd.AddCommand(suspendSourceHelmCmd)
|
||||
}
|
||||
|
||||
func suspendSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("source name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: name,
|
||||
}
|
||||
var repository sourcev1.HelmRepository
|
||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("suspending source %s in %s namespace", name, rootArgs.namespace)
|
||||
repository.Spec.Suspend = true
|
||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("source suspended")
|
||||
|
||||
return nil
|
||||
func (obj helmRepositoryAdapter) isSuspended() bool {
|
||||
return obj.HelmRepository.Spec.Suspend
|
||||
}
|
||||
|
||||
func (obj helmRepositoryAdapter) setSuspended() {
|
||||
obj.HelmRepository.Spec.Suspend = true
|
||||
}
|
||||
|
||||
@@ -22,8 +22,12 @@ import (
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
@@ -34,33 +38,30 @@ import (
|
||||
|
||||
var uninstallCmd = &cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "Uninstall the toolkit components",
|
||||
Long: "The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.",
|
||||
Example: ` # Dry-run uninstall of all components
|
||||
flux uninstall --dry-run --namespace=flux-system
|
||||
Short: "Uninstall Flux and its custom resource definitions",
|
||||
Long: "The uninstall command removes the Flux components and the toolkit.fluxcd.io resources from the cluster.",
|
||||
Example: ` # Uninstall Flux components, its custom resources and namespace
|
||||
flux uninstall --namespace=flux-system
|
||||
|
||||
# Uninstall all components and delete custom resource definitions
|
||||
flux uninstall --resources --crds --namespace=flux-system
|
||||
# Uninstall Flux but keep the namespace
|
||||
flux uninstall --namespace=infra --keep-namespace=true
|
||||
`,
|
||||
RunE: uninstallCmdRun,
|
||||
}
|
||||
|
||||
type uninstallFlags struct {
|
||||
crds bool
|
||||
resources bool
|
||||
dryRun bool
|
||||
silent bool
|
||||
keepNamespace bool
|
||||
dryRun bool
|
||||
silent bool
|
||||
}
|
||||
|
||||
var uninstallArgs uninstallFlags
|
||||
|
||||
func init() {
|
||||
uninstallCmd.Flags().BoolVar(&uninstallArgs.resources, "resources", true,
|
||||
"removes custom resources such as Kustomizations, GitRepositories and HelmRepositories")
|
||||
uninstallCmd.Flags().BoolVar(&uninstallArgs.crds, "crds", false,
|
||||
"removes all CRDs previously installed")
|
||||
uninstallCmd.Flags().BoolVar(&uninstallArgs.keepNamespace, "keep-namespace", false,
|
||||
"skip namespace deletion")
|
||||
uninstallCmd.Flags().BoolVar(&uninstallArgs.dryRun, "dry-run", false,
|
||||
"only print the object that would be deleted")
|
||||
"only print the objects that would be deleted")
|
||||
uninstallCmd.Flags().BoolVarP(&uninstallArgs.silent, "silent", "s", false,
|
||||
"delete components without asking for confirmation")
|
||||
|
||||
@@ -68,6 +69,16 @@ func init() {
|
||||
}
|
||||
|
||||
func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if !uninstallArgs.dryRun && !uninstallArgs.silent {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Are you sure you want to delete Flux and its custom resource definitions",
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err != nil {
|
||||
return fmt.Errorf("aborting")
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -76,96 +87,227 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !uninstallArgs.dryRun && !uninstallArgs.silent {
|
||||
prompt := promptui.Prompt{
|
||||
Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", rootArgs.namespace),
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err != nil {
|
||||
return fmt.Errorf("aborting")
|
||||
}
|
||||
}
|
||||
logger.Actionf("deleting components in %s namespace", rootArgs.namespace)
|
||||
uninstallComponents(ctx, kubeClient, rootArgs.namespace, uninstallArgs.dryRun)
|
||||
|
||||
dryRun := "--dry-run=server"
|
||||
deleteResources := uninstallArgs.resources || uninstallArgs.crds
|
||||
logger.Actionf("deleting toolkit.fluxcd.io finalizers in all namespaces")
|
||||
uninstallFinalizers(ctx, kubeClient, uninstallArgs.dryRun)
|
||||
|
||||
// known kinds with finalizers
|
||||
namespacedKinds := []string{
|
||||
sourcev1.GitRepositoryKind,
|
||||
sourcev1.HelmRepositoryKind,
|
||||
sourcev1.BucketKind,
|
||||
}
|
||||
logger.Actionf("deleting toolkit.fluxcd.io custom resource definitions")
|
||||
uninstallCustomResourceDefinitions(ctx, kubeClient, rootArgs.namespace, uninstallArgs.dryRun)
|
||||
|
||||
// suspend bootstrap kustomization to avoid finalizers deadlock
|
||||
kustomizationName := types.NamespacedName{
|
||||
Namespace: rootArgs.namespace,
|
||||
Name: rootArgs.namespace,
|
||||
}
|
||||
var kustomization kustomizev1.Kustomization
|
||||
err = kubeClient.Get(ctx, kustomizationName, &kustomization)
|
||||
if err == nil {
|
||||
kustomization.Spec.Suspend = true
|
||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
||||
return fmt.Errorf("unable to suspend kustomization '%s': %w", kustomizationName.String(), err)
|
||||
}
|
||||
}
|
||||
if err == nil || apierrors.IsNotFound(err) {
|
||||
namespacedKinds = append(namespacedKinds, kustomizev1.KustomizationKind)
|
||||
}
|
||||
|
||||
// add HelmRelease kind to deletion list if exists
|
||||
var list helmv2.HelmReleaseList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace)); err == nil {
|
||||
namespacedKinds = append(namespacedKinds, helmv2.HelmReleaseKind)
|
||||
}
|
||||
|
||||
if deleteResources {
|
||||
logger.Actionf("uninstalling custom resources")
|
||||
for _, kind := range namespacedKinds {
|
||||
if err := deleteAll(ctx, kind, uninstallArgs.dryRun); err != nil {
|
||||
logger.Failuref("kubectl: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var kinds []string
|
||||
if uninstallArgs.crds {
|
||||
kinds = append(kinds, "crds")
|
||||
}
|
||||
|
||||
kinds = append(kinds, "clusterroles,clusterrolebindings", "namespace")
|
||||
|
||||
logger.Actionf("uninstalling components")
|
||||
|
||||
for _, kind := range kinds {
|
||||
kubectlArgs := []string{
|
||||
"delete", kind,
|
||||
"-l", fmt.Sprintf("app.kubernetes.io/instance=%s", rootArgs.namespace),
|
||||
"--ignore-not-found", "--timeout", rootArgs.timeout.String(),
|
||||
}
|
||||
if uninstallArgs.dryRun {
|
||||
kubectlArgs = append(kubectlArgs, dryRun)
|
||||
}
|
||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||
return fmt.Errorf("uninstall failed: %w", err)
|
||||
}
|
||||
if !uninstallArgs.keepNamespace {
|
||||
uninstallNamespace(ctx, kubeClient, rootArgs.namespace, uninstallArgs.dryRun)
|
||||
}
|
||||
|
||||
logger.Successf("uninstall finished")
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteAll(ctx context.Context, kind string, dryRun bool) error {
|
||||
kubectlArgs := []string{
|
||||
"delete", kind, "--ignore-not-found",
|
||||
"--all", "--all-namespaces",
|
||||
"--timeout", rootArgs.timeout.String(),
|
||||
func uninstallComponents(ctx context.Context, kubeClient client.Client, namespace string, dryRun bool) {
|
||||
opts, dryRunStr := getDeleteOptions(dryRun)
|
||||
selector := client.MatchingLabels{"app.kubernetes.io/instance": namespace}
|
||||
{
|
||||
var list appsv1.DeploymentList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace), selector); err == nil {
|
||||
for _, r := range list.Items {
|
||||
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("Deployment/%s/%s deletion failed: %s", r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("Deployment/%s/%s deleted %s", r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
kubectlArgs = append(kubectlArgs, "--dry-run=server")
|
||||
{
|
||||
var list corev1.ServiceList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace), selector); err == nil {
|
||||
for _, r := range list.Items {
|
||||
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("Service/%s/%s deletion failed: %s", r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("Service/%s/%s deleted %s", r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list networkingv1.NetworkPolicyList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace), selector); err == nil {
|
||||
for _, r := range list.Items {
|
||||
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("NetworkPolicy/%s/%s deletion failed: %s", r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("NetworkPolicy/%s/%s deleted %s", r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list corev1.ServiceAccountList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace), selector); err == nil {
|
||||
for _, r := range list.Items {
|
||||
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("ServiceAccount/%s/%s deletion failed: %s", r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("ServiceAccount/%s/%s deleted %s", r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list rbacv1.ClusterRoleList
|
||||
if err := kubeClient.List(ctx, &list, selector); err == nil {
|
||||
for _, r := range list.Items {
|
||||
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("ClusterRole/%s deletion failed: %s", r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("ClusterRole/%s deleted %s", r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list rbacv1.ClusterRoleBindingList
|
||||
if err := kubeClient.List(ctx, &list, selector); err == nil {
|
||||
for _, r := range list.Items {
|
||||
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("ClusterRoleBinding/%s deletion failed: %s", r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("ClusterRoleBinding/%s deleted %s", r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
|
||||
return err
|
||||
}
|
||||
|
||||
func uninstallFinalizers(ctx context.Context, kubeClient client.Client, dryRun bool) {
|
||||
opts, dryRunStr := getUpdateOptions(dryRun)
|
||||
{
|
||||
var list sourcev1.GitRepositoryList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||
for _, r := range list.Items {
|
||||
r.Finalizers = []string{}
|
||||
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list sourcev1.HelmRepositoryList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||
for _, r := range list.Items {
|
||||
r.Finalizers = []string{}
|
||||
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list sourcev1.HelmChartList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||
for _, r := range list.Items {
|
||||
r.Finalizers = []string{}
|
||||
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list sourcev1.BucketList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||
for _, r := range list.Items {
|
||||
r.Finalizers = []string{}
|
||||
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list kustomizev1.KustomizationList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||
for _, r := range list.Items {
|
||||
r.Finalizers = []string{}
|
||||
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var list helmv2.HelmReleaseList
|
||||
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||
for _, r := range list.Items {
|
||||
r.Finalizers = []string{}
|
||||
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func uninstallCustomResourceDefinitions(ctx context.Context, kubeClient client.Client, namespace string, dryRun bool) {
|
||||
opts, dryRunStr := getDeleteOptions(dryRun)
|
||||
selector := client.MatchingLabels{"app.kubernetes.io/instance": namespace}
|
||||
{
|
||||
var list apiextensionsv1.CustomResourceDefinitionList
|
||||
if err := kubeClient.List(ctx, &list, selector); err == nil {
|
||||
for _, r := range list.Items {
|
||||
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||
logger.Failuref("CustomResourceDefinition/%s deletion failed: %s", r.Name, err.Error())
|
||||
} else {
|
||||
logger.Successf("CustomResourceDefinition/%s deleted %s", r.Name, dryRunStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func uninstallNamespace(ctx context.Context, kubeClient client.Client, namespace string, dryRun bool) {
|
||||
opts, dryRunStr := getDeleteOptions(dryRun)
|
||||
ns := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
|
||||
if err := kubeClient.Delete(ctx, &ns, opts); err != nil {
|
||||
logger.Failuref("Namespace/%s deletion failed: %s", namespace, err.Error())
|
||||
} else {
|
||||
logger.Successf("Namespace/%s deleted %s", namespace, dryRunStr)
|
||||
}
|
||||
}
|
||||
|
||||
func getDeleteOptions(dryRun bool) (*client.DeleteOptions, string) {
|
||||
opts := &client.DeleteOptions{}
|
||||
var dryRunStr string
|
||||
if dryRun {
|
||||
client.DryRunAll.ApplyToDelete(opts)
|
||||
dryRunStr = "(dry run)"
|
||||
}
|
||||
|
||||
return opts, dryRunStr
|
||||
}
|
||||
|
||||
func getUpdateOptions(dryRun bool) (*client.UpdateOptions, string) {
|
||||
opts := &client.UpdateOptions{}
|
||||
var dryRunStr string
|
||||
if dryRun {
|
||||
client.DryRunAll.ApplyToUpdate(opts)
|
||||
dryRunStr = "(dry run)"
|
||||
}
|
||||
|
||||
return opts, dryRunStr
|
||||
}
|
||||
|
||||
42
cmd/flux/version.go
Normal file
42
cmd/flux/version.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
)
|
||||
|
||||
func getVersion(input string) (string, error) {
|
||||
if input == "" {
|
||||
return rootArgs.defaults.Version, nil
|
||||
}
|
||||
|
||||
if isEmbeddedVersion(input) {
|
||||
return input, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if input == install.MakeDefaultOptions().Version {
|
||||
input, err = install.GetLatestVersion()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
if ok, err := install.ExistingVersion(input); err != nil || !ok {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("targeted version '%s' does not exist", input)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if !utils.CompatibleVersion(VERSION, input) {
|
||||
return "", fmt.Errorf("targeted version '%s' is not compatible with your current version of flux (%s)", input, VERSION)
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func isEmbeddedVersion(input string) bool {
|
||||
return input == rootArgs.defaults.Version
|
||||
}
|
||||
@@ -12,7 +12,7 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
||||
# Check prerequisites
|
||||
flux check --pre
|
||||
|
||||
# Install the latest version of the toolkit
|
||||
# Install the latest version of Flux
|
||||
flux install --version=master
|
||||
|
||||
# Create a source from a public Git repository
|
||||
@@ -59,8 +59,8 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
||||
# Delete a GitRepository source
|
||||
flux delete source git webapp-latest
|
||||
|
||||
# Uninstall the toolkit and delete CRDs
|
||||
flux uninstall --crds
|
||||
# Uninstall Flux and delete CRDs
|
||||
flux uninstall
|
||||
|
||||
```
|
||||
|
||||
@@ -84,9 +84,9 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
||||
* [flux delete](flux_delete.md) - Delete sources and resources
|
||||
* [flux export](flux_export.md) - Export resources in YAML format
|
||||
* [flux get](flux_get.md) - Get sources and resources
|
||||
* [flux install](flux_install.md) - Install the toolkit components
|
||||
* [flux install](flux_install.md) - Install or upgrade Flux
|
||||
* [flux reconcile](flux_reconcile.md) - Reconcile sources and resources
|
||||
* [flux resume](flux_resume.md) - Resume suspended resources
|
||||
* [flux suspend](flux_suspend.md) - Suspend resources
|
||||
* [flux uninstall](flux_uninstall.md) - Uninstall the toolkit components
|
||||
* [flux uninstall](flux_uninstall.md) - Uninstall Flux and its custom resource definitions
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
|
||||
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
|
||||
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
||||
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
||||
-v, --version string toolkit version (default "latest")
|
||||
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints
|
||||
-v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases
|
||||
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
||||
```
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ flux bootstrap github [flags]
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitHub user or organization name
|
||||
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--personal if true, the owner is assumed to be a GitHub user; otherwise an org
|
||||
--private if true, the repository is assumed to be private (default true)
|
||||
--repository string GitHub repository name
|
||||
--ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
--team stringArray GitHub team to be given maintainer access
|
||||
@@ -74,8 +74,9 @@ flux bootstrap github [flags]
|
||||
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
||||
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints
|
||||
--verbose print generated objects
|
||||
-v, --version string toolkit version (default "latest")
|
||||
-v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases
|
||||
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
||||
```
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ flux bootstrap gitlab [flags]
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitLab user or group name
|
||||
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--personal if true, the owner is assumed to be a GitLab user; otherwise a group
|
||||
--private if true, the repository is assumed to be private (default true)
|
||||
--repository string GitLab repository name
|
||||
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
```
|
||||
@@ -70,8 +70,9 @@ flux bootstrap gitlab [flags]
|
||||
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
||||
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints
|
||||
--verbose print generated objects
|
||||
-v, --version string toolkit version (default "latest")
|
||||
-v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases
|
||||
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
||||
```
|
||||
|
||||
|
||||
@@ -25,9 +25,10 @@ flux check [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
-h, --help help for check
|
||||
--pre only run pre-installation checks
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
-h, --help help for check
|
||||
--pre only run pre-installation checks
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -32,11 +32,12 @@ flux create helmrelease [name] [flags]
|
||||
--source=Bucket/podinfo \
|
||||
--chart=./charts/podinfo
|
||||
|
||||
# Create a HelmRelease with values from a local YAML file
|
||||
# Create a HelmRelease with values from local YAML files
|
||||
flux create hr podinfo \
|
||||
--source=HelmRepository/podinfo \
|
||||
--chart=podinfo \
|
||||
--values=./my-values.yaml
|
||||
--values=./my-values1.yaml \
|
||||
--values=./my-values2.yaml
|
||||
|
||||
# Create a HelmRelease with values from a Kubernetes secret
|
||||
kubectl -n app create secret generic my-secret-values \
|
||||
@@ -78,7 +79,7 @@ flux create helmrelease [name] [flags]
|
||||
--service-account string the name of the service account to impersonate when reconciling this HelmRelease
|
||||
--source helmChartSource source that contains the chart in the format '<kind>/<name>', where kind must be one of: (HelmRepository, GitRepository, Bucket)
|
||||
--target-namespace string namespace to install this release, defaults to the HelmRelease namespace
|
||||
--values string local path to the values.yaml file
|
||||
--values stringArray local path to values.yaml files
|
||||
--values-from helmReleaseValuesFrom Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', where kind must be one of: (Secret, ConfigMap)
|
||||
```
|
||||
|
||||
|
||||
@@ -18,10 +18,13 @@ flux create image policy <name> [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--filter-regex string regular expression pattern used to filter the image tags
|
||||
-h, --help help for policy
|
||||
--image-ref string the name of an image repository object
|
||||
--semver string a semver range to apply to tags; e.g., '1.x'
|
||||
--filter-extract string replacement pattern (using capture groups from --filter-regex) to use for sorting
|
||||
--filter-regex string regular expression pattern used to filter the image tags
|
||||
-h, --help help for policy
|
||||
--image-ref string the name of an image repository object
|
||||
--select-alpha string use alphabetical sorting to select image; either "asc" meaning select the last, or "desc" meaning select the first
|
||||
--select-numeric string use numeric sorting to select image; either "asc" meaning select the last, or "desc" meaning select the first
|
||||
--select-semver string a semver range to apply to tags; e.g., '1.x'
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -11,9 +11,40 @@ An ImageRepository object specifies an image repository to scan.
|
||||
flux create image repository <name> [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Create an ImageRepository object to scan the alpine image repository:
|
||||
flux create image repository alpine-repo --image alpine --interval 20m
|
||||
|
||||
# Create an image repository that uses an image pull secret (assumed to
|
||||
# have been created already):
|
||||
flux create image repository myapp-repo \
|
||||
--secret-ref image-pull \
|
||||
--image ghcr.io/example.com/myapp --interval 5m
|
||||
|
||||
# Create a TLS secret for a local image registry using a self-signed
|
||||
# host certificate, and use it to scan an image. ca.pem is a file
|
||||
# containing the CA certificate used to sign the host certificate.
|
||||
flux create secret tls local-registry-cert --ca-file ./ca.pem
|
||||
flux create image repository app-repo \
|
||||
--cert-secret-ref local-registry-cert \
|
||||
--image local-registry:5000/app --interval 5m
|
||||
|
||||
# Create a TLS secret with a client certificate and key, and use it
|
||||
# to scan a private image registry.
|
||||
flux create secret tls client-cert \
|
||||
--cert-file client.crt --key-file client.key
|
||||
flux create image repository app-repo \
|
||||
--cert-secret-ref client-cert \
|
||||
--image registry.example.com/private/app --interval 5m
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--cert-ref string the name of a secret to use for TLS certificates
|
||||
-h, --help help for repository
|
||||
--image string the image repository to scan; e.g., library/alpine
|
||||
--scan-timeout duration a timeout for scanning; this defaults to the interval if not set
|
||||
|
||||
@@ -30,4 +30,5 @@ The create source sub-commands generate Kubernetes secrets specific to Flux.
|
||||
* [flux create](flux_create.md) - Create or update sources and resources
|
||||
* [flux create secret git](flux_create_secret_git.md) - Create or update a Kubernetes secret for Git authentication
|
||||
* [flux create secret helm](flux_create_secret_helm.md) - Create or update a Kubernetes secret for Helm repository authentication
|
||||
* [flux create secret tls](flux_create_secret_tls.md) - Create or update a Kubernetes secret with TLS certificates
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ flux create secret git [name] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--ca-file string path to TLS CA file used for validating self-signed certificates
|
||||
-h, --help help for git
|
||||
-p, --password string basic authentication password
|
||||
--ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p256, p384, p521) (default p384)
|
||||
|
||||
56
docs/cmd/flux_create_secret_tls.md
Normal file
56
docs/cmd/flux_create_secret_tls.md
Normal file
@@ -0,0 +1,56 @@
|
||||
## flux create secret tls
|
||||
|
||||
Create or update a Kubernetes secret with TLS certificates
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
The create secret tls command generates a Kubernetes secret with certificates for use with TLS.
|
||||
|
||||
```
|
||||
flux create secret tls [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
|
||||
# Create a TLS secret on disk and encrypt it with Mozilla SOPS.
|
||||
# Files are expected to be PEM-encoded.
|
||||
flux create secret tls certs \
|
||||
--namespace=my-namespace \
|
||||
--cert-file=./client.crt \
|
||||
--key-file=./client.key \
|
||||
--export > certs.yaml
|
||||
|
||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||
--in-place certs.yaml
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--ca-file string TLS authentication CA file path
|
||||
--cert-file string TLS authentication cert file path
|
||||
-h, --help help for tls
|
||||
--key-file string TLS authentication key file path
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--export export in YAML format to stdout
|
||||
--interval duration source sync interval (default 1m0s)
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux create secret](flux_create_secret.md) - Create or update Kubernetes secrets
|
||||
|
||||
@@ -56,6 +56,7 @@ flux create source git [name] [flags]
|
||||
|
||||
```
|
||||
--branch string git branch (default "master")
|
||||
--ca-file string path to TLS CA file used for validating self-signed certificates, requires libgit2
|
||||
--git-implementation gitImplementation the Git implementation to use, available options are: (go-git, libgit2)
|
||||
-h, --help help for git
|
||||
-p, --password string basic authentication password
|
||||
|
||||
@@ -28,8 +28,8 @@ The delete sub-commands delete sources and resources.
|
||||
* [flux](flux.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
* [flux delete alert](flux_delete_alert.md) - Delete a Alert resource
|
||||
* [flux delete alert-provider](flux_delete_alert-provider.md) - Delete a Provider resource
|
||||
* [flux delete auto](flux_delete_auto.md) - Delete automation objects
|
||||
* [flux delete helmrelease](flux_delete_helmrelease.md) - Delete a HelmRelease resource
|
||||
* [flux delete image](flux_delete_image.md) - Delete image automation objects
|
||||
* [flux delete kustomization](flux_delete_kustomization.md) - Delete a Kustomization resource
|
||||
* [flux delete receiver](flux_delete_receiver.md) - Delete a Receiver resource
|
||||
* [flux delete source](flux_delete_source.md) - Delete sources
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
## flux delete auto
|
||||
## flux delete image
|
||||
|
||||
Delete automation objects
|
||||
Delete image automation objects
|
||||
|
||||
### Synopsis
|
||||
|
||||
The delete auto sub-commands delete automation objects.
|
||||
The delete image sub-commands delete image automation objects.
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for auto
|
||||
-h, --help help for image
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -26,7 +26,7 @@ The delete auto sub-commands delete automation objects.
|
||||
### SEE ALSO
|
||||
|
||||
* [flux delete](flux_delete.md) - Delete sources and resources
|
||||
* [flux delete auto image-policy](flux_delete_auto_image-policy.md) - Delete an ImagePolicy object
|
||||
* [flux delete auto image-repository](flux_delete_auto_image-repository.md) - Delete an ImageRepository object
|
||||
* [flux delete auto image-update](flux_delete_auto_image-update.md) - Delete an ImageUpdateAutomation object
|
||||
* [flux delete image policy](flux_delete_image_policy.md) - Delete an ImagePolicy object
|
||||
* [flux delete image repository](flux_delete_image_repository.md) - Delete an ImageRepository object
|
||||
* [flux delete image update](flux_delete_image_update.md) - Delete an ImageUpdateAutomation object
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
## flux delete auto image-policy
|
||||
## flux delete image policy
|
||||
|
||||
Delete an ImagePolicy object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The delete auto image-policy command deletes the given ImagePolicy from the cluster.
|
||||
The delete image policy command deletes the given ImagePolicy from the cluster.
|
||||
|
||||
```
|
||||
flux delete auto image-policy [name] [flags]
|
||||
flux delete image policy [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Delete an image policy
|
||||
flux delete auto image-policy alpine3.x
|
||||
flux delete image policy alpine3.x
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image-policy
|
||||
-h, --help help for policy
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -37,5 +37,5 @@ flux delete auto image-policy [name] [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux delete auto](flux_delete_auto.md) - Delete automation objects
|
||||
* [flux delete image](flux_delete_image.md) - Delete image automation objects
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
## flux delete auto image-repository
|
||||
## flux delete image repository
|
||||
|
||||
Delete an ImageRepository object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The delete auto image-repository command deletes the given ImageRepository from the cluster.
|
||||
The delete image repository command deletes the given ImageRepository from the cluster.
|
||||
|
||||
```
|
||||
flux delete auto image-repository [name] [flags]
|
||||
flux delete image repository [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Delete an image repository
|
||||
flux delete auto image-repository alpine
|
||||
flux delete image repository alpine
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image-repository
|
||||
-h, --help help for repository
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -37,5 +37,5 @@ flux delete auto image-repository [name] [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux delete auto](flux_delete_auto.md) - Delete automation objects
|
||||
* [flux delete image](flux_delete_image.md) - Delete image automation objects
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
## flux delete auto image-update
|
||||
## flux delete image update
|
||||
|
||||
Delete an ImageUpdateAutomation object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The delete auto image-update command deletes the given ImageUpdateAutomation from the cluster.
|
||||
The delete image update command deletes the given ImageUpdateAutomation from the cluster.
|
||||
|
||||
```
|
||||
flux delete auto image-update [name] [flags]
|
||||
flux delete image update [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Delete an image update automation
|
||||
flux delete auto image-update latest-images
|
||||
flux delete image update latest-images
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image-update
|
||||
-h, --help help for update
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -37,5 +37,5 @@ flux delete auto image-update [name] [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux delete auto](flux_delete_auto.md) - Delete automation objects
|
||||
* [flux delete image](flux_delete_image.md) - Delete image automation objects
|
||||
|
||||
@@ -29,7 +29,7 @@ The get sub-commands print the statuses of sources and resources.
|
||||
* [flux get alert-providers](flux_get_alert-providers.md) - Get Provider statuses
|
||||
* [flux get alerts](flux_get_alerts.md) - Get Alert statuses
|
||||
* [flux get helmreleases](flux_get_helmreleases.md) - Get HelmRelease statuses
|
||||
* [flux get image](flux_get_image.md) - Get image automation object status
|
||||
* [flux get images](flux_get_images.md) - Get image automation object status
|
||||
* [flux get kustomizations](flux_get_kustomizations.md) - Get Kustomization statuses
|
||||
* [flux get receivers](flux_get_receivers.md) - Get Receiver statuses
|
||||
* [flux get sources](flux_get_sources.md) - Get source statuses
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## flux get image
|
||||
## flux get images
|
||||
|
||||
Get image automation object status
|
||||
|
||||
@@ -9,7 +9,7 @@ The get image sub-commands print the status of image automation objects.
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image
|
||||
-h, --help help for images
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -26,7 +26,7 @@ The get image sub-commands print the status of image automation objects.
|
||||
### SEE ALSO
|
||||
|
||||
* [flux get](flux_get.md) - Get sources and resources
|
||||
* [flux get image policy](flux_get_image_policy.md) - Get ImagePolicy status
|
||||
* [flux get image repository](flux_get_image_repository.md) - Get ImageRepository status
|
||||
* [flux get image update](flux_get_image_update.md) - Get ImageUpdateAutomation status
|
||||
* [flux get images policy](flux_get_images_policy.md) - Get ImagePolicy status
|
||||
* [flux get images repository](flux_get_images_repository.md) - Get ImageRepository status
|
||||
* [flux get images update](flux_get_images_update.md) - Get ImageUpdateAutomation status
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## flux get image policy
|
||||
## flux get images policy
|
||||
|
||||
Get ImagePolicy status
|
||||
|
||||
@@ -7,7 +7,7 @@ Get ImagePolicy status
|
||||
The get image policy command prints the status of ImagePolicy objects.
|
||||
|
||||
```
|
||||
flux get image policy [flags]
|
||||
flux get images policy [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -40,5 +40,5 @@ flux get image policy [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux get image](flux_get_image.md) - Get image automation object status
|
||||
* [flux get images](flux_get_images.md) - Get image automation object status
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## flux get image repository
|
||||
## flux get images repository
|
||||
|
||||
Get ImageRepository status
|
||||
|
||||
@@ -7,7 +7,7 @@ Get ImageRepository status
|
||||
The get image repository command prints the status of ImageRepository objects.
|
||||
|
||||
```
|
||||
flux get image repository [flags]
|
||||
flux get images repository [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -40,5 +40,5 @@ flux get image repository [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux get image](flux_get_image.md) - Get image automation object status
|
||||
* [flux get images](flux_get_images.md) - Get image automation object status
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## flux get image update
|
||||
## flux get images update
|
||||
|
||||
Get ImageUpdateAutomation status
|
||||
|
||||
@@ -7,7 +7,7 @@ Get ImageUpdateAutomation status
|
||||
The get image update command prints the status of ImageUpdateAutomation objects.
|
||||
|
||||
```
|
||||
flux get image update [flags]
|
||||
flux get images update [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -40,5 +40,5 @@ flux get image update [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux get image](flux_get_image.md) - Get image automation object status
|
||||
* [flux get images](flux_get_images.md) - Get image automation object status
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
## flux install
|
||||
|
||||
Install the toolkit components
|
||||
Install or upgrade Flux
|
||||
|
||||
### Synopsis
|
||||
|
||||
The install command deploys the toolkit components in the specified namespace.
|
||||
The install command deploys Flux in the specified namespace.
|
||||
If a previous version is installed, then an in-place upgrade will be performed.
|
||||
|
||||
```
|
||||
@@ -17,9 +17,12 @@ flux install [flags]
|
||||
# Install the latest version in the flux-system namespace
|
||||
flux install --version=latest --namespace=flux-system
|
||||
|
||||
# Dry-run install for a specific version and a series of components
|
||||
# Install a specific version and a series of components
|
||||
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
|
||||
|
||||
# Install Flux onto tainted Kubernetes nodes
|
||||
flux install --toleration-keys=node.kubernetes.io/dedicated-to-flux
|
||||
|
||||
# Dry-run install with manifests preview
|
||||
flux install --dry-run --verbose
|
||||
|
||||
@@ -41,7 +44,8 @@ flux install [flags]
|
||||
--log-level logLevel log level, available options are: (debug, info, error) (default info)
|
||||
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
|
||||
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
||||
-v, --version string toolkit version (default "latest")
|
||||
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints
|
||||
-v, --version string toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases
|
||||
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
||||
```
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
## flux uninstall
|
||||
|
||||
Uninstall the toolkit components
|
||||
Uninstall Flux and its custom resource definitions
|
||||
|
||||
### Synopsis
|
||||
|
||||
The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.
|
||||
The uninstall command removes the Flux components and the toolkit.fluxcd.io resources from the cluster.
|
||||
|
||||
```
|
||||
flux uninstall [flags]
|
||||
@@ -13,22 +13,21 @@ flux uninstall [flags]
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Dry-run uninstall of all components
|
||||
flux uninstall --dry-run --namespace=flux-system
|
||||
# Uninstall Flux components, its custom resources and namespace
|
||||
flux uninstall --namespace=flux-system
|
||||
|
||||
# Uninstall all components and delete custom resource definitions
|
||||
flux uninstall --resources --crds --namespace=flux-system
|
||||
# Uninstall Flux but keep the namespace
|
||||
flux uninstall --namespace=infra --keep-namespace=true
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--crds removes all CRDs previously installed
|
||||
--dry-run only print the object that would be deleted
|
||||
-h, --help help for uninstall
|
||||
--resources removes custom resources such as Kustomizations, GitRepositories and HelmRepositories (default true)
|
||||
-s, --silent delete components without asking for confirmation
|
||||
--dry-run only print the objects that would be deleted
|
||||
-h, --help help for uninstall
|
||||
--keep-namespace skip namespace deletion
|
||||
-s, --silent delete components without asking for confirmation
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -20,6 +20,7 @@ Features:
|
||||
- Runs Helm install/upgrade in a specific order, taking into account the depends-on relationship defined in a set of `HelmRelease` objects
|
||||
- Prunes Helm releases removed from cluster (garbage collection)
|
||||
- Reports Helm releases statuses (alerting provided by [notification-controller](../notification/controller.md))
|
||||
- Built-in Kustomize compatible Helm post renderer, providing support for strategic merge, JSON 6902 and images patches
|
||||
|
||||
Links:
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user