Compare commits
218 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
700cef0989 | ||
|
|
3ed3e553e7 | ||
|
|
d68158ddc9 | ||
|
|
9f83a69242 | ||
|
|
bf69dbd43d | ||
|
|
465ea5ccfd | ||
|
|
92ef39e2ad | ||
|
|
0404790df9 | ||
|
|
f880e93df4 | ||
|
|
4697b1101d | ||
|
|
50ff2accd2 | ||
|
|
c7d876eb8f | ||
|
|
eda392dfcd | ||
|
|
3b91e14f6d | ||
|
|
17e3c57d7e | ||
|
|
1c744a0f97 | ||
|
|
99bdb20aeb | ||
|
|
fbe7050cb8 | ||
|
|
12ea028aa9 | ||
|
|
ea62cb5fc9 | ||
|
|
d27c2164b2 | ||
|
|
ef8f5cb87d | ||
|
|
378a2c2a0e | ||
|
|
2597ad0f73 | ||
|
|
0df34bed59 | ||
|
|
be65cf8052 | ||
|
|
8922753591 | ||
|
|
87e11ed653 | ||
|
|
ab34771b3d | ||
|
|
e733c4f55a | ||
|
|
37b60666c4 | ||
|
|
734d736bdf | ||
|
|
8ca65059f7 | ||
|
|
086f174463 | ||
|
|
48fd70fc09 | ||
|
|
606266e976 | ||
|
|
f7006a8172 | ||
|
|
653dcc8d78 | ||
|
|
8df140c713 | ||
|
|
089af9cc90 | ||
|
|
695fb55b13 | ||
|
|
ec21eedd56 | ||
|
|
5ba3774fd5 | ||
|
|
12a2100fcf | ||
|
|
68074d3543 | ||
|
|
18849e36c7 | ||
|
|
671fe274da | ||
|
|
af1d9102b9 | ||
|
|
9dc10ef7d1 | ||
|
|
86a3cf20e7 | ||
|
|
27a42ecd8e | ||
|
|
ae7a59fbb4 | ||
|
|
598dfc32e8 | ||
|
|
a40d124e23 | ||
|
|
9df3fcab18 | ||
|
|
b6ce969d1b | ||
|
|
6a37649ee6 | ||
|
|
8926095660 | ||
|
|
bd34870334 | ||
|
|
a56ce1f867 | ||
|
|
dab5bbd393 | ||
|
|
15ebfd7eb6 | ||
|
|
5ab8dd2557 | ||
|
|
9164914d16 | ||
|
|
c9e0bc0807 | ||
|
|
61439adf9b | ||
|
|
e4d7450643 | ||
|
|
0fbcfded57 | ||
|
|
fb3a434f95 | ||
|
|
4f66da84d6 | ||
|
|
b67e8aafab | ||
|
|
0b4f1d30a6 | ||
|
|
c494e6bf7e | ||
|
|
b8c57c7901 | ||
|
|
6aed4631e7 | ||
|
|
5df9118365 | ||
|
|
4a4af94d6c | ||
|
|
baa54fb84a | ||
|
|
cb6470f817 | ||
|
|
1a904e138f | ||
|
|
3b482529ff | ||
|
|
67997437db | ||
|
|
a5541eddca | ||
|
|
203157e525 | ||
|
|
655c2261ba | ||
|
|
45e446eb00 | ||
|
|
68abe37648 | ||
|
|
df6a0a3762 | ||
|
|
c35bae577f | ||
|
|
3567941eda | ||
|
|
3c95fe6380 | ||
|
|
d07f0d003c | ||
|
|
86774309db | ||
|
|
427f23e32d | ||
|
|
70cddde16c | ||
|
|
e86789b643 | ||
|
|
f52fec66bd | ||
|
|
f4926d1e45 | ||
|
|
2774c1a5cd | ||
|
|
8274bc0ea3 | ||
|
|
e9531e4d57 | ||
|
|
3a8aad7e5c | ||
|
|
ef079c5b58 | ||
|
|
50332aa2ee | ||
|
|
b47f3a57dc | ||
|
|
9a928744cc | ||
|
|
328d403507 | ||
|
|
76ffd76bd3 | ||
|
|
951589e652 | ||
|
|
7bb0704401 | ||
|
|
3aa45e72e7 | ||
|
|
67691e92e3 | ||
|
|
43388ec67b | ||
|
|
2fe3934491 | ||
|
|
f60ba95b4c | ||
|
|
4ecf541748 | ||
|
|
7994829765 | ||
|
|
ce14951436 | ||
|
|
a5ce8221a3 | ||
|
|
e6344ef18e | ||
|
|
99e60634ad | ||
|
|
6c656b7366 | ||
|
|
fc3a09b5ad | ||
|
|
b1484f2f24 | ||
|
|
7dcf884e38 | ||
|
|
b6d349da8c | ||
|
|
40ce3d50c2 | ||
|
|
68046067c5 | ||
|
|
e3b12a8a24 | ||
|
|
f123b9d3cb | ||
|
|
f4ce89ae26 | ||
|
|
ea451e7e49 | ||
|
|
d434575047 | ||
|
|
e627634184 | ||
|
|
e0dd12505f | ||
|
|
5a67f94380 | ||
|
|
5f9dd7a5a5 | ||
|
|
bce0da2806 | ||
|
|
a58c40f2d7 | ||
|
|
65d5cadf29 | ||
|
|
1ea5d4d2e3 | ||
|
|
719ef3c44c | ||
|
|
f4adfc3029 | ||
|
|
d8d08091cc | ||
|
|
de4b3ef3dc | ||
|
|
7bd6aedb73 | ||
|
|
fffe40fbd4 | ||
|
|
74feda73af | ||
|
|
8b5583930e | ||
|
|
466fdae70e | ||
|
|
054a62fb30 | ||
|
|
c694b570e0 | ||
|
|
4204ec1d43 | ||
|
|
6d4e37ccb2 | ||
|
|
eef06c993e | ||
|
|
58362fbbb9 | ||
|
|
b872e595ae | ||
|
|
18c3f79319 | ||
|
|
8f0cd35d7a | ||
|
|
c8bcf19f32 | ||
|
|
5bee3047ac | ||
|
|
0d2f6bf02d | ||
|
|
7481c6beb0 | ||
|
|
4ece12348b | ||
|
|
e65a5beaae | ||
|
|
ef576128e3 | ||
|
|
7f0bc2ada2 | ||
|
|
96c373d045 | ||
|
|
22648cae3b | ||
|
|
f57ce14754 | ||
|
|
1d3a381389 | ||
|
|
9055e753a9 | ||
|
|
6390812cbb | ||
|
|
fa46f05423 | ||
|
|
6b0ffe0b13 | ||
|
|
e724d90202 | ||
|
|
6129943685 | ||
|
|
d4e37cbda5 | ||
|
|
cccfb3a560 | ||
|
|
d0403038ed | ||
|
|
a5a7d7970f | ||
|
|
62b9377f15 | ||
|
|
ec2c71f9ef | ||
|
|
b54fd2c6b3 | ||
|
|
e5066c3712 | ||
|
|
fd1c038303 | ||
|
|
64e7a857b8 | ||
|
|
7da24932ab | ||
|
|
1a2ea8407b | ||
|
|
32f94bab97 | ||
|
|
dea4a67639 | ||
|
|
236ffd1767 | ||
|
|
1b2ffad2f1 | ||
|
|
4750d0d81c | ||
|
|
63a210a0b2 | ||
|
|
465eaa24d3 | ||
|
|
c23e8c7ee1 | ||
|
|
974f01cb46 | ||
|
|
16fa9b2753 | ||
|
|
9deab1c415 | ||
|
|
7c01eeb115 | ||
|
|
998f0c7d53 | ||
|
|
bd41406aaa | ||
|
|
f17801753d | ||
|
|
92891fd340 | ||
|
|
aa122455f7 | ||
|
|
880e70c19c | ||
|
|
968f249562 | ||
|
|
bb9f476be2 | ||
|
|
276f43fdeb | ||
|
|
150d1c2a5a | ||
|
|
1bf3814701 | ||
|
|
166181c745 | ||
|
|
bfff977d41 | ||
|
|
787d755261 | ||
|
|
acf7173959 | ||
|
|
e6132e36ba | ||
|
|
585b97c462 |
9
.github/workflows/bootstrap.yaml
vendored
9
.github/workflows/bootstrap.yaml
vendored
@@ -47,7 +47,8 @@ jobs:
|
|||||||
--owner=fluxcd-testing \
|
--owner=fluxcd-testing \
|
||||||
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
||||||
--branch=main \
|
--branch=main \
|
||||||
--path=test-cluster
|
--path=test-cluster \
|
||||||
|
--team=team-z
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
- name: bootstrap no-op
|
- name: bootstrap no-op
|
||||||
@@ -56,7 +57,8 @@ jobs:
|
|||||||
--owner=fluxcd-testing \
|
--owner=fluxcd-testing \
|
||||||
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
||||||
--branch=main \
|
--branch=main \
|
||||||
--path=test-cluster
|
--path=test-cluster \
|
||||||
|
--team=team-z
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
- name: uninstall
|
- name: uninstall
|
||||||
@@ -69,7 +71,8 @@ jobs:
|
|||||||
--owner=fluxcd-testing \
|
--owner=fluxcd-testing \
|
||||||
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
||||||
--branch=main \
|
--branch=main \
|
||||||
--path=test-cluster
|
--path=test-cluster \
|
||||||
|
--team=team-z
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
- name: delete repository
|
- name: delete repository
|
||||||
|
|||||||
76
.github/workflows/docs.yaml
vendored
76
.github/workflows/docs.yaml
vendored
@@ -1,76 +0,0 @@
|
|||||||
name: Publish docs via GitHub Pages
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ 'docs*', main ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Deploy docs
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout master
|
|
||||||
uses: actions/checkout@v1
|
|
||||||
- name: Copy assets
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
controller_version() {
|
|
||||||
sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
# source-controller CRDs
|
|
||||||
SOURCE_VER=$(controller_version source-controller)
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/api/source.md" > docs/components/source/api.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/gitrepositories.md" > docs/components/source/gitrepositories.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/helmrepositories.md" > docs/components/source/helmrepositories.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/helmcharts.md" > docs/components/source/helmcharts.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/buckets.md" > docs/components/source/buckets.md
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
# kustomize-controller CRDs
|
|
||||||
KUSTOMIZE_VER=$(controller_version kustomize-controller)
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/kustomize-controller/$KUSTOMIZE_VER/docs/api/kustomize.md" > docs/components/kustomize/api.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/kustomize-controller/$KUSTOMIZE_VER/docs/spec/v1beta1/kustomization.md" > docs/components/kustomize/kustomization.md
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
# helm-controller CRDs
|
|
||||||
HELM_VER=$(controller_version helm-controller)
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/helm-controller/$HELM_VER/docs/api/helmrelease.md" > docs/components/helm/api.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/helm-controller/$HELM_VER/docs/spec/v2beta1/helmreleases.md" > docs/components/helm/helmreleases.md
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
# notification-controller CRDs
|
|
||||||
NOTIFICATION_VER=$(controller_version notification-controller)
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/api/notification.md" > docs/components/notification/api.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/event.md" > docs/components/notification/event.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/alert.md" > docs/components/notification/alert.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/provider.md" > docs/components/notification/provider.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/receiver.md" > docs/components/notification/receiver.md
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
# image-*-controller CRDs; these use the same API group
|
|
||||||
IMG_REFL_VER=$(controller_version image-reflector-controller)
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/api/image-reflector.md" > docs/components/image/reflector-api.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/spec/v1alpha1/imagerepositories.md" > docs/components/image/imagerepositories.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/spec/v1alpha1/imagepolicies.md" > docs/components/image/imagepolicies.md
|
|
||||||
|
|
||||||
IMG_AUTO_VER=$(controller_version image-automation-controller)
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-automation-controller/$IMG_AUTO_VER/docs/api/image-automation.md" > docs/components/image/automation-api.md
|
|
||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-automation-controller/$IMG_AUTO_VER/docs/spec/v1alpha1/imageupdateautomations.md" > docs/components/image/imageupdateautomations.md
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
# install script
|
|
||||||
cp install/flux.sh docs/install.sh
|
|
||||||
}
|
|
||||||
- name: Deploy docs
|
|
||||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
CUSTOM_DOMAIN: toolkit.fluxcd.io
|
|
||||||
5
.github/workflows/e2e.yaml
vendored
5
.github/workflows/e2e.yaml
vendored
@@ -121,7 +121,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
/tmp/flux create hr podinfo-helm \
|
/tmp/flux create hr podinfo-helm \
|
||||||
--target-namespace=default \
|
--target-namespace=default \
|
||||||
--source=HelmRepository/podinfo \
|
--source=HelmRepository/podinfo.flux-system \
|
||||||
--chart=podinfo \
|
--chart=podinfo \
|
||||||
--chart-version=">4.0.0 <5.0.0"
|
--chart-version=">4.0.0 <5.0.0"
|
||||||
- name: flux create helmrelease --source=GitRepository/podinfo
|
- name: flux create helmrelease --source=GitRepository/podinfo
|
||||||
@@ -188,7 +188,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
/tmp/flux create source git flux-system \
|
/tmp/flux create source git flux-system \
|
||||||
--url=https://github.com/fluxcd/flux2-kustomize-helm-example \
|
--url=https://github.com/fluxcd/flux2-kustomize-helm-example \
|
||||||
--branch=main
|
--branch=main \
|
||||||
|
--recurse-submodules
|
||||||
/tmp/flux create kustomization flux-system \
|
/tmp/flux create kustomization flux-system \
|
||||||
--source=flux-system \
|
--source=flux-system \
|
||||||
--path=./clusters/staging
|
--path=./clusters/staging
|
||||||
|
|||||||
31
.github/workflows/release.yaml
vendored
31
.github/workflows/release.yaml
vendored
@@ -16,6 +16,26 @@ jobs:
|
|||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.x
|
go-version: 1.16.x
|
||||||
|
- name: Setup QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
with:
|
||||||
|
platforms: all
|
||||||
|
- name: Setup Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
with:
|
||||||
|
buildkitd-flags: "--debug"
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: fluxcdbot
|
||||||
|
password: ${{ secrets.GHCR_TOKEN }}
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: fluxcdbot
|
||||||
|
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||||
- name: Download release notes utility
|
- name: Download release notes utility
|
||||||
env:
|
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
|
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
|
||||||
@@ -33,6 +53,17 @@ jobs:
|
|||||||
make cmd/flux/manifests
|
make cmd/flux/manifests
|
||||||
./manifests/scripts/bundle.sh "" ./output manifests.tar.gz
|
./manifests/scripts/bundle.sh "" ./output manifests.tar.gz
|
||||||
kustomize build ./manifests/install > ./output/install.yaml
|
kustomize build ./manifests/install > ./output/install.yaml
|
||||||
|
- name: Build CRDs
|
||||||
|
run: |
|
||||||
|
kustomize build manifests/crds > all-crds.yaml
|
||||||
|
- name: Generate OpenAPI JSON schemas from CRDs
|
||||||
|
uses: fluxcd/pkg//actions/crdjsonschema@main
|
||||||
|
with:
|
||||||
|
crd: all-crds.yaml
|
||||||
|
output: schemas
|
||||||
|
- name: Archive the OpenAPI JSON schemas
|
||||||
|
run: |
|
||||||
|
tar -czvf ./output/crd-schemas.tar.gz -C schemas .
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v1
|
uses: goreleaser/goreleaser-action@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
65
.github/workflows/update.yaml
vendored
65
.github/workflows/update.yaml
vendored
@@ -23,20 +23,32 @@ jobs:
|
|||||||
PR_BODY=""
|
PR_BODY=""
|
||||||
|
|
||||||
bump_version() {
|
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 LATEST_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\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
|
local CTRL_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
|
||||||
|
local CRD_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p" manifests/crds/kustomization.yaml)
|
||||||
|
local MOD_VERSION=$(go list -m -f '{{ .Version }}' "github.com/fluxcd/$1/api")
|
||||||
|
|
||||||
if [[ "${RELEASE_VERSION}" != "${CURRENT_VERSION}" ]]; then
|
local changed=false
|
||||||
# bump kustomize
|
|
||||||
sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${RELEASE_VERSION}\2/g" "manifests/bases/$1/kustomization.yaml"
|
|
||||||
|
|
||||||
if [[ ! -z $(grep "github.com/fluxcd/$1/api" go.mod | awk '{print $2}') ]]; then
|
if [[ "${CTRL_VERSION}" != "${LATEST_VERSION}" ]]; then
|
||||||
# bump go mod
|
sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${LATEST_VERSION}\2/g" "manifests/bases/$1/kustomization.yaml"
|
||||||
go mod edit -require="github.com/fluxcd/$1/api@${RELEASE_VERSION}"
|
changed=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# NB: special URL encoded formatting required for newlines
|
if [[ "${CRD_VERSION}" != "${LATEST_VERSION}" ]]; then
|
||||||
PR_BODY="$PR_BODY- $1 to ${RELEASE_VERSION}%0A https://github.com/fluxcd/$1/blob/${RELEASE_VERSION}/CHANGELOG.md%0A"
|
sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${LATEST_VERSION}\2/g" "manifests/crds/kustomization.yaml"
|
||||||
|
changed=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${MOD_VERSION}" != "${LATEST_VERSION}" ]]; then
|
||||||
|
go mod edit -require="github.com/fluxcd/$1/api@${LATEST_VERSION}"
|
||||||
|
rm go.sum
|
||||||
|
go mod tidy
|
||||||
|
changed=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$changed" == true ]]; then
|
||||||
|
PR_BODY="$PR_BODY- $1 to ${LATEST_VERSION}%0A https://github.com/fluxcd/$1/blob/${LATEST_VERSION}/CHANGELOG.md%0A"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,9 +61,6 @@ jobs:
|
|||||||
bump_version image-reflector-controller
|
bump_version image-reflector-controller
|
||||||
bump_version image-automation-controller
|
bump_version image-automation-controller
|
||||||
|
|
||||||
# add missing and remove unused modules
|
|
||||||
go mod tidy
|
|
||||||
|
|
||||||
# diff change
|
# diff change
|
||||||
git diff
|
git diff
|
||||||
|
|
||||||
@@ -63,21 +72,21 @@ jobs:
|
|||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||||
commit-message: |
|
commit-message: |
|
||||||
Update toolkit components
|
Update toolkit components
|
||||||
|
|
||||||
${{ steps.update.outputs.pr_body }}
|
${{ steps.update.outputs.pr_body }}
|
||||||
committer: GitHub <noreply@github.com>
|
committer: GitHub <noreply@github.com>
|
||||||
author: fluxcdbot <fluxcdbot@users.noreply.github.com>
|
author: fluxcdbot <fluxcdbot@users.noreply.github.com>
|
||||||
signoff: true
|
signoff: true
|
||||||
branch: update-components
|
branch: update-components
|
||||||
title: Update toolkit components
|
title: Update toolkit components
|
||||||
body: |
|
body: |
|
||||||
${{ steps.update.outputs.pr_body }}
|
${{ steps.update.outputs.pr_body }}
|
||||||
labels: |
|
labels: |
|
||||||
area/build
|
area/build
|
||||||
reviewers: ${{ secrets.ASSIGNEES }}
|
reviewers: ${{ secrets.ASSIGNEES }}
|
||||||
|
|
||||||
- name: Check output
|
- name: Check output
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -11,8 +11,14 @@
|
|||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
|
# Release
|
||||||
|
dist/
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
bin/
|
bin/
|
||||||
output/
|
output/
|
||||||
cmd/flux/manifests/
|
cmd/flux/manifests/
|
||||||
|
|
||||||
|
# Docs
|
||||||
|
site/
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ brews:
|
|||||||
name: homebrew-tap
|
name: homebrew-tap
|
||||||
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
|
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
|
||||||
folder: Formula
|
folder: Formula
|
||||||
homepage: "https://toolkit.fluxcd.io/"
|
homepage: "https://fluxcd.io/"
|
||||||
description: "Flux CLI"
|
description: "Flux CLI"
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: kubectl
|
- name: kubectl
|
||||||
@@ -72,5 +72,67 @@ publishers:
|
|||||||
.github/aur/flux-go/publish.sh {{ .Version }}
|
.github/aur/flux-go/publish.sh {{ .Version }}
|
||||||
release:
|
release:
|
||||||
extra_files:
|
extra_files:
|
||||||
|
- glob: ./output/crd-schemas.tar.gz
|
||||||
- glob: ./output/manifests.tar.gz
|
- glob: ./output/manifests.tar.gz
|
||||||
- glob: ./output/install.yaml
|
- glob: ./output/install.yaml
|
||||||
|
dockers:
|
||||||
|
- image_templates:
|
||||||
|
- 'fluxcd/flux-cli:{{ .Tag }}-amd64'
|
||||||
|
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-amd64'
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
use_buildx: true
|
||||||
|
goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--build-arg=ARCH=linux/amd64"
|
||||||
|
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||||
|
- "--label=org.opencontainers.image.name={{ .ProjectName }}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||||
|
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||||
|
- "--label=org.opencontainers.image.source={{ .GitURL }}"
|
||||||
|
- "--platform=linux/amd64"
|
||||||
|
- image_templates:
|
||||||
|
- 'fluxcd/flux-cli:{{ .Tag }}-arm64'
|
||||||
|
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm64'
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
use_buildx: true
|
||||||
|
goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--build-arg=ARCH=linux/arm64"
|
||||||
|
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||||
|
- "--label=org.opencontainers.image.name={{ .ProjectName }}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||||
|
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||||
|
- "--label=org.opencontainers.image.source={{ .GitURL }}"
|
||||||
|
- "--platform=linux/arm64"
|
||||||
|
- image_templates:
|
||||||
|
- 'fluxcd/flux-cli:{{ .Tag }}-arm'
|
||||||
|
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm'
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
use_buildx: true
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--build-arg=ARCH=linux/arm"
|
||||||
|
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||||
|
- "--label=org.opencontainers.image.name={{ .ProjectName }}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||||
|
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||||
|
- "--label=org.opencontainers.image.source={{ .GitURL }}"
|
||||||
|
- "--platform=linux/arm/v7"
|
||||||
|
docker_manifests:
|
||||||
|
- name_template: 'fluxcd/flux-cli:{{ .Tag }}'
|
||||||
|
image_templates:
|
||||||
|
- 'fluxcd/flux-cli:{{ .Tag }}-amd64'
|
||||||
|
- 'fluxcd/flux-cli:{{ .Tag }}-arm64'
|
||||||
|
- 'fluxcd/flux-cli:{{ .Tag }}-arm'
|
||||||
|
- name_template: 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}'
|
||||||
|
image_templates:
|
||||||
|
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-amd64'
|
||||||
|
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm64'
|
||||||
|
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm'
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ This project is composed of:
|
|||||||
### Understanding the code
|
### Understanding the code
|
||||||
|
|
||||||
To get started with developing controllers, you might want to review
|
To get started with developing controllers, you might want to review
|
||||||
[our guide](https://toolkit.fluxcd.io/dev-guides/source-watcher/) which
|
[our guide](https://fluxcd.io/docs/gitops-toolkit/source-watcher/) which
|
||||||
walks you through writing a short and concise controller that watches out
|
walks you through writing a short and concise controller that watches out
|
||||||
for source changes.
|
for source changes.
|
||||||
|
|
||||||
|
|||||||
23
Dockerfile
Normal file
23
Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM alpine:3.13 as builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates curl
|
||||||
|
|
||||||
|
ARG ARCH=linux/amd64
|
||||||
|
ARG KUBECTL_VER=1.20.4
|
||||||
|
|
||||||
|
RUN curl -sL https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \
|
||||||
|
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl && \
|
||||||
|
kubectl version --client=true
|
||||||
|
|
||||||
|
FROM alpine:3.13 as flux-cli
|
||||||
|
|
||||||
|
# Create minimal nsswitch.conf file to prioritize the usage of /etc/hosts over DNS queries.
|
||||||
|
# https://github.com/gliderlabs/docker-alpine/issues/367#issuecomment-354316460
|
||||||
|
RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates
|
||||||
|
|
||||||
|
COPY --from=builder /usr/local/bin/kubectl /usr/local/bin/
|
||||||
|
COPY --chmod=755 flux /usr/local/bin/
|
||||||
|
|
||||||
|
ENTRYPOINT [ "flux" ]
|
||||||
7
Makefile
7
Makefile
@@ -14,7 +14,7 @@ fmt:
|
|||||||
vet:
|
vet:
|
||||||
go vet ./...
|
go vet ./...
|
||||||
|
|
||||||
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet docs
|
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet
|
||||||
go test ./... -coverprofile cover.out
|
go test ./... -coverprofile cover.out
|
||||||
|
|
||||||
$(EMBEDDED_MANIFESTS_TARGET): $(call rwildcard,manifests/,*.yaml *.json)
|
$(EMBEDDED_MANIFESTS_TARGET): $(call rwildcard,manifests/,*.yaml *.json)
|
||||||
@@ -26,10 +26,5 @@ build: $(EMBEDDED_MANIFESTS_TARGET)
|
|||||||
install:
|
install:
|
||||||
go install cmd/flux
|
go install cmd/flux
|
||||||
|
|
||||||
.PHONY: docs
|
|
||||||
docs:
|
|
||||||
rm -rf docs/cmd/*
|
|
||||||
mkdir -p ./docs/cmd && go run ./cmd/flux/ docgen
|
|
||||||
|
|
||||||
install-dev:
|
install-dev:
|
||||||
CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/flux
|
CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/flux
|
||||||
|
|||||||
58
README.md
58
README.md
@@ -1,5 +1,6 @@
|
|||||||
# Flux version 2
|
# Flux version 2
|
||||||
|
|
||||||
|
[](https://bestpractices.coreinfrastructure.org/projects/4782)
|
||||||
[](https://github.com/fluxcd/flux2/actions)
|
[](https://github.com/fluxcd/flux2/actions)
|
||||||
[](https://goreportcard.com/report/github.com/fluxcd/flux2)
|
[](https://goreportcard.com/report/github.com/fluxcd/flux2)
|
||||||
[](https://github.com/fluxcd/flux2/blob/main/LICENSE)
|
[](https://github.com/fluxcd/flux2/blob/main/LICENSE)
|
||||||
@@ -30,7 +31,7 @@ brew install fluxcd/tap/flux
|
|||||||
With Bash:
|
With Bash:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
|
curl -s https://fluxcd.io/install.sh | sudo bash
|
||||||
|
|
||||||
# enable completions in ~/.bash_profile
|
# enable completions in ~/.bash_profile
|
||||||
. <(flux completion bash)
|
. <(flux completion bash)
|
||||||
@@ -48,6 +49,11 @@ Arch Linux (AUR) packages:
|
|||||||
Binaries for macOS, Windows and Linux AMD64/ARM are available to download on the
|
Binaries for macOS, Windows and Linux AMD64/ARM are available to download on the
|
||||||
[release page](https://github.com/fluxcd/flux2/releases).
|
[release page](https://github.com/fluxcd/flux2/releases).
|
||||||
|
|
||||||
|
A container image with `kubectl` and `flux` is available on Docker Hub and GitHub:
|
||||||
|
|
||||||
|
* `docker.io/fluxcd/flux-cli:<version>`
|
||||||
|
* `ghcr.io/fluxcd/flux-cli:<version>`
|
||||||
|
|
||||||
Verify that your cluster satisfies the prerequisites with:
|
Verify that your cluster satisfies the prerequisites with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -57,13 +63,13 @@ flux check --pre
|
|||||||
## Get started
|
## Get started
|
||||||
|
|
||||||
To get started with Flux, start [browsing the
|
To get started with Flux, start [browsing the
|
||||||
documentation](https://toolkit.fluxcd.io) or get started with one of
|
documentation](https://fluxcd.io/docs/) or get started with one of
|
||||||
the following guides:
|
the following guides:
|
||||||
|
|
||||||
- [Get started with Flux](https://toolkit.fluxcd.io/get-started/)
|
- [Get started with Flux](https://fluxcd.io/docs/get-started/)
|
||||||
- [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/)
|
- [Manage Helm Releases](https://fluxcd.io/docs/guides/helmreleases/)
|
||||||
- [Automate image updates to Git](https://toolkit.fluxcd.io/guides/image-update/)
|
- [Automate image updates to Git](https://fluxcd.io/docs/guides/image-update/)
|
||||||
- [Manage Kubernetes secrets with Mozilla SOPS](https://toolkit.fluxcd.io/guides/mozilla-sops/)
|
- [Manage Kubernetes secrets with Mozilla SOPS](https://fluxcd.io/docs/guides/mozilla-sops/)
|
||||||
|
|
||||||
If you need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
If you need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
||||||
|
|
||||||
@@ -74,31 +80,31 @@ runtime for Flux v2. The APIs comprise Kubernetes custom resources,
|
|||||||
which can be created and updated by a cluster user, or by other
|
which can be created and updated by a cluster user, or by other
|
||||||
automation tooling.
|
automation tooling.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
You can use the toolkit to extend Flux, or to build your own systems
|
You can use the toolkit to extend Flux, or to build your own systems
|
||||||
for continuous delivery -- see [the developer
|
for continuous delivery -- see [the developer
|
||||||
guides](https://toolkit.fluxcd.io/dev-guides/source-watcher/).
|
guides](https://fluxcd.io/docs/gitops-toolkit/source-watcher/).
|
||||||
|
|
||||||
### Components
|
### Components
|
||||||
|
|
||||||
- [Source Controller](https://toolkit.fluxcd.io/components/source/controller/)
|
- [Source Controller](https://fluxcd.io/docs/components/source/)
|
||||||
- [GitRepository CRD](https://toolkit.fluxcd.io/components/source/gitrepositories/)
|
- [GitRepository CRD](https://fluxcd.io/docs/components/source/gitrepositories/)
|
||||||
- [HelmRepository CRD](https://toolkit.fluxcd.io/components/source/helmrepositories/)
|
- [HelmRepository CRD](https://fluxcd.io/docs/components/source/helmrepositories/)
|
||||||
- [HelmChart CRD](https://toolkit.fluxcd.io/components/source/helmcharts/)
|
- [HelmChart CRD](https://fluxcd.io/docs/components/source/helmcharts/)
|
||||||
- [Bucket CRD](https://toolkit.fluxcd.io/components/source/buckets/)
|
- [Bucket CRD](https://fluxcd.io/docs/components/source/buckets/)
|
||||||
- [Kustomize Controller](https://toolkit.fluxcd.io/components/kustomize/controller/)
|
- [Kustomize Controller](https://fluxcd.io/docs/components/kustomize/)
|
||||||
- [Kustomization CRD](https://toolkit.fluxcd.io/components/kustomize/kustomization/)
|
- [Kustomization CRD](https://fluxcd.io/docs/components/kustomize/kustomization/)
|
||||||
- [Helm Controller](https://toolkit.fluxcd.io/components/helm/controller/)
|
- [Helm Controller](https://fluxcd.io/docs/components/helm/)
|
||||||
- [HelmRelease CRD](https://toolkit.fluxcd.io/components/helm/helmreleases/)
|
- [HelmRelease CRD](https://fluxcd.io/docs/components/helm/helmreleases/)
|
||||||
- [Notification Controller](https://toolkit.fluxcd.io/components/notification/controller/)
|
- [Notification Controller](https://fluxcd.io/docs/components/notification/)
|
||||||
- [Provider CRD](https://toolkit.fluxcd.io/components/notification/provider/)
|
- [Provider CRD](https://fluxcd.io/docs/components/notification/provider/)
|
||||||
- [Alert CRD](https://toolkit.fluxcd.io/components/notification/alert/)
|
- [Alert CRD](https://fluxcd.io/docs/components/notification/alert/)
|
||||||
- [Receiver CRD](https://toolkit.fluxcd.io/components/notification/receiver/)
|
- [Receiver CRD](https://fluxcd.io/docs/components/notification/receiver/)
|
||||||
- [Image Automation Controllers](https://toolkit.fluxcd.io/components/image/controller/)
|
- [Image Automation Controllers](https://fluxcd.io/docs/components/image/)
|
||||||
- [ImageRepository CRD](https://toolkit.fluxcd.io/components/image/imagerepositories/)
|
- [ImageRepository CRD](https://fluxcd.io/docs/components/image/imagerepositories/)
|
||||||
- [ImagePolicy CRD](https://toolkit.fluxcd.io/components/image/imagepolicies/)
|
- [ImagePolicy CRD](https://fluxcd.io/docs/components/image/imagepolicies/)
|
||||||
- [ImageUpdateAutomation CRD](https://toolkit.fluxcd.io/components/image/imageupdateautomations/)
|
- [ImageUpdateAutomation CRD](https://fluxcd.io/docs/components/image/imageupdateautomations/)
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
@@ -106,7 +112,7 @@ Need help or want to contribute? Please see the links below. The Flux project is
|
|||||||
new contributors and there are a multitude of ways to get involved.
|
new contributors and there are a multitude of ways to get involved.
|
||||||
|
|
||||||
- Getting Started?
|
- Getting Started?
|
||||||
- Look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
|
- Look at our [Get Started guide](https://fluxcd.io/docs/get-started/) and give us feedback
|
||||||
- Need help?
|
- Need help?
|
||||||
- First: Ask questions on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
- 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/)
|
- Second: Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
||||||
|
|||||||
52
cmd/flux/alert.go
Normal file
52
cmd/flux/alert.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// notificationv1.Alert
|
||||||
|
|
||||||
|
var alertType = apiType{
|
||||||
|
kind: notificationv1.AlertKind,
|
||||||
|
humanKind: "alert",
|
||||||
|
}
|
||||||
|
|
||||||
|
type alertAdapter struct {
|
||||||
|
*notificationv1.Alert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a alertAdapter) asClientObject() client.Object {
|
||||||
|
return a.Alert
|
||||||
|
}
|
||||||
|
|
||||||
|
// notificationv1.Alert
|
||||||
|
|
||||||
|
type alertListAdapter struct {
|
||||||
|
*notificationv1.AlertList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a alertListAdapter) asClientList() client.ObjectList {
|
||||||
|
return a.AlertList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a alertListAdapter) len() int {
|
||||||
|
return len(a.AlertList.Items)
|
||||||
|
}
|
||||||
52
cmd/flux/alert_provider.go
Normal file
52
cmd/flux/alert_provider.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// notificationv1.Provider
|
||||||
|
|
||||||
|
var alertProviderType = apiType{
|
||||||
|
kind: notificationv1.ProviderKind,
|
||||||
|
humanKind: "alert provider",
|
||||||
|
}
|
||||||
|
|
||||||
|
type alertProviderAdapter struct {
|
||||||
|
*notificationv1.Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a alertProviderAdapter) asClientObject() client.Object {
|
||||||
|
return a.Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// notificationv1.Provider
|
||||||
|
|
||||||
|
type alertProviderListAdapter struct {
|
||||||
|
*notificationv1.ProviderList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a alertProviderListAdapter) asClientList() client.ObjectList {
|
||||||
|
return a.ProviderList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a alertProviderListAdapter) len() int {
|
||||||
|
return len(a.ProviderList.Items)
|
||||||
|
}
|
||||||
@@ -17,26 +17,15 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"io/ioutil"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||||
kus "github.com/fluxcd/flux2/pkg/manifestgen/kustomization"
|
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
|
||||||
"github.com/fluxcd/flux2/pkg/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapCmd = &cobra.Command{
|
var bootstrapCmd = &cobra.Command{
|
||||||
@@ -46,21 +35,39 @@ var bootstrapCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type bootstrapFlags struct {
|
type bootstrapFlags struct {
|
||||||
version string
|
version string
|
||||||
|
arch flags.Arch
|
||||||
|
logLevel flags.LogLevel
|
||||||
|
|
||||||
|
branch string
|
||||||
|
recurseSubmodules bool
|
||||||
|
manifestsPath string
|
||||||
|
|
||||||
defaultComponents []string
|
defaultComponents []string
|
||||||
extraComponents []string
|
extraComponents []string
|
||||||
registry string
|
requiredComponents []string
|
||||||
imagePullSecret string
|
|
||||||
branch string
|
registry string
|
||||||
|
imagePullSecret string
|
||||||
|
|
||||||
|
secretName string
|
||||||
|
tokenAuth bool
|
||||||
|
keyAlgorithm flags.PublicKeyAlgorithm
|
||||||
|
keyRSABits flags.RSAKeyBits
|
||||||
|
keyECDSACurve flags.ECDSACurve
|
||||||
|
sshHostname string
|
||||||
|
caFile string
|
||||||
|
privateKeyFile string
|
||||||
|
|
||||||
watchAllNamespaces bool
|
watchAllNamespaces bool
|
||||||
networkPolicy bool
|
networkPolicy bool
|
||||||
manifestsPath string
|
|
||||||
arch flags.Arch
|
|
||||||
logLevel flags.LogLevel
|
|
||||||
requiredComponents []string
|
|
||||||
tokenAuth bool
|
|
||||||
clusterDomain string
|
clusterDomain string
|
||||||
tolerationKeys []string
|
tolerationKeys []string
|
||||||
|
|
||||||
|
authorName string
|
||||||
|
authorEmail string
|
||||||
|
|
||||||
|
commitMessageAppendix string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -72,17 +79,23 @@ var bootstrapArgs = NewBootstrapFlags()
|
|||||||
func init() {
|
func init() {
|
||||||
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "",
|
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "",
|
||||||
"toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
|
"toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
|
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
|
||||||
"list of components, accepts comma-separated values")
|
"list of components, accepts comma-separated values")
|
||||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
|
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
|
||||||
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
|
||||||
"container registry where the toolkit images are published")
|
"container registry where the toolkit images are published")
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
|
||||||
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description())
|
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch,
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch, "Git branch")
|
||||||
"default branch (for GitHub this must match the default branch setting for the organization)")
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.recurseSubmodules, "recurse-submodules", false,
|
||||||
|
"when enabled, configures the GitRepository source to initialize and include Git submodules in the artifact it produces")
|
||||||
|
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.watchAllNamespaces, "watch-all-namespaces", true,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.watchAllNamespaces, "watch-all-namespaces", true,
|
||||||
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
|
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.networkPolicy, "network-policy", true,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.networkPolicy, "network-policy", true,
|
||||||
@@ -90,12 +103,27 @@ func init() {
|
|||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false,
|
||||||
"when enabled, the personal access token will be used instead of SSH deploy key")
|
"when enabled, the personal access token will be used instead of SSH deploy key")
|
||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description())
|
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().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
|
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
|
||||||
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
|
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
|
||||||
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.secretName, "secret-name", rootArgs.defaults.Namespace, "name of the secret the sync credentials can be found in or stored to")
|
||||||
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.keyAlgorithm, "ssh-key-algorithm", bootstrapArgs.keyAlgorithm.Description())
|
||||||
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.keyRSABits, "ssh-rsa-bits", bootstrapArgs.keyRSABits.Description())
|
||||||
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.keyECDSACurve, "ssh-ecdsa-curve", bootstrapArgs.keyECDSACurve.Description())
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.sshHostname, "ssh-hostname", "", "SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.privateKeyFile, "private-key-file", "", "path to a private key file used for authenticating to the Git SSH server")
|
||||||
|
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.authorName, "author-name", "Flux", "author name for Git commits")
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.authorEmail, "author-email", "", "author email for Git commits")
|
||||||
|
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.commitMessageAppendix, "commit-message-appendix", "", "string to add to the commit messages, e.g. '[ci skip]'")
|
||||||
|
|
||||||
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description())
|
||||||
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
||||||
|
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
||||||
|
|
||||||
rootCmd.AddCommand(bootstrapCmd)
|
rootCmd.AddCommand(bootstrapCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +131,9 @@ func NewBootstrapFlags() bootstrapFlags {
|
|||||||
return bootstrapFlags{
|
return bootstrapFlags{
|
||||||
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
|
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
|
||||||
requiredComponents: []string{"source-controller", "kustomize-controller"},
|
requiredComponents: []string{"source-controller", "kustomize-controller"},
|
||||||
|
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm),
|
||||||
|
keyRSABits: 2048,
|
||||||
|
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +141,20 @@ func bootstrapComponents() []string {
|
|||||||
return append(bootstrapArgs.defaultComponents, bootstrapArgs.extraComponents...)
|
return append(bootstrapArgs.defaultComponents, bootstrapArgs.extraComponents...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildEmbeddedManifestBase() (string, error) {
|
||||||
|
if !isEmbeddedVersion(bootstrapArgs.version) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
tmpBaseDir, err := ioutil.TempDir("", "flux-manifests-")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if err := writeEmbeddedManifests(tmpBaseDir); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tmpBaseDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
func bootstrapValidate() error {
|
func bootstrapValidate() error {
|
||||||
components := bootstrapComponents()
|
components := bootstrapComponents()
|
||||||
for _, component := range bootstrapArgs.requiredComponents {
|
for _, component := range bootstrapArgs.requiredComponents {
|
||||||
@@ -125,179 +170,10 @@ func bootstrapValidate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
|
func mapTeamSlice(s []string, defaultPermission string) map[string]string {
|
||||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
m := make(map[string]string, len(s))
|
||||||
return "", err
|
for _, v := range s {
|
||||||
} else {
|
m[v] = defaultPermission
|
||||||
bootstrapArgs.version = ver
|
|
||||||
}
|
}
|
||||||
|
return m
|
||||||
manifestsBase := ""
|
|
||||||
if isEmbeddedVersion(bootstrapArgs.version) {
|
|
||||||
if err := writeEmbeddedManifests(tmpDir); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
manifestsBase = tmpDir
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := install.Options{
|
|
||||||
BaseURL: localManifests,
|
|
||||||
Version: bootstrapArgs.version,
|
|
||||||
Namespace: namespace,
|
|
||||||
Components: bootstrapComponents(),
|
|
||||||
Registry: bootstrapArgs.registry,
|
|
||||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
|
||||||
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
|
||||||
NetworkPolicy: bootstrapArgs.networkPolicy,
|
|
||||||
LogLevel: bootstrapArgs.logLevel.String(),
|
|
||||||
NotificationController: rootArgs.defaults.NotificationController,
|
|
||||||
ManifestFile: rootArgs.defaults.ManifestFile,
|
|
||||||
Timeout: rootArgs.timeout,
|
|
||||||
TargetPath: targetPath,
|
|
||||||
ClusterDomain: bootstrapArgs.clusterDomain,
|
|
||||||
TolerationKeys: bootstrapArgs.tolerationKeys,
|
|
||||||
}
|
|
||||||
|
|
||||||
if localManifests == "" {
|
|
||||||
opts.BaseURL = rootArgs.defaults.BaseURL
|
|
||||||
}
|
|
||||||
|
|
||||||
output, err := install.Generate(opts, manifestsBase)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
filePath, err := output.WriteFile(tmpDir)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
|
||||||
}
|
|
||||||
return filePath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
|
|
||||||
kubectlArgs := []string{"apply", "-f", manifestPath}
|
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
|
||||||
return fmt.Errorf("install failed: %w", err)
|
|
||||||
}
|
|
||||||
kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("install failed: %w", err)
|
|
||||||
}
|
|
||||||
statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("install failed: %w", err)
|
|
||||||
}
|
|
||||||
componentRefs, err := buildComponentObjectRefs(components...)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("install failed: %w", err)
|
|
||||||
}
|
|
||||||
logger.Waitingf("verifying installation")
|
|
||||||
if err := statusChecker.Assess(componentRefs...); err != nil {
|
|
||||||
return fmt.Errorf("install failed")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) (string, error) {
|
|
||||||
opts := sync.Options{
|
|
||||||
Name: name,
|
|
||||||
Namespace: namespace,
|
|
||||||
URL: url,
|
|
||||||
Branch: branch,
|
|
||||||
Interval: interval,
|
|
||||||
Secret: namespace,
|
|
||||||
TargetPath: targetPath,
|
|
||||||
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest, err := sync.Generate(opts)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
output, err := manifest.WriteFile(tmpDir)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
outputDir := filepath.Dir(output)
|
|
||||||
|
|
||||||
kusOpts := kus.MakeDefaultOptions()
|
|
||||||
kusOpts.BaseDir = tmpDir
|
|
||||||
kusOpts.TargetPath = filepath.Dir(manifest.Path)
|
|
||||||
|
|
||||||
kustomization, err := kus.Generate(kusOpts)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if _, err = kustomization.WriteFile(tmpDir); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputDir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error {
|
|
||||||
kubectlArgs := []string{"apply", "-k", manifestsPath}
|
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for cluster sync")
|
|
||||||
|
|
||||||
var gitRepository sourcev1.GitRepository
|
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
|
||||||
isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
|
||||||
isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldInstallManifests(ctx context.Context, kubeClient client.Client, namespace string) bool {
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: namespace,
|
|
||||||
}
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
if err := kubeClient.Get(ctx, namespacedName, &kustomization); err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return kustomization.Status.LastAppliedRevision == ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldCreateDeployKey(ctx context.Context, kubeClient client.Client, namespace string) bool {
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: namespace,
|
|
||||||
}
|
|
||||||
|
|
||||||
var existing corev1.Secret
|
|
||||||
if err := kubeClient.Get(ctx, namespacedName, &existing); err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkIfBootstrapPathDiffers(ctx context.Context, kubeClient client.Client, namespace string, path string) (string, bool) {
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Name: namespace,
|
|
||||||
Namespace: namespace,
|
|
||||||
}
|
|
||||||
var fluxSystemKustomization kustomizev1.Kustomization
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, &fluxSystemKustomization)
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
if fluxSystemKustomization.Spec.Path == path {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
return fluxSystemKustomization.Spec.Path, true
|
|
||||||
}
|
}
|
||||||
|
|||||||
259
cmd/flux/bootstrap_git.go
Normal file
259
cmd/flux/bootstrap_git.go
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
|
||||||
|
"github.com/manifoldco/promptui"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap"
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap/git/gogit"
|
||||||
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bootstrapGitCmd = &cobra.Command{
|
||||||
|
Use: "git",
|
||||||
|
Short: "Bootstrap toolkit components in a Git repository",
|
||||||
|
Long: `The bootstrap git command commits the toolkit components manifests to the
|
||||||
|
branch of a Git repository. It then configures the target cluster to synchronize with
|
||||||
|
the repository. If the toolkit components are present on the cluster, the bootstrap
|
||||||
|
command will perform an upgrade if needed.`,
|
||||||
|
Example: ` # Run bootstrap for a Git repository and authenticate with your SSH agent
|
||||||
|
flux bootstrap git --url=ssh://git@example.com/repository.git
|
||||||
|
|
||||||
|
# Run bootstrap for a Git repository and authenticate using a password
|
||||||
|
flux bootstrap git --url=https://example.com/repository.git --password=<password>
|
||||||
|
|
||||||
|
# Run bootstrap for a Git repository with a passwordless private key
|
||||||
|
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key>
|
||||||
|
|
||||||
|
# Run bootstrap for a Git repository with a private key and password
|
||||||
|
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key> --password=<password>
|
||||||
|
`,
|
||||||
|
RunE: bootstrapGitCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type gitFlags struct {
|
||||||
|
url string
|
||||||
|
interval time.Duration
|
||||||
|
path flags.SafeRelativePath
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
var gitArgs gitFlags
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bootstrapGitCmd.Flags().StringVar(&gitArgs.url, "url", "", "Git repository URL")
|
||||||
|
bootstrapGitCmd.Flags().DurationVar(&gitArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
|
bootstrapGitCmd.Flags().Var(&gitArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
|
bootstrapGitCmd.Flags().StringVarP(&gitArgs.username, "username", "u", "git", "basic authentication username")
|
||||||
|
bootstrapGitCmd.Flags().StringVarP(&gitArgs.password, "password", "p", "", "basic authentication password")
|
||||||
|
|
||||||
|
bootstrapCmd.AddCommand(bootstrapGitCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := bootstrapValidate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repositoryURL, err := url.Parse(gitArgs.url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gitAuth, err := transportForURL(repositoryURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manifest base
|
||||||
|
if ver, err := getVersion(bootstrapArgs.version); err == nil {
|
||||||
|
bootstrapArgs.version = ver
|
||||||
|
}
|
||||||
|
manifestsBase, err := buildEmbeddedManifestBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
|
// Lazy go-git repository
|
||||||
|
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
gitClient := gogit.New(tmpDir, gitAuth)
|
||||||
|
|
||||||
|
// Install manifest config
|
||||||
|
installOptions := install.Options{
|
||||||
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
|
Version: bootstrapArgs.version,
|
||||||
|
Namespace: rootArgs.namespace,
|
||||||
|
Components: bootstrapComponents(),
|
||||||
|
Registry: bootstrapArgs.registry,
|
||||||
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
|
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||||
|
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||||
|
LogLevel: bootstrapArgs.logLevel.String(),
|
||||||
|
NotificationController: rootArgs.defaults.NotificationController,
|
||||||
|
ManifestFile: rootArgs.defaults.ManifestFile,
|
||||||
|
Timeout: rootArgs.timeout,
|
||||||
|
TargetPath: gitArgs.path.ToSlash(),
|
||||||
|
ClusterDomain: bootstrapArgs.clusterDomain,
|
||||||
|
TolerationKeys: bootstrapArgs.tolerationKeys,
|
||||||
|
}
|
||||||
|
if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
|
||||||
|
installOptions.BaseURL = customBaseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source generation and secret config
|
||||||
|
secretOpts := sourcesecret.Options{
|
||||||
|
Name: bootstrapArgs.secretName,
|
||||||
|
Namespace: rootArgs.namespace,
|
||||||
|
TargetPath: gitArgs.path.String(),
|
||||||
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
|
}
|
||||||
|
if bootstrapArgs.tokenAuth {
|
||||||
|
secretOpts.Username = gitArgs.username
|
||||||
|
secretOpts.Password = gitArgs.password
|
||||||
|
|
||||||
|
if bootstrapArgs.caFile != "" {
|
||||||
|
secretOpts.CAFilePath = bootstrapArgs.caFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure repository URL to match auth config for sync.
|
||||||
|
repositoryURL.User = nil
|
||||||
|
repositoryURL.Scheme = "https"
|
||||||
|
repositoryURL.Host = repositoryURL.Hostname()
|
||||||
|
} else {
|
||||||
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
|
secretOpts.Password = gitArgs.password
|
||||||
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
|
||||||
|
// Configure repository URL to match auth config for sync.
|
||||||
|
repositoryURL.User = url.User(gitArgs.username)
|
||||||
|
repositoryURL.Scheme = "ssh"
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
|
repositoryURL.Host = bootstrapArgs.sshHostname
|
||||||
|
}
|
||||||
|
if bootstrapArgs.privateKeyFile != "" {
|
||||||
|
secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure last as it depends on the config above.
|
||||||
|
secretOpts.SSHHostname = repositoryURL.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync manifest config
|
||||||
|
syncOpts := sync.Options{
|
||||||
|
Interval: gitArgs.interval,
|
||||||
|
Name: rootArgs.namespace,
|
||||||
|
Namespace: rootArgs.namespace,
|
||||||
|
URL: repositoryURL.String(),
|
||||||
|
Branch: bootstrapArgs.branch,
|
||||||
|
Secret: bootstrapArgs.secretName,
|
||||||
|
TargetPath: gitArgs.path.ToSlash(),
|
||||||
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
|
GitImplementation: sourceGitArgs.gitImplementation.String(),
|
||||||
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrap config
|
||||||
|
bootstrapOpts := []bootstrap.GitOption{
|
||||||
|
bootstrap.WithRepositoryURL(gitArgs.url),
|
||||||
|
bootstrap.WithBranch(bootstrapArgs.branch),
|
||||||
|
bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail),
|
||||||
|
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
||||||
|
bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
|
||||||
|
bootstrap.WithPostGenerateSecretFunc(promptPublicKey),
|
||||||
|
bootstrap.WithLogger(logger),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup bootstrapper with constructed configs
|
||||||
|
b, err := bootstrap.NewPlainGitProvider(gitClient, kubeClient, bootstrapOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run
|
||||||
|
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transportForURL constructs a transport.AuthMethod based on the scheme
|
||||||
|
// of the given URL and the configured flags. If the protocol equals
|
||||||
|
// "ssh" but no private key is configured, authentication using the local
|
||||||
|
// SSH-agent is attempted.
|
||||||
|
func transportForURL(u *url.URL) (transport.AuthMethod, error) {
|
||||||
|
switch u.Scheme {
|
||||||
|
case "https":
|
||||||
|
return &http.BasicAuth{
|
||||||
|
Username: gitArgs.username,
|
||||||
|
Password: gitArgs.password,
|
||||||
|
}, nil
|
||||||
|
case "ssh":
|
||||||
|
if bootstrapArgs.privateKeyFile != "" {
|
||||||
|
return ssh.NewPublicKeysFromFile(u.User.Username(), bootstrapArgs.privateKeyFile, gitArgs.password)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("scheme %q is not supported", u.Scheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func promptPublicKey(ctx context.Context, secret corev1.Secret, _ sourcesecret.Options) error {
|
||||||
|
ppk, ok := secret.StringData[sourcesecret.PublicKeySecretKey]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Successf("public key: %s", strings.TrimSpace(ppk))
|
||||||
|
prompt := promptui.Prompt{
|
||||||
|
Label: "Please give the key access to your repository",
|
||||||
|
IsConfirm: true,
|
||||||
|
}
|
||||||
|
_, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("aborting")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -20,20 +20,20 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/git"
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap"
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap/git/gogit"
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap/provider"
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitHubCmd = &cobra.Command{
|
var bootstrapGitHubCmd = &cobra.Command{
|
||||||
@@ -47,44 +47,46 @@ the bootstrap command will perform an upgrade if needed.`,
|
|||||||
Example: ` # Create a GitHub personal access token and export it as an env var
|
Example: ` # Create a GitHub personal access token and export it as an env var
|
||||||
export GITHUB_TOKEN=<my-token>
|
export GITHUB_TOKEN=<my-token>
|
||||||
|
|
||||||
# Run bootstrap for a private repo owned by a GitHub organization
|
# Run bootstrap for a private repository owned by a GitHub organization
|
||||||
flux bootstrap github --owner=<organization> --repository=<repo name>
|
flux bootstrap github --owner=<organization> --repository=<repository name>
|
||||||
|
|
||||||
# Run bootstrap for a private repo and assign organization teams to it
|
# Run bootstrap for a private repository and assign organization teams to it
|
||||||
flux bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
|
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug> --team=<team2 slug>
|
||||||
|
|
||||||
# Run bootstrap for a repository path
|
# Run bootstrap for a repository path
|
||||||
flux bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster
|
flux bootstrap github --owner=<organization> --repository=<repository name> --path=dev-cluster
|
||||||
|
|
||||||
# Run bootstrap for a public repository on a personal account
|
# Run bootstrap for a public repository on a personal account
|
||||||
flux bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
|
flux bootstrap github --owner=<user> --repository=<repository name> --private=false --personal=true
|
||||||
|
|
||||||
# Run bootstrap for a private repo hosted on GitHub Enterprise using SSH auth
|
# Run bootstrap for a private repository hosted on GitHub Enterprise using SSH auth
|
||||||
flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> --ssh-hostname=<domain>
|
flux bootstrap github --owner=<organization> --repository=<repository name> --hostname=<domain> --ssh-hostname=<domain>
|
||||||
|
|
||||||
# Run bootstrap for a private repo hosted on GitHub Enterprise using HTTPS auth
|
# Run bootstrap for a private repository hosted on GitHub Enterprise using HTTPS auth
|
||||||
flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> --token-auth
|
flux bootstrap github --owner=<organization> --repository=<repository name> --hostname=<domain> --token-auth
|
||||||
|
|
||||||
# Run bootstrap for a an existing repository with a branch named main
|
# Run bootstrap for an existing repository with a branch named main
|
||||||
flux bootstrap github --owner=<organization> --repository=<repo name> --branch=main
|
flux bootstrap github --owner=<organization> --repository=<repository name> --branch=main`,
|
||||||
`,
|
|
||||||
RunE: bootstrapGitHubCmdRun,
|
RunE: bootstrapGitHubCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
type githubFlags struct {
|
type githubFlags struct {
|
||||||
owner string
|
owner string
|
||||||
repository string
|
repository string
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
personal bool
|
personal bool
|
||||||
private bool
|
private bool
|
||||||
hostname string
|
hostname string
|
||||||
path flags.SafeRelativePath
|
path flags.SafeRelativePath
|
||||||
teams []string
|
teams []string
|
||||||
sshHostname string
|
readWriteKey bool
|
||||||
|
reconcile bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ghDefaultPermission = "maintain"
|
ghDefaultPermission = "maintain"
|
||||||
|
ghDefaultDomain = "github.com"
|
||||||
|
ghTokenEnvVar = "GITHUB_TOKEN"
|
||||||
)
|
)
|
||||||
|
|
||||||
var githubArgs githubFlags
|
var githubArgs githubFlags
|
||||||
@@ -92,21 +94,22 @@ var githubArgs githubFlags
|
|||||||
func init() {
|
func init() {
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository 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().StringSliceVar(&githubArgs.teams, "team", []string{}, "GitHub team to be given maintainer access (also accepts comma-separated values)")
|
||||||
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.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().BoolVar(&githubArgs.private, "private", true, "if true, the repository is setup or configured as private")
|
||||||
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
|
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", ghDefaultDomain, "GitHub hostname")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
|
||||||
bootstrapGitHubCmd.Flags().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
bootstrapGitHubCmd.Flags().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions")
|
||||||
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.reconcile, "reconcile", false, "if true, the configured options are also reconciled if the repository already exists")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitHubCmd)
|
bootstrapCmd.AddCommand(bootstrapGitHubCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
ghToken := os.Getenv(git.GitHubTokenName)
|
ghToken := os.Getenv(ghTokenEnvVar)
|
||||||
if ghToken == "" {
|
if ghToken == "" {
|
||||||
return fmt.Errorf("%s environment variable not found", git.GitHubTokenName)
|
return fmt.Errorf("%s environment variable not found", ghTokenEnvVar)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bootstrapValidate(); err != nil {
|
if err := bootstrapValidate(); err != nil {
|
||||||
@@ -121,205 +124,129 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(
|
// Manifest base
|
||||||
ctx,
|
if ver, err := getVersion(bootstrapArgs.version); err == nil {
|
||||||
kubeClient,
|
bootstrapArgs.version = ver
|
||||||
rootArgs.namespace,
|
|
||||||
filepath.ToSlash(githubArgs.path.String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
if bootstrapPathDiffers {
|
|
||||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
|
||||||
}
|
}
|
||||||
|
manifestsBase, err := buildEmbeddedManifestBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
repository, err := git.NewRepository(
|
// Build GitHub provider
|
||||||
githubArgs.repository,
|
providerCfg := provider.Config{
|
||||||
githubArgs.owner,
|
Provider: provider.GitProviderGitHub,
|
||||||
githubArgs.hostname,
|
Hostname: githubArgs.hostname,
|
||||||
ghToken,
|
Token: ghToken,
|
||||||
"flux",
|
}
|
||||||
githubArgs.owner+"@users.noreply.github.com",
|
providerClient, err := provider.BuildGitProvider(providerCfg)
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if githubArgs.sshHostname != "" {
|
// Lazy go-git repository
|
||||||
repository.SSHHost = githubArgs.sshHostname
|
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
||||||
}
|
|
||||||
|
|
||||||
provider := &git.GithubProvider{
|
|
||||||
IsPrivate: githubArgs.private,
|
|
||||||
IsPersonal: githubArgs.personal,
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
gitClient := gogit.New(tmpDir, &http.BasicAuth{
|
||||||
|
Username: githubArgs.owner,
|
||||||
|
Password: ghToken,
|
||||||
|
})
|
||||||
|
|
||||||
// create GitHub repository if doesn't exists
|
// Install manifest config
|
||||||
logger.Actionf("connecting to %s", githubArgs.hostname)
|
installOptions := install.Options{
|
||||||
changed, err := provider.CreateRepository(ctx, repository)
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
if err != nil {
|
Version: bootstrapArgs.version,
|
||||||
return err
|
Namespace: rootArgs.namespace,
|
||||||
|
Components: bootstrapComponents(),
|
||||||
|
Registry: bootstrapArgs.registry,
|
||||||
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
|
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||||
|
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||||
|
LogLevel: bootstrapArgs.logLevel.String(),
|
||||||
|
NotificationController: rootArgs.defaults.NotificationController,
|
||||||
|
ManifestFile: rootArgs.defaults.ManifestFile,
|
||||||
|
Timeout: rootArgs.timeout,
|
||||||
|
TargetPath: githubArgs.path.ToSlash(),
|
||||||
|
ClusterDomain: bootstrapArgs.clusterDomain,
|
||||||
|
TolerationKeys: bootstrapArgs.tolerationKeys,
|
||||||
}
|
}
|
||||||
if changed {
|
if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
|
||||||
logger.Successf("repository created")
|
installOptions.BaseURL = customBaseURL
|
||||||
}
|
}
|
||||||
|
|
||||||
withErrors := false
|
// Source generation and secret config
|
||||||
// add teams to org repository
|
|
||||||
if !githubArgs.personal {
|
|
||||||
for _, team := range githubArgs.teams {
|
|
||||||
if changed, err := provider.AddTeam(ctx, repository, team, ghDefaultPermission); err != nil {
|
|
||||||
logger.Failuref(err.Error())
|
|
||||||
withErrors = true
|
|
||||||
} else if changed {
|
|
||||||
logger.Successf("%s team access granted", team)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clone repository and checkout the main branch
|
|
||||||
if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("repository cloned")
|
|
||||||
|
|
||||||
// generate install manifests
|
|
||||||
logger.Generatef("generating manifests")
|
|
||||||
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),
|
|
||||||
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// push install manifests
|
|
||||||
if changed {
|
|
||||||
if err := repository.Push(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("components manifests pushed")
|
|
||||||
} else {
|
|
||||||
logger.Successf("components are up to date")
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine if repo synchronization is working
|
|
||||||
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace)
|
|
||||||
|
|
||||||
if isInstall {
|
|
||||||
// apply install manifests
|
|
||||||
logger.Actionf("installing components in %s namespace", rootArgs.namespace)
|
|
||||||
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("install completed")
|
|
||||||
}
|
|
||||||
|
|
||||||
repoURL := repository.GetSSH()
|
|
||||||
secretOpts := sourcesecret.Options{
|
secretOpts := sourcesecret.Options{
|
||||||
Name: rootArgs.namespace,
|
Name: bootstrapArgs.secretName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: rootArgs.namespace,
|
||||||
|
TargetPath: githubArgs.path.ToSlash(),
|
||||||
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
}
|
}
|
||||||
if bootstrapArgs.tokenAuth {
|
if bootstrapArgs.tokenAuth {
|
||||||
// Setup HTTPS token auth
|
|
||||||
repoURL = repository.GetURL()
|
|
||||||
secretOpts.Username = "git"
|
secretOpts.Username = "git"
|
||||||
secretOpts.Password = ghToken
|
secretOpts.Password = ghToken
|
||||||
} else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
|
|
||||||
// Setup SSH auth
|
if bootstrapArgs.caFile != "" {
|
||||||
u, err := url.Parse(repoURL)
|
secretOpts.CAFilePath = bootstrapArgs.caFile
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
|
||||||
}
|
}
|
||||||
secretOpts.SSHHostname = u.Host
|
} else {
|
||||||
secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
secretOpts.RSAKeyBits = 2048
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
}
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
secretOpts.SSHHostname = githubArgs.hostname
|
||||||
|
|
||||||
secret, err := sourcesecret.Generate(secretOpts)
|
if bootstrapArgs.sshHostname != "" {
|
||||||
if err != nil {
|
secretOpts.SSHHostname = bootstrapArgs.sshHostname
|
||||||
return err
|
|
||||||
}
|
|
||||||
var s corev1.Secret
|
|
||||||
if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(s.StringData) > 0 {
|
|
||||||
logger.Actionf("configuring deploy key")
|
|
||||||
if err := upsertSecret(ctx, kubeClient, s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
|
|
||||||
keyName := "flux"
|
|
||||||
if githubArgs.path != "" {
|
|
||||||
keyName = fmt.Sprintf("flux-%s", githubArgs.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil {
|
|
||||||
return err
|
|
||||||
} else if changed {
|
|
||||||
logger.Successf("deploy key configured")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure repo synchronization
|
// Sync manifest config
|
||||||
logger.Actionf("generating sync manifests")
|
syncOpts := sync.Options{
|
||||||
syncManifests, err := generateSyncManifests(
|
Interval: githubArgs.interval,
|
||||||
repoURL,
|
Name: rootArgs.namespace,
|
||||||
bootstrapArgs.branch,
|
Namespace: rootArgs.namespace,
|
||||||
rootArgs.namespace,
|
Branch: bootstrapArgs.branch,
|
||||||
rootArgs.namespace,
|
Secret: bootstrapArgs.secretName,
|
||||||
filepath.ToSlash(githubArgs.path.String()),
|
TargetPath: githubArgs.path.ToSlash(),
|
||||||
tmpDir,
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
githubArgs.interval,
|
GitImplementation: sourceGitArgs.gitImplementation.String(),
|
||||||
)
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrap config
|
||||||
|
bootstrapOpts := []bootstrap.GitProviderOption{
|
||||||
|
bootstrap.WithProviderRepository(githubArgs.owner, githubArgs.repository, githubArgs.personal),
|
||||||
|
bootstrap.WithBranch(bootstrapArgs.branch),
|
||||||
|
bootstrap.WithBootstrapTransportType("https"),
|
||||||
|
bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail),
|
||||||
|
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
||||||
|
bootstrap.WithProviderTeamPermissions(mapTeamSlice(githubArgs.teams, ghDefaultPermission)),
|
||||||
|
bootstrap.WithReadWriteKeyPermissions(githubArgs.readWriteKey),
|
||||||
|
bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
|
||||||
|
bootstrap.WithLogger(logger),
|
||||||
|
}
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||||
|
}
|
||||||
|
if bootstrapArgs.tokenAuth {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||||
|
}
|
||||||
|
if !githubArgs.private {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||||
|
}
|
||||||
|
if githubArgs.reconcile {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithReconcile())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup bootstrapper with constructed configs
|
||||||
|
b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// Run
|
||||||
if changed, err = repository.Commit(
|
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
|
||||||
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 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("sync manifests pushed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply manifests and waiting for sync
|
|
||||||
logger.Actionf("applying sync manifests")
|
|
||||||
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if withErrors {
|
|
||||||
return fmt.Errorf("bootstrap completed with errors")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Successf("bootstrap finished")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,22 +20,22 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/git"
|
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap"
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap/git/gogit"
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap/provider"
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitLabCmd = &cobra.Command{
|
var bootstrapGitLabCmd = &cobra.Command{
|
||||||
@@ -49,40 +49,44 @@ the bootstrap command will perform an upgrade if needed.`,
|
|||||||
Example: ` # Create a GitLab API token and export it as an env var
|
Example: ` # Create a GitLab API token and export it as an env var
|
||||||
export GITLAB_TOKEN=<my-token>
|
export GITLAB_TOKEN=<my-token>
|
||||||
|
|
||||||
# Run bootstrap for a private repo using HTTPS token authentication
|
# Run bootstrap for a private repository using HTTPS token authentication
|
||||||
flux bootstrap gitlab --owner=<group> --repository=<repo name> --token-auth
|
flux bootstrap gitlab --owner=<group> --repository=<repository name> --token-auth
|
||||||
|
|
||||||
# Run bootstrap for a private repo using SSH authentication
|
# Run bootstrap for a private repository using SSH authentication
|
||||||
flux bootstrap gitlab --owner=<group> --repository=<repo name>
|
flux bootstrap gitlab --owner=<group> --repository=<repository name>
|
||||||
|
|
||||||
# Run bootstrap for a repository path
|
# Run bootstrap for a repository path
|
||||||
flux bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
|
flux bootstrap gitlab --owner=<group> --repository=<repository name> --path=dev-cluster
|
||||||
|
|
||||||
# Run bootstrap for a public repository on a personal account
|
# Run bootstrap for a public repository on a personal account
|
||||||
flux bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal --token-auth
|
flux bootstrap gitlab --owner=<user> --repository=<repository name> --private=false --personal --token-auth
|
||||||
|
|
||||||
# Run bootstrap for a private repo hosted on a GitLab server
|
# Run bootstrap for a private repository hosted on a GitLab server
|
||||||
flux bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain> --token-auth
|
flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
|
||||||
|
|
||||||
# Run bootstrap for a an existing repository with a branch named main
|
# Run bootstrap for a an existing repository with a branch named main
|
||||||
flux bootstrap gitlab --owner=<organization> --repository=<repo name> --branch=main --token-auth
|
flux bootstrap gitlab --owner=<organization> --repository=<repository name> --branch=main --token-auth`,
|
||||||
`,
|
|
||||||
RunE: bootstrapGitLabCmdRun,
|
RunE: bootstrapGitLabCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z`
|
glDefaultPermission = "maintain"
|
||||||
|
glDefaultDomain = "gitlab.com"
|
||||||
|
glTokenEnvVar = "GITLAB_TOKEN"
|
||||||
|
gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z`
|
||||||
)
|
)
|
||||||
|
|
||||||
type gitlabFlags struct {
|
type gitlabFlags struct {
|
||||||
owner string
|
owner string
|
||||||
repository string
|
repository string
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
personal bool
|
personal bool
|
||||||
private bool
|
private bool
|
||||||
hostname string
|
hostname string
|
||||||
sshHostname string
|
path flags.SafeRelativePath
|
||||||
path flags.SafeRelativePath
|
teams []string
|
||||||
|
readWriteKey bool
|
||||||
|
reconcile bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitlabArgs gitlabFlags
|
var gitlabArgs gitlabFlags
|
||||||
@@ -90,29 +94,30 @@ var gitlabArgs gitlabFlags
|
|||||||
func init() {
|
func init() {
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name")
|
||||||
|
bootstrapGitLabCmd.Flags().StringSliceVar(&gitlabArgs.teams, "team", []string{}, "GitLab teams to be given maintainer access (also accepts comma-separated values)")
|
||||||
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.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().BoolVar(&gitlabArgs.private, "private", true, "if true, the repository is setup or configured as private")
|
||||||
bootstrapGitLabCmd.Flags().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval")
|
bootstrapGitLabCmd.Flags().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", glDefaultDomain, "GitLab hostname")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.sshHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
|
||||||
bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions")
|
||||||
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.reconcile, "reconcile", false, "if true, the configured options are also reconciled if the repository already exists")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
glToken := os.Getenv(git.GitLabTokenName)
|
glToken := os.Getenv(glTokenEnvVar)
|
||||||
if glToken == "" {
|
if glToken == "" {
|
||||||
return fmt.Errorf("%s environment variable not found", git.GitLabTokenName)
|
return fmt.Errorf("%s environment variable not found", glTokenEnvVar)
|
||||||
}
|
}
|
||||||
|
|
||||||
projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository)
|
if projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository); err != nil || !projectNameIsValid {
|
||||||
if err != nil {
|
if err == nil {
|
||||||
|
err = fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", gitlabArgs.repository)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !projectNameIsValid {
|
|
||||||
return fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", gitlabArgs.repository)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bootstrapValidate(); err != nil {
|
if err := bootstrapValidate(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -126,183 +131,138 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String()))
|
// Manifest base
|
||||||
|
if ver, err := getVersion(bootstrapArgs.version); err == nil {
|
||||||
if bootstrapPathDiffers {
|
bootstrapArgs.version = ver
|
||||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
|
||||||
}
|
}
|
||||||
|
manifestsBase, err := buildEmbeddedManifestBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
repository, err := git.NewRepository(
|
// Build GitLab provider
|
||||||
gitlabArgs.repository,
|
providerCfg := provider.Config{
|
||||||
gitlabArgs.owner,
|
Provider: provider.GitProviderGitLab,
|
||||||
gitlabArgs.hostname,
|
Hostname: gitlabArgs.hostname,
|
||||||
glToken,
|
Token: glToken,
|
||||||
"flux",
|
}
|
||||||
gitlabArgs.owner+"@users.noreply.gitlab.com",
|
// Workaround for: https://github.com/fluxcd/go-git-providers/issues/55
|
||||||
)
|
if hostname := providerCfg.Hostname; hostname != glDefaultDomain &&
|
||||||
|
!strings.HasPrefix(hostname, "https://") &&
|
||||||
|
!strings.HasPrefix(hostname, "http://") {
|
||||||
|
providerCfg.Hostname = "https://" + providerCfg.Hostname
|
||||||
|
}
|
||||||
|
providerClient, err := provider.BuildGitProvider(providerCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if gitlabArgs.sshHostname != "" {
|
// Lazy go-git repository
|
||||||
repository.SSHHost = gitlabArgs.sshHostname
|
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
gitClient := gogit.New(tmpDir, &http.BasicAuth{
|
||||||
|
Username: gitlabArgs.owner,
|
||||||
|
Password: glToken,
|
||||||
|
})
|
||||||
|
|
||||||
provider := &git.GitLabProvider{
|
// Install manifest config
|
||||||
IsPrivate: gitlabArgs.private,
|
installOptions := install.Options{
|
||||||
IsPersonal: gitlabArgs.personal,
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
|
Version: bootstrapArgs.version,
|
||||||
|
Namespace: rootArgs.namespace,
|
||||||
|
Components: bootstrapComponents(),
|
||||||
|
Registry: bootstrapArgs.registry,
|
||||||
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
|
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||||
|
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||||
|
LogLevel: bootstrapArgs.logLevel.String(),
|
||||||
|
NotificationController: rootArgs.defaults.NotificationController,
|
||||||
|
ManifestFile: rootArgs.defaults.ManifestFile,
|
||||||
|
Timeout: rootArgs.timeout,
|
||||||
|
TargetPath: gitlabArgs.path.ToSlash(),
|
||||||
|
ClusterDomain: bootstrapArgs.clusterDomain,
|
||||||
|
TolerationKeys: bootstrapArgs.tolerationKeys,
|
||||||
|
}
|
||||||
|
if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
|
||||||
|
installOptions.BaseURL = customBaseURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// create GitLab project if doesn't exists
|
// Source generation and secret config
|
||||||
logger.Actionf("connecting to %s", gitlabArgs.hostname)
|
|
||||||
changed, err := provider.CreateRepository(ctx, repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if changed {
|
|
||||||
logger.Successf("repository created")
|
|
||||||
}
|
|
||||||
|
|
||||||
// clone repository and checkout the master branch
|
|
||||||
if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("repository cloned")
|
|
||||||
|
|
||||||
// generate install manifests
|
|
||||||
logger.Generatef("generating manifests")
|
|
||||||
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),
|
|
||||||
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// push install manifests
|
|
||||||
if changed {
|
|
||||||
if err := repository.Push(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("components manifests pushed")
|
|
||||||
} else {
|
|
||||||
logger.Successf("components are up to date")
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine if repo synchronization is working
|
|
||||||
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace)
|
|
||||||
|
|
||||||
if isInstall {
|
|
||||||
// apply install manifests
|
|
||||||
logger.Actionf("installing components in %s namespace", rootArgs.namespace)
|
|
||||||
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("install completed")
|
|
||||||
}
|
|
||||||
|
|
||||||
repoURL := repository.GetSSH()
|
|
||||||
secretOpts := sourcesecret.Options{
|
secretOpts := sourcesecret.Options{
|
||||||
Name: rootArgs.namespace,
|
Name: bootstrapArgs.secretName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: rootArgs.namespace,
|
||||||
|
TargetPath: gitlabArgs.path.String(),
|
||||||
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
}
|
}
|
||||||
if bootstrapArgs.tokenAuth {
|
if bootstrapArgs.tokenAuth {
|
||||||
// Setup HTTPS token auth
|
|
||||||
repoURL = repository.GetURL()
|
|
||||||
secretOpts.Username = "git"
|
secretOpts.Username = "git"
|
||||||
secretOpts.Password = glToken
|
secretOpts.Password = glToken
|
||||||
} else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
|
|
||||||
// Setup SSH auth
|
if bootstrapArgs.caFile != "" {
|
||||||
u, err := url.Parse(repoURL)
|
secretOpts.CAFilePath = bootstrapArgs.caFile
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
|
||||||
}
|
}
|
||||||
secretOpts.SSHHostname = u.Host
|
} else {
|
||||||
secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
secretOpts.RSAKeyBits = 2048
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
}
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
secretOpts.SSHHostname = gitlabArgs.hostname
|
||||||
|
|
||||||
secret, err := sourcesecret.Generate(secretOpts)
|
if bootstrapArgs.privateKeyFile != "" {
|
||||||
if err != nil {
|
secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
|
||||||
return err
|
|
||||||
}
|
|
||||||
var s corev1.Secret
|
|
||||||
if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(s.StringData) > 0 {
|
|
||||||
logger.Actionf("configuring deploy key")
|
|
||||||
if err := upsertSecret(ctx, kubeClient, s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
|
secretOpts.SSHHostname = bootstrapArgs.sshHostname
|
||||||
keyName := "flux"
|
|
||||||
if gitlabArgs.path != "" {
|
|
||||||
keyName = fmt.Sprintf("flux-%s", gitlabArgs.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil {
|
|
||||||
return err
|
|
||||||
} else if changed {
|
|
||||||
logger.Successf("deploy key configured")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure repo synchronization
|
// Sync manifest config
|
||||||
logger.Actionf("generating sync manifests")
|
syncOpts := sync.Options{
|
||||||
syncManifests, err := generateSyncManifests(
|
Interval: gitlabArgs.interval,
|
||||||
repoURL,
|
Name: rootArgs.namespace,
|
||||||
bootstrapArgs.branch,
|
Namespace: rootArgs.namespace,
|
||||||
rootArgs.namespace,
|
Branch: bootstrapArgs.branch,
|
||||||
rootArgs.namespace,
|
Secret: bootstrapArgs.secretName,
|
||||||
filepath.ToSlash(gitlabArgs.path.String()),
|
TargetPath: gitlabArgs.path.ToSlash(),
|
||||||
tmpDir,
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
gitlabArgs.interval,
|
GitImplementation: sourceGitArgs.gitImplementation.String(),
|
||||||
)
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrap config
|
||||||
|
bootstrapOpts := []bootstrap.GitProviderOption{
|
||||||
|
bootstrap.WithProviderRepository(gitlabArgs.owner, gitlabArgs.repository, gitlabArgs.personal),
|
||||||
|
bootstrap.WithBranch(bootstrapArgs.branch),
|
||||||
|
bootstrap.WithBootstrapTransportType("https"),
|
||||||
|
bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail),
|
||||||
|
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
||||||
|
bootstrap.WithProviderTeamPermissions(mapTeamSlice(gitlabArgs.teams, glDefaultPermission)),
|
||||||
|
bootstrap.WithReadWriteKeyPermissions(gitlabArgs.readWriteKey),
|
||||||
|
bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
|
||||||
|
bootstrap.WithLogger(logger),
|
||||||
|
}
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||||
|
}
|
||||||
|
if bootstrapArgs.tokenAuth {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||||
|
}
|
||||||
|
if !gitlabArgs.private {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||||
|
}
|
||||||
|
if gitlabArgs.reconcile {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithReconcile())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup bootstrapper with constructed configs
|
||||||
|
b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// Run
|
||||||
if changed, err = repository.Commit(
|
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
|
||||||
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 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("sync manifests pushed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply manifests and waiting for sync
|
|
||||||
logger.Actionf("applying sync manifests")
|
|
||||||
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Successf("bootstrap finished")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,7 @@ the local environment is configured correctly and if the installed components ar
|
|||||||
flux check --pre
|
flux check --pre
|
||||||
|
|
||||||
# Run installation checks
|
# Run installation checks
|
||||||
flux check
|
flux check`,
|
||||||
`,
|
|
||||||
RunE: runCheckCmd,
|
RunE: runCheckCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ var completionBashCmd = &cobra.Command{
|
|||||||
To configure your bash shell to load completions for each session add to your bashrc
|
To configure your bash shell to load completions for each session add to your bashrc
|
||||||
|
|
||||||
# ~/.bashrc or ~/.profile
|
# ~/.bashrc or ~/.profile
|
||||||
command -v flux >/dev/null && . <(flux completion bash)
|
command -v flux >/dev/null && . <(flux completion bash)`,
|
||||||
`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
rootCmd.GenBashCompletion(os.Stdout)
|
rootCmd.GenBashCompletion(os.Stdout)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ var completionFishCmd = &cobra.Command{
|
|||||||
|
|
||||||
flux completion fish > ~/.config/fish/completions/flux.fish
|
flux completion fish > ~/.config/fish/completions/flux.fish
|
||||||
|
|
||||||
See http://fishshell.com/docs/current/index.html#completion-own for more details
|
See http://fishshell.com/docs/current/index.html#completion-own for more details`,
|
||||||
`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
rootCmd.GenFishCompletion(os.Stdout, true)
|
rootCmd.GenFishCompletion(os.Stdout, true)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ flux completion >> flux-completion.ps1
|
|||||||
Linux:
|
Linux:
|
||||||
|
|
||||||
cd "${XDG_CONFIG_HOME:-"$HOME/.config/"}/powershell/modules"
|
cd "${XDG_CONFIG_HOME:-"$HOME/.config/"}/powershell/modules"
|
||||||
flux completion >> flux-completions.ps1
|
flux completion >> flux-completions.ps1`,
|
||||||
`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
rootCmd.GenPowerShellCompletion(os.Stdout)
|
rootCmd.GenPowerShellCompletion(os.Stdout)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ echo "${fpath// /\n}" | grep -i completion
|
|||||||
flux completion zsh > _flux
|
flux completion zsh > _flux
|
||||||
|
|
||||||
mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
|
mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
|
||||||
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto`,
|
||||||
`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
rootCmd.GenZshCompletion(os.Stdout)
|
rootCmd.GenZshCompletion(os.Stdout)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ var createAlertCmd = &cobra.Command{
|
|||||||
--event-severity info \
|
--event-severity info \
|
||||||
--event-source Kustomization/flux-system \
|
--event-source Kustomization/flux-system \
|
||||||
--provider-ref slack \
|
--provider-ref slack \
|
||||||
flux-system
|
flux-system`,
|
||||||
`,
|
|
||||||
RunE: createAlertCmdRun,
|
RunE: createAlertCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +58,7 @@ var alertArgs alertFlags
|
|||||||
func init() {
|
func init() {
|
||||||
createAlertCmd.Flags().StringVar(&alertArgs.providerRef, "provider-ref", "", "reference to provider")
|
createAlertCmd.Flags().StringVar(&alertArgs.providerRef, "provider-ref", "", "reference to provider")
|
||||||
createAlertCmd.Flags().StringVar(&alertArgs.eventSeverity, "event-severity", "", "severity of events to send alerts for")
|
createAlertCmd.Flags().StringVar(&alertArgs.eventSeverity, "event-severity", "", "severity of events to send alerts for")
|
||||||
createAlertCmd.Flags().StringArrayVar(&alertArgs.eventSources, "event-source", []string{}, "sources that should generate alerts (<kind>/<name>)")
|
createAlertCmd.Flags().StringSliceVar(&alertArgs.eventSources, "event-source", []string{}, "sources that should generate alerts (<kind>/<name>), also accepts comma-separated values")
|
||||||
createCmd.AddCommand(createAlertCmd)
|
createCmd.AddCommand(createAlertCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,14 +74,15 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
eventSources := []notificationv1.CrossNamespaceObjectReference{}
|
eventSources := []notificationv1.CrossNamespaceObjectReference{}
|
||||||
for _, eventSource := range alertArgs.eventSources {
|
for _, eventSource := range alertArgs.eventSources {
|
||||||
kind, name := utils.ParseObjectKindName(eventSource)
|
kind, name, namespace := utils.ParseObjectKindNameNamespace(eventSource)
|
||||||
if kind == "" {
|
if kind == "" {
|
||||||
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
|
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
eventSources = append(eventSources, notificationv1.CrossNamespaceObjectReference{
|
eventSources = append(eventSources, notificationv1.CrossNamespaceObjectReference{
|
||||||
Kind: kind,
|
Kind: kind,
|
||||||
Name: name,
|
Name: name,
|
||||||
|
Namespace: namespace,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return exportAlert(alert)
|
return printExport(exportAlert(&alert))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
|||||||
@@ -49,8 +49,7 @@ var createAlertProviderCmd = &cobra.Command{
|
|||||||
flux create alert-provider github-podinfo \
|
flux create alert-provider github-podinfo \
|
||||||
--type github \
|
--type github \
|
||||||
--address https://github.com/stefanprodan/podinfo \
|
--address https://github.com/stefanprodan/podinfo \
|
||||||
--secret-ref github-token
|
--secret-ref github-token`,
|
||||||
`,
|
|
||||||
RunE: createAlertProviderCmdRun,
|
RunE: createAlertProviderCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +112,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return exportAlertProvider(provider)
|
return printExport(exportAlertProvider(&provider))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
|||||||
@@ -91,13 +91,18 @@ var createHelmReleaseCmd = &cobra.Command{
|
|||||||
--source=HelmRepository/podinfo \
|
--source=HelmRepository/podinfo \
|
||||||
--chart=podinfo
|
--chart=podinfo
|
||||||
|
|
||||||
|
# Create a HelmRelease using a source from a different namespace
|
||||||
|
flux create hr podinfo \
|
||||||
|
--namespace=default \
|
||||||
|
--source=HelmRepository/podinfo.flux-system \
|
||||||
|
--chart=podinfo
|
||||||
|
|
||||||
# Create a HelmRelease definition on disk without applying it on the cluster
|
# Create a HelmRelease definition on disk without applying it on the cluster
|
||||||
flux create hr podinfo \
|
flux create hr podinfo \
|
||||||
--source=HelmRepository/podinfo \
|
--source=HelmRepository/podinfo \
|
||||||
--chart=podinfo \
|
--chart=podinfo \
|
||||||
--values=./values.yaml \
|
--values=./values.yaml \
|
||||||
--export > podinfo-release.yaml
|
--export > podinfo-release.yaml`,
|
||||||
`,
|
|
||||||
RunE: createHelmReleaseCmdRun,
|
RunE: createHelmReleaseCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,9 +113,10 @@ type helmReleaseFlags struct {
|
|||||||
chart string
|
chart string
|
||||||
chartVersion string
|
chartVersion string
|
||||||
targetNamespace string
|
targetNamespace string
|
||||||
valuesFile []string
|
valuesFiles []string
|
||||||
valuesFrom flags.HelmReleaseValuesFrom
|
valuesFrom flags.HelmReleaseValuesFrom
|
||||||
saName string
|
saName string
|
||||||
|
crds flags.CRDsPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
var helmReleaseArgs helmReleaseFlags
|
var helmReleaseArgs helmReleaseFlags
|
||||||
@@ -120,11 +126,12 @@ func init() {
|
|||||||
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.source, "source", helmReleaseArgs.source.Description())
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.source, "source", helmReleaseArgs.source.Description())
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chart, "chart", "", "Helm chart name or path")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chart, "chart", "", "Helm chart name or path")
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
|
||||||
createHelmReleaseCmd.Flags().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().StringSliceVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
|
||||||
createHelmReleaseCmd.Flags().StringArrayVar(&helmReleaseArgs.valuesFile, "values", nil, "local path to values.yaml files")
|
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.valuesFiles, "values", nil, "local path to values.yaml files, also accepts comma-separated values")
|
||||||
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
|
||||||
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.crds, "crds", helmReleaseArgs.crds.Description())
|
||||||
createCmd.AddCommand(createHelmReleaseCmd)
|
createCmd.AddCommand(createHelmReleaseCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,8 +172,9 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Chart: helmReleaseArgs.chart,
|
Chart: helmReleaseArgs.chart,
|
||||||
Version: helmReleaseArgs.chartVersion,
|
Version: helmReleaseArgs.chartVersion,
|
||||||
SourceRef: helmv2.CrossNamespaceObjectReference{
|
SourceRef: helmv2.CrossNamespaceObjectReference{
|
||||||
Kind: helmReleaseArgs.source.Kind,
|
Kind: helmReleaseArgs.source.Kind,
|
||||||
Name: helmReleaseArgs.source.Name,
|
Name: helmReleaseArgs.source.Name,
|
||||||
|
Namespace: helmReleaseArgs.source.Namespace,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -178,9 +186,14 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
|
helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(helmReleaseArgs.valuesFile) > 0 {
|
if helmReleaseArgs.crds != "" {
|
||||||
var valuesMap map[string]interface{}
|
helmRelease.Spec.Install = &helmv2.Install{CRDs: helmv2.Create}
|
||||||
for _, v := range helmReleaseArgs.valuesFile {
|
helmRelease.Spec.Upgrade = &helmv2.Upgrade{CRDs: helmv2.CRDsPolicy(helmReleaseArgs.crds.String())}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(helmReleaseArgs.valuesFiles) > 0 {
|
||||||
|
valuesMap := make(map[string]interface{})
|
||||||
|
for _, v := range helmReleaseArgs.valuesFiles {
|
||||||
data, err := ioutil.ReadFile(v)
|
data, err := ioutil.ReadFile(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("reading values from %s failed: %w", v, err)
|
return fmt.Errorf("reading values from %s failed: %w", v, err)
|
||||||
@@ -196,11 +209,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("unmarshaling values from %s failed: %w", v, err)
|
return fmt.Errorf("unmarshaling values from %s failed: %w", v, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if valuesMap == nil {
|
valuesMap = transform.MergeMaps(valuesMap, jsonMap)
|
||||||
valuesMap = jsonMap
|
|
||||||
} else {
|
|
||||||
valuesMap = transform.MergeMaps(valuesMap, jsonMap)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonRaw, err := json.Marshal(valuesMap)
|
jsonRaw, err := json.Marshal(valuesMap)
|
||||||
@@ -219,7 +228,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return exportHelmRelease(helmRelease)
|
return printExport(exportHelmRelease(&helmRelease))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
|||||||
@@ -17,20 +17,17 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const createImageLong = `
|
const createImageLong = `The create image sub-commands work with image automation objects; that is,
|
||||||
The create image sub-commands work with image automation objects; that is,
|
|
||||||
object controlling updates to git based on e.g., new container images
|
object controlling updates to git based on e.g., new container images
|
||||||
being available.`
|
being available.`
|
||||||
|
|
||||||
var createImageCmd = &cobra.Command{
|
var createImageCmd = &cobra.Command{
|
||||||
Use: "image",
|
Use: "image",
|
||||||
Short: "Create or update resources dealing with image automation",
|
Short: "Create or update resources dealing with image automation",
|
||||||
Long: strings.TrimSpace(createImageLong),
|
Long: createImageLong,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImagePolicyCmd = &cobra.Command{
|
var createImagePolicyCmd = &cobra.Command{
|
||||||
@@ -50,8 +50,7 @@ the status of the object.`,
|
|||||||
--image-ref=podinfo \
|
--image-ref=podinfo \
|
||||||
--select-numeric=asc \
|
--select-numeric=asc \
|
||||||
--filter-regex='^main-[a-f0-9]+-(?P<ts>[0-9]+)' \
|
--filter-regex='^main-[a-f0-9]+-(?P<ts>[0-9]+)' \
|
||||||
--filter-extract='$ts'
|
--filter-extract='$ts'`,
|
||||||
`,
|
|
||||||
RunE: createImagePolicyRun}
|
RunE: createImagePolicyRun}
|
||||||
|
|
||||||
type imagePolicyFlags struct {
|
type imagePolicyFlags struct {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImageRepositoryCmd = &cobra.Command{
|
var createImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -57,8 +57,7 @@ An ImageRepository object specifies an image repository to scan.`,
|
|||||||
--cert-file client.crt --key-file client.key
|
--cert-file client.crt --key-file client.key
|
||||||
flux create image repository app-repo \
|
flux create image repository app-repo \
|
||||||
--cert-secret-ref client-cert \
|
--cert-secret-ref client-cert \
|
||||||
--image registry.example.com/private/app --interval 5m
|
--image registry.example.com/private/app --interval 5m`,
|
||||||
`,
|
|
||||||
RunE: createImageRepositoryRun,
|
RunE: createImageRepositoryRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImageUpdateCmd = &cobra.Command{
|
var createImageUpdateCmd = &cobra.Command{
|
||||||
@@ -50,8 +49,7 @@ mentioned in YAMLs in a git repository.`,
|
|||||||
--push-branch=image-updates \
|
--push-branch=image-updates \
|
||||||
--author-name=flux \
|
--author-name=flux \
|
||||||
--author-email=flux@example.com \
|
--author-email=flux@example.com \
|
||||||
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}"
|
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}"`,
|
||||||
`,
|
|
||||||
RunE: createImageUpdateRun,
|
RunE: createImageUpdateRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,25 +112,33 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
|
|||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Spec: autov1.ImageUpdateAutomationSpec{
|
Spec: autov1.ImageUpdateAutomationSpec{
|
||||||
Checkout: autov1.GitCheckoutSpec{
|
SourceRef: autov1.SourceReference{
|
||||||
GitRepositoryRef: meta.LocalObjectReference{
|
Kind: sourcev1.GitRepositoryKind,
|
||||||
Name: imageUpdateArgs.gitRepoRef,
|
Name: imageUpdateArgs.gitRepoRef,
|
||||||
|
},
|
||||||
|
|
||||||
|
GitSpec: &autov1.GitSpec{
|
||||||
|
Checkout: &autov1.GitCheckoutSpec{
|
||||||
|
Reference: sourcev1.GitRepositoryRef{
|
||||||
|
Branch: imageUpdateArgs.checkoutBranch,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Commit: autov1.CommitSpec{
|
||||||
|
Author: autov1.CommitUser{
|
||||||
|
Name: imageUpdateArgs.authorName,
|
||||||
|
Email: imageUpdateArgs.authorEmail,
|
||||||
|
},
|
||||||
|
MessageTemplate: imageUpdateArgs.commitTemplate,
|
||||||
},
|
},
|
||||||
Branch: imageUpdateArgs.checkoutBranch,
|
|
||||||
},
|
},
|
||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: createArgs.interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
Commit: autov1.CommitSpec{
|
|
||||||
AuthorName: imageUpdateArgs.authorName,
|
|
||||||
AuthorEmail: imageUpdateArgs.authorEmail,
|
|
||||||
MessageTemplate: imageUpdateArgs.commitTemplate,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if imageUpdateArgs.pushBranch != "" {
|
if imageUpdateArgs.pushBranch != "" {
|
||||||
update.Spec.Push = &autov1.PushSpec{
|
update.Spec.GitSpec.Push = &autov1.PushSpec{
|
||||||
Branch: imageUpdateArgs.pushBranch,
|
Branch: imageUpdateArgs.pushBranch,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ var createKsCmd = &cobra.Command{
|
|||||||
Long: "The kustomization source create command generates a Kustomize resource for a given source.",
|
Long: "The kustomization source create command generates a Kustomize resource for a given source.",
|
||||||
Example: ` # Create a Kustomization resource from a source at a given path
|
Example: ` # Create a Kustomization resource from a source at a given path
|
||||||
flux create kustomization contour \
|
flux create kustomization contour \
|
||||||
--source=contour \
|
--source=GitRepository/contour \
|
||||||
--path="./examples/contour/" \
|
--path="./examples/contour/" \
|
||||||
--prune=true \
|
--prune=true \
|
||||||
--interval=10m \
|
--interval=10m \
|
||||||
@@ -58,7 +57,16 @@ var createKsCmd = &cobra.Command{
|
|||||||
# Create a Kustomization resource that depends on the previous one
|
# Create a Kustomization resource that depends on the previous one
|
||||||
flux create kustomization webapp \
|
flux create kustomization webapp \
|
||||||
--depends-on=contour \
|
--depends-on=contour \
|
||||||
--source=webapp \
|
--source=GitRepository/webapp \
|
||||||
|
--path="./deploy/overlays/dev" \
|
||||||
|
--prune=true \
|
||||||
|
--interval=5m \
|
||||||
|
--validation=client
|
||||||
|
|
||||||
|
# Create a Kustomization using a source from a different namespace
|
||||||
|
flux create kustomization podinfo \
|
||||||
|
--namespace=default \
|
||||||
|
--source=GitRepository/podinfo.flux-system \
|
||||||
--path="./deploy/overlays/dev" \
|
--path="./deploy/overlays/dev" \
|
||||||
--prune=true \
|
--prune=true \
|
||||||
--interval=5m \
|
--interval=5m \
|
||||||
@@ -68,8 +76,7 @@ var createKsCmd = &cobra.Command{
|
|||||||
flux create kustomization secrets \
|
flux create kustomization secrets \
|
||||||
--source=Bucket/secrets \
|
--source=Bucket/secrets \
|
||||||
--prune=true \
|
--prune=true \
|
||||||
--interval=5m
|
--interval=5m`,
|
||||||
`,
|
|
||||||
RunE: createKsCmdRun,
|
RunE: createKsCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +100,10 @@ func init() {
|
|||||||
createKsCmd.Flags().Var(&kustomizationArgs.source, "source", kustomizationArgs.source.Description())
|
createKsCmd.Flags().Var(&kustomizationArgs.source, "source", kustomizationArgs.source.Description())
|
||||||
createKsCmd.Flags().Var(&kustomizationArgs.path, "path", "path to the directory containing a kustomization.yaml file")
|
createKsCmd.Flags().Var(&kustomizationArgs.path, "path", "path to the directory containing a kustomization.yaml file")
|
||||||
createKsCmd.Flags().BoolVar(&kustomizationArgs.prune, "prune", false, "enable garbage collection")
|
createKsCmd.Flags().BoolVar(&kustomizationArgs.prune, "prune", false, "enable garbage collection")
|
||||||
createKsCmd.Flags().StringArrayVar(&kustomizationArgs.healthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
createKsCmd.Flags().StringSliceVar(&kustomizationArgs.healthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
||||||
createKsCmd.Flags().DurationVar(&kustomizationArgs.healthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
createKsCmd.Flags().DurationVar(&kustomizationArgs.healthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
||||||
createKsCmd.Flags().StringVar(&kustomizationArgs.validation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.validation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
|
||||||
createKsCmd.Flags().StringArrayVar(&kustomizationArgs.dependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'")
|
createKsCmd.Flags().StringSliceVar(&kustomizationArgs.dependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>', also accepts comma-separated values")
|
||||||
createKsCmd.Flags().StringVar(&kustomizationArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this Kustomization")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this Kustomization")
|
||||||
createKsCmd.Flags().Var(&kustomizationArgs.decryptionProvider, "decryption-provider", kustomizationArgs.decryptionProvider.Description())
|
createKsCmd.Flags().Var(&kustomizationArgs.decryptionProvider, "decryption-provider", kustomizationArgs.decryptionProvider.Description())
|
||||||
createKsCmd.Flags().StringVar(&kustomizationArgs.decryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.decryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
|
||||||
@@ -143,11 +150,12 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: createArgs.interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
Path: filepath.ToSlash(kustomizationArgs.path.String()),
|
Path: kustomizationArgs.path.ToSlash(),
|
||||||
Prune: kustomizationArgs.prune,
|
Prune: kustomizationArgs.prune,
|
||||||
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
||||||
Kind: kustomizationArgs.source.Kind,
|
Kind: kustomizationArgs.source.Kind,
|
||||||
Name: kustomizationArgs.source.Name,
|
Name: kustomizationArgs.source.Name,
|
||||||
|
Namespace: kustomizationArgs.source.Namespace,
|
||||||
},
|
},
|
||||||
Suspend: false,
|
Suspend: false,
|
||||||
Validation: kustomizationArgs.validation,
|
Validation: kustomizationArgs.validation,
|
||||||
@@ -211,7 +219,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return exportKs(kustomization)
|
return printExport(exportKs(&kustomization))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ var createReceiverCmd = &cobra.Command{
|
|||||||
--event push \
|
--event push \
|
||||||
--secret-ref webhook-token \
|
--secret-ref webhook-token \
|
||||||
--resource GitRepository/webapp \
|
--resource GitRepository/webapp \
|
||||||
--resource HelmRepository/webapp
|
--resource HelmRepository/webapp`,
|
||||||
`,
|
|
||||||
RunE: createReceiverCmdRun,
|
RunE: createReceiverCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,8 +61,8 @@ var receiverArgs receiverFlags
|
|||||||
func init() {
|
func init() {
|
||||||
createReceiverCmd.Flags().StringVar(&receiverArgs.receiverType, "type", "", "")
|
createReceiverCmd.Flags().StringVar(&receiverArgs.receiverType, "type", "", "")
|
||||||
createReceiverCmd.Flags().StringVar(&receiverArgs.secretRef, "secret-ref", "", "")
|
createReceiverCmd.Flags().StringVar(&receiverArgs.secretRef, "secret-ref", "", "")
|
||||||
createReceiverCmd.Flags().StringArrayVar(&receiverArgs.events, "event", []string{}, "")
|
createReceiverCmd.Flags().StringSliceVar(&receiverArgs.events, "event", []string{}, "also accepts comma-separated values")
|
||||||
createReceiverCmd.Flags().StringArrayVar(&receiverArgs.resources, "resource", []string{}, "")
|
createReceiverCmd.Flags().StringSliceVar(&receiverArgs.resources, "resource", []string{}, "also accepts comma-separated values")
|
||||||
createCmd.AddCommand(createReceiverCmd)
|
createCmd.AddCommand(createReceiverCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +124,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return exportReceiver(receiver)
|
return printExport(exportReceiver(&receiver))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
|||||||
@@ -34,8 +34,7 @@ import (
|
|||||||
var createSecretGitCmd = &cobra.Command{
|
var createSecretGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Create or update a Kubernetes secret for Git authentication",
|
Short: "Create or update a Kubernetes secret for Git authentication",
|
||||||
Long: `
|
Long: `The create secret git command generates a Kubernetes secret with Git credentials.
|
||||||
The create secret git command generates a Kubernetes secret with Git credentials.
|
|
||||||
For Git over SSH, the host and SSH keys are automatically generated and stored in the secret.
|
For Git over SSH, the host and SSH keys are automatically generated and stored in the secret.
|
||||||
For Git over HTTP/S, the provided basic authentication credentials are stored in the secret.`,
|
For Git over HTTP/S, the provided basic authentication credentials are stored in the secret.`,
|
||||||
Example: ` # Create a Git SSH authentication secret using an ECDSA P-521 curve public key
|
Example: ` # Create a Git SSH authentication secret using an ECDSA P-521 curve public key
|
||||||
@@ -45,6 +44,19 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
|
|||||||
--ssh-key-algorithm=ecdsa \
|
--ssh-key-algorithm=ecdsa \
|
||||||
--ssh-ecdsa-curve=p521
|
--ssh-ecdsa-curve=p521
|
||||||
|
|
||||||
|
# Create a Git SSH authentication secret with a passwordless private key from file
|
||||||
|
# The public SSH host key will still be gathered from the host
|
||||||
|
flux create secret git podinfo-auth \
|
||||||
|
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||||
|
--private-key-file=./private.key
|
||||||
|
|
||||||
|
# Create a Git SSH authentication secret with a passworded private key from file
|
||||||
|
# The public SSH host key will still be gathered from the host
|
||||||
|
flux create secret git podinfo-auth \
|
||||||
|
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||||
|
--private-key-file=./private.key \
|
||||||
|
--password=<password>
|
||||||
|
|
||||||
# Create a secret for a Git repository using basic authentication
|
# Create a secret for a Git repository using basic authentication
|
||||||
flux create secret git podinfo-auth \
|
flux create secret git podinfo-auth \
|
||||||
--url=https://github.com/stefanprodan/podinfo \
|
--url=https://github.com/stefanprodan/podinfo \
|
||||||
@@ -65,19 +77,19 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
|
|||||||
--export > podinfo-auth.yaml
|
--export > podinfo-auth.yaml
|
||||||
|
|
||||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
--in-place podinfo-auth.yaml
|
--in-place podinfo-auth.yaml`,
|
||||||
`,
|
|
||||||
RunE: createSecretGitCmdRun,
|
RunE: createSecretGitCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
type secretGitFlags struct {
|
type secretGitFlags struct {
|
||||||
url string
|
url string
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
keyAlgorithm flags.PublicKeyAlgorithm
|
keyAlgorithm flags.PublicKeyAlgorithm
|
||||||
rsaBits flags.RSAKeyBits
|
rsaBits flags.RSAKeyBits
|
||||||
ecdsaCurve flags.ECDSACurve
|
ecdsaCurve flags.ECDSACurve
|
||||||
caFile string
|
caFile string
|
||||||
|
privateKeyFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
var secretGitArgs = NewSecretGitFlags()
|
var secretGitArgs = NewSecretGitFlags()
|
||||||
@@ -90,6 +102,7 @@ func init() {
|
|||||||
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.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().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")
|
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
||||||
|
createSecretGitCmd.Flags().StringVar(&secretGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
|
||||||
|
|
||||||
createSecretCmd.AddCommand(createSecretGitCmd)
|
createSecretCmd.AddCommand(createSecretGitCmd)
|
||||||
}
|
}
|
||||||
@@ -130,9 +143,11 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "ssh":
|
case "ssh":
|
||||||
opts.SSHHostname = u.Host
|
opts.SSHHostname = u.Host
|
||||||
|
opts.PrivateKeyPath = secretGitArgs.privateKeyFile
|
||||||
opts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(secretGitArgs.keyAlgorithm)
|
opts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(secretGitArgs.keyAlgorithm)
|
||||||
opts.RSAKeyBits = int(secretGitArgs.rsaBits)
|
opts.RSAKeyBits = int(secretGitArgs.rsaBits)
|
||||||
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
|
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
|
||||||
|
opts.Password = secretGitArgs.password
|
||||||
case "http", "https":
|
case "http", "https":
|
||||||
if secretGitArgs.username == "" || secretGitArgs.password == "" {
|
if secretGitArgs.username == "" || secretGitArgs.password == "" {
|
||||||
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
||||||
|
|||||||
@@ -31,10 +31,8 @@ import (
|
|||||||
var createSecretHelmCmd = &cobra.Command{
|
var createSecretHelmCmd = &cobra.Command{
|
||||||
Use: "helm [name]",
|
Use: "helm [name]",
|
||||||
Short: "Create or update a Kubernetes secret for Helm repository authentication",
|
Short: "Create or update a Kubernetes secret for Helm repository authentication",
|
||||||
Long: `
|
Long: `The create secret helm command generates a Kubernetes secret with basic authentication credentials.`,
|
||||||
The create secret helm command generates a Kubernetes secret with basic authentication credentials.`,
|
Example: ` # Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
|
||||||
Example: `
|
|
||||||
# Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
|
|
||||||
flux create secret helm repo-auth \
|
flux create secret helm repo-auth \
|
||||||
--namespace=my-namespace \
|
--namespace=my-namespace \
|
||||||
--username=my-username \
|
--username=my-username \
|
||||||
@@ -44,14 +42,13 @@ The create secret helm command generates a Kubernetes secret with basic authenti
|
|||||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
--in-place repo-auth.yaml
|
--in-place repo-auth.yaml
|
||||||
|
|
||||||
# Create an authentication secret using a custom TLS cert
|
# Create a Helm authentication secret using a custom TLS cert
|
||||||
flux create secret helm repo-auth \
|
flux create secret helm repo-auth \
|
||||||
--username=username \
|
--username=username \
|
||||||
--password=password \
|
--password=password \
|
||||||
--cert-file=./cert.crt \
|
--cert-file=./cert.crt \
|
||||||
--key-file=./key.crt \
|
--key-file=./key.crt \
|
||||||
--ca-file=./ca.crt
|
--ca-file=./ca.crt`,
|
||||||
`,
|
|
||||||
RunE: createSecretHelmCmdRun,
|
RunE: createSecretHelmCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,8 @@ import (
|
|||||||
var createSecretTLSCmd = &cobra.Command{
|
var createSecretTLSCmd = &cobra.Command{
|
||||||
Use: "tls [name]",
|
Use: "tls [name]",
|
||||||
Short: "Create or update a Kubernetes secret with TLS certificates",
|
Short: "Create or update a Kubernetes secret with TLS certificates",
|
||||||
Long: `
|
Long: `The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`,
|
||||||
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.
|
||||||
Example: `
|
|
||||||
# Create a TLS secret on disk and encrypt it with Mozilla SOPS.
|
|
||||||
# Files are expected to be PEM-encoded.
|
# Files are expected to be PEM-encoded.
|
||||||
flux create secret tls certs \
|
flux create secret tls certs \
|
||||||
--namespace=my-namespace \
|
--namespace=my-namespace \
|
||||||
@@ -44,8 +42,7 @@ The create secret tls command generates a Kubernetes secret with certificates fo
|
|||||||
--export > certs.yaml
|
--export > certs.yaml
|
||||||
|
|
||||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
--in-place certs.yaml
|
--in-place certs.yaml`,
|
||||||
`,
|
|
||||||
RunE: createSecretTLSCmdRun,
|
RunE: createSecretTLSCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,9 @@ import (
|
|||||||
var createSourceBucketCmd = &cobra.Command{
|
var createSourceBucketCmd = &cobra.Command{
|
||||||
Use: "bucket [name]",
|
Use: "bucket [name]",
|
||||||
Short: "Create or update a Bucket source",
|
Short: "Create or update a Bucket source",
|
||||||
Long: `
|
Long: `The create source bucket command generates a Bucket resource and waits for it to be downloaded.
|
||||||
The create source bucket command generates a Bucket resource and waits for it to be downloaded.
|
|
||||||
For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`,
|
For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`,
|
||||||
Example: ` # Create a source from a Buckets using static authentication
|
Example: ` # Create a source for a Bucket using static authentication
|
||||||
flux create source bucket podinfo \
|
flux create source bucket podinfo \
|
||||||
--bucket-name=podinfo \
|
--bucket-name=podinfo \
|
||||||
--endpoint=minio.minio.svc.cluster.local:9000 \
|
--endpoint=minio.minio.svc.cluster.local:9000 \
|
||||||
@@ -52,14 +51,13 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
|
|||||||
--secret-key=mysecretkey \
|
--secret-key=mysecretkey \
|
||||||
--interval=10m
|
--interval=10m
|
||||||
|
|
||||||
# Create a source from an Amazon S3 Bucket using IAM authentication
|
# Create a source for an Amazon S3 Bucket using IAM authentication
|
||||||
flux create source bucket podinfo \
|
flux create source bucket podinfo \
|
||||||
--bucket-name=podinfo \
|
--bucket-name=podinfo \
|
||||||
--provider=aws \
|
--provider=aws \
|
||||||
--endpoint=s3.amazonaws.com \
|
--endpoint=s3.amazonaws.com \
|
||||||
--region=us-east-1 \
|
--region=us-east-1 \
|
||||||
--interval=10m
|
--interval=10m`,
|
||||||
`,
|
|
||||||
RunE: createSourceBucketCmdRun,
|
RunE: createSourceBucketCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +142,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return exportBucket(*bucket)
|
return printExport(exportBucket(bucket))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
|||||||
@@ -49,19 +49,20 @@ type sourceGitFlags struct {
|
|||||||
semver string
|
semver string
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
caFile string
|
|
||||||
keyAlgorithm flags.PublicKeyAlgorithm
|
keyAlgorithm flags.PublicKeyAlgorithm
|
||||||
keyRSABits flags.RSAKeyBits
|
keyRSABits flags.RSAKeyBits
|
||||||
keyECDSACurve flags.ECDSACurve
|
keyECDSACurve flags.ECDSACurve
|
||||||
secretRef string
|
secretRef string
|
||||||
gitImplementation flags.GitImplementation
|
gitImplementation flags.GitImplementation
|
||||||
|
caFile string
|
||||||
|
privateKeyFile string
|
||||||
|
recurseSubmodules bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var createSourceGitCmd = &cobra.Command{
|
var createSourceGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Create or update a GitRepository source",
|
Short: "Create or update a GitRepository source",
|
||||||
Long: `
|
Long: `The create source git command generates a GitRepository resource and waits for it to sync.
|
||||||
The create source git command generates a GitRepository resource and waits for it to sync.
|
|
||||||
For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
|
For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
|
||||||
For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
|
For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
|
||||||
Example: ` # Create a source from a public Git repository master branch
|
Example: ` # Create a source from a public Git repository master branch
|
||||||
@@ -69,7 +70,7 @@ For private Git repositories, the basic authentication credentials are stored in
|
|||||||
--url=https://github.com/stefanprodan/podinfo \
|
--url=https://github.com/stefanprodan/podinfo \
|
||||||
--branch=master
|
--branch=master
|
||||||
|
|
||||||
# Create a source from a Git repository pinned to specific git tag
|
# Create a source for a Git repository pinned to specific git tag
|
||||||
flux create source git podinfo \
|
flux create source git podinfo \
|
||||||
--url=https://github.com/stefanprodan/podinfo \
|
--url=https://github.com/stefanprodan/podinfo \
|
||||||
--tag="3.2.3"
|
--tag="3.2.3"
|
||||||
@@ -79,12 +80,12 @@ For private Git repositories, the basic authentication credentials are stored in
|
|||||||
--url=https://github.com/stefanprodan/podinfo \
|
--url=https://github.com/stefanprodan/podinfo \
|
||||||
--tag-semver=">=3.2.0 <3.3.0"
|
--tag-semver=">=3.2.0 <3.3.0"
|
||||||
|
|
||||||
# Create a source from a Git repository using SSH authentication
|
# Create a source for a Git repository using SSH authentication
|
||||||
flux create source git podinfo \
|
flux create source git podinfo \
|
||||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||||
--branch=master
|
--branch=master
|
||||||
|
|
||||||
# Create a source from a Git repository using SSH authentication and an
|
# Create a source for a Git repository using SSH authentication and an
|
||||||
# ECDSA P-521 curve public key
|
# ECDSA P-521 curve public key
|
||||||
flux create source git podinfo \
|
flux create source git podinfo \
|
||||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||||
@@ -92,12 +93,28 @@ For private Git repositories, the basic authentication credentials are stored in
|
|||||||
--ssh-key-algorithm=ecdsa \
|
--ssh-key-algorithm=ecdsa \
|
||||||
--ssh-ecdsa-curve=p521
|
--ssh-ecdsa-curve=p521
|
||||||
|
|
||||||
# Create a source from a Git repository using basic authentication
|
# Create a source for a Git repository using SSH authentication and a
|
||||||
|
# passwordless private key from file
|
||||||
|
# The public SSH host key will still be gathered from the host
|
||||||
|
flux create source git podinfo \
|
||||||
|
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||||
|
--branch=master \
|
||||||
|
--private-key-file=./private.key
|
||||||
|
|
||||||
|
# Create a source for a Git repository using SSH authentication and a
|
||||||
|
# private key with a password from file
|
||||||
|
# The public SSH host key will still be gathered from the host
|
||||||
|
flux create source git podinfo \
|
||||||
|
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||||
|
--branch=master \
|
||||||
|
--private-key-file=./private.key \
|
||||||
|
--password=<password>
|
||||||
|
|
||||||
|
# Create a source for a Git repository using basic authentication
|
||||||
flux create source git podinfo \
|
flux create source git podinfo \
|
||||||
--url=https://github.com/stefanprodan/podinfo \
|
--url=https://github.com/stefanprodan/podinfo \
|
||||||
--username=username \
|
--username=username \
|
||||||
--password=password
|
--password=password`,
|
||||||
`,
|
|
||||||
RunE: createSourceGitCmdRun,
|
RunE: createSourceGitCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +122,7 @@ var sourceGitArgs = newSourceGitFlags()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.url, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
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.branch, "branch", "", "git branch")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.tag, "tag", "", "git tag")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.tag, "tag", "", "git tag")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.semver, "tag-semver", "", "git tag semver range")
|
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.username, "username", "u", "", "basic authentication username")
|
||||||
@@ -115,7 +132,10 @@ func init() {
|
|||||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyECDSACurve, "ssh-ecdsa-curve", sourceGitArgs.keyECDSACurve.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().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().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")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
||||||
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
|
||||||
|
createSourceGitCmd.Flags().BoolVar(&sourceGitArgs.recurseSubmodules, "recurse-submodules", false,
|
||||||
|
"when enabled, configures the GitRepository source to initialize and include Git submodules in the artifact it produces")
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceGitCmd)
|
createSourceCmd.AddCommand(createSourceGitCmd)
|
||||||
}
|
}
|
||||||
@@ -138,16 +158,6 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("url is required")
|
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(sourceGitArgs.url)
|
u, err := url.Parse(sourceGitArgs.url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
return fmt.Errorf("git URL parse failed: %w", err)
|
||||||
@@ -156,6 +166,24 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sourceGitArgs.branch == "" && sourceGitArgs.tag == "" && sourceGitArgs.semver == "" {
|
||||||
|
return fmt.Errorf("a Git ref is required, use one of the following: --branch, --tag or --tag-semver")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceGitArgs.caFile != "" && u.Scheme == "ssh" {
|
||||||
|
return fmt.Errorf("specifing a CA file is not supported for Git over SSH")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceGitArgs.recurseSubmodules && sourceGitArgs.gitImplementation == sourcev1.LibGit2Implementation {
|
||||||
|
return fmt.Errorf("recurse submodules requires --git-implementation=%s", sourcev1.GoGitImplementation)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir, err := ioutil.TempDir("", name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
sourceLabels, err := parseLabels()
|
sourceLabels, err := parseLabels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -172,7 +200,8 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: createArgs.interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
Reference: &sourcev1.GitRepositoryRef{},
|
RecurseSubmodules: sourceGitArgs.recurseSubmodules,
|
||||||
|
Reference: &sourcev1.GitRepositoryRef{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +224,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return exportGit(gitRepository)
|
return printExport(exportGit(&gitRepository))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
@@ -216,13 +245,19 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "ssh":
|
case "ssh":
|
||||||
secretOpts.SSHHostname = u.Host
|
secretOpts.SSHHostname = u.Host
|
||||||
|
secretOpts.PrivateKeyPath = sourceGitArgs.privateKeyFile
|
||||||
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(sourceGitArgs.keyAlgorithm)
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(sourceGitArgs.keyAlgorithm)
|
||||||
secretOpts.RSAKeyBits = int(sourceGitArgs.keyRSABits)
|
secretOpts.RSAKeyBits = int(sourceGitArgs.keyRSABits)
|
||||||
secretOpts.ECDSACurve = sourceGitArgs.keyECDSACurve.Curve
|
secretOpts.ECDSACurve = sourceGitArgs.keyECDSACurve.Curve
|
||||||
|
secretOpts.Password = sourceGitArgs.password
|
||||||
case "https":
|
case "https":
|
||||||
secretOpts.Username = sourceGitArgs.username
|
secretOpts.Username = sourceGitArgs.username
|
||||||
secretOpts.Password = sourceGitArgs.password
|
secretOpts.Password = sourceGitArgs.password
|
||||||
secretOpts.CAFilePath = sourceGitArgs.caFile
|
secretOpts.CAFilePath = sourceGitArgs.caFile
|
||||||
|
case "http":
|
||||||
|
logger.Warningf("insecure configuration: credentials configured for an HTTP URL")
|
||||||
|
secretOpts.Username = sourceGitArgs.username
|
||||||
|
secretOpts.Password = sourceGitArgs.password
|
||||||
}
|
}
|
||||||
secret, err := sourcesecret.Generate(secretOpts)
|
secret, err := sourcesecret.Generate(secretOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -43,27 +43,25 @@ import (
|
|||||||
var createSourceHelmCmd = &cobra.Command{
|
var createSourceHelmCmd = &cobra.Command{
|
||||||
Use: "helm [name]",
|
Use: "helm [name]",
|
||||||
Short: "Create or update a HelmRepository source",
|
Short: "Create or update a HelmRepository source",
|
||||||
Long: `
|
Long: `The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
|
||||||
The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
|
|
||||||
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
|
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
|
||||||
Example: ` # Create a source from a public Helm repository
|
Example: ` # Create a source for a public Helm repository
|
||||||
flux create source helm podinfo \
|
flux create source helm podinfo \
|
||||||
--url=https://stefanprodan.github.io/podinfo \
|
--url=https://stefanprodan.github.io/podinfo \
|
||||||
--interval=10m
|
--interval=10m
|
||||||
|
|
||||||
# Create a source from a Helm repository using basic authentication
|
# Create a source for a Helm repository using basic authentication
|
||||||
flux create source helm podinfo \
|
flux create source helm podinfo \
|
||||||
--url=https://stefanprodan.github.io/podinfo \
|
--url=https://stefanprodan.github.io/podinfo \
|
||||||
--username=username \
|
--username=username \
|
||||||
--password=password
|
--password=password
|
||||||
|
|
||||||
# Create a source from a Helm repository using TLS authentication
|
# Create a source for a Helm repository using TLS authentication
|
||||||
flux create source helm podinfo \
|
flux create source helm podinfo \
|
||||||
--url=https://stefanprodan.github.io/podinfo \
|
--url=https://stefanprodan.github.io/podinfo \
|
||||||
--cert-file=./cert.crt \
|
--cert-file=./cert.crt \
|
||||||
--key-file=./key.crt \
|
--key-file=./key.crt \
|
||||||
--ca-file=./ca.crt
|
--ca-file=./ca.crt`,
|
||||||
`,
|
|
||||||
RunE: createSourceHelmCmdRun,
|
RunE: createSourceHelmCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +135,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return exportHelmRepository(*helmRepository)
|
return printExport(exportHelmRepository(helmRepository))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
|||||||
@@ -37,8 +37,7 @@ import (
|
|||||||
var createTenantCmd = &cobra.Command{
|
var createTenantCmd = &cobra.Command{
|
||||||
Use: "tenant",
|
Use: "tenant",
|
||||||
Short: "Create or update a tenant",
|
Short: "Create or update a tenant",
|
||||||
Long: `
|
Long: `The create tenant command generates namespaces, service accounts and role bindings to limit the
|
||||||
The create tenant command generates namespaces, service accounts and role bindings to limit the
|
|
||||||
reconcilers scope to the tenant namespaces.`,
|
reconcilers scope to the tenant namespaces.`,
|
||||||
Example: ` # Create a tenant with access to a namespace
|
Example: ` # Create a tenant with access to a namespace
|
||||||
flux create tenant dev-team \
|
flux create tenant dev-team \
|
||||||
@@ -49,8 +48,7 @@ reconcilers scope to the tenant namespaces.`,
|
|||||||
flux create tenant dev-team \
|
flux create tenant dev-team \
|
||||||
--with-namespace=frontend \
|
--with-namespace=frontend \
|
||||||
--with-namespace=backend \
|
--with-namespace=backend \
|
||||||
--export > dev-team.yaml
|
--export > dev-team.yaml`,
|
||||||
`,
|
|
||||||
RunE: createTenantCmdRun,
|
RunE: createTenantCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,14 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/manifoldco/promptui"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,56 +27,13 @@ var deleteAlertCmd = &cobra.Command{
|
|||||||
Short: "Delete a Alert resource",
|
Short: "Delete a Alert resource",
|
||||||
Long: "The delete alert command removes the given Alert from the cluster.",
|
Long: "The delete alert command removes the given Alert from the cluster.",
|
||||||
Example: ` # Delete an Alert and the Kubernetes resources created by it
|
Example: ` # Delete an Alert and the Kubernetes resources created by it
|
||||||
flux delete alert main
|
flux delete alert main`,
|
||||||
`,
|
RunE: deleteCommand{
|
||||||
RunE: deleteAlertCmdRun,
|
apiType: alertType,
|
||||||
|
object: universalAdapter{¬ificationv1.Alert{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteCmd.AddCommand(deleteAlertCmd)
|
deleteCmd.AddCommand(deleteAlertCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("alert 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 alert notificationv1.Alert
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &alert)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !deleteArgs.silent {
|
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Are you sure you want to delete this Alert",
|
|
||||||
IsConfirm: true,
|
|
||||||
}
|
|
||||||
if _, err := prompt.Run(); err != nil {
|
|
||||||
return fmt.Errorf("aborting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("deleting alert %s in %s namespace", name, rootArgs.namespace)
|
|
||||||
err = kubeClient.Delete(ctx, &alert)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("alert deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,14 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/manifoldco/promptui"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,56 +27,13 @@ var deleteAlertProviderCmd = &cobra.Command{
|
|||||||
Short: "Delete a Provider resource",
|
Short: "Delete a Provider resource",
|
||||||
Long: "The delete alert-provider command removes the given Provider from the cluster.",
|
Long: "The delete alert-provider command removes the given Provider from the cluster.",
|
||||||
Example: ` # Delete a Provider and the Kubernetes resources created by it
|
Example: ` # Delete a Provider and the Kubernetes resources created by it
|
||||||
flux delete alert-provider slack
|
flux delete alert-provider slack`,
|
||||||
`,
|
RunE: deleteCommand{
|
||||||
RunE: deleteAlertProviderCmdRun,
|
apiType: alertProviderType,
|
||||||
|
object: universalAdapter{¬ificationv1.Provider{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteCmd.AddCommand(deleteAlertProviderCmd)
|
deleteCmd.AddCommand(deleteAlertProviderCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("provider 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 alertProvider notificationv1.Provider
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !deleteArgs.silent {
|
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Are you sure you want to delete this Provider",
|
|
||||||
IsConfirm: true,
|
|
||||||
}
|
|
||||||
if _, err := prompt.Run(); err != nil {
|
|
||||||
return fmt.Errorf("aborting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("deleting provider %s in %s namespace", name, rootArgs.namespace)
|
|
||||||
err = kubeClient.Delete(ctx, &alertProvider)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("provider deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteHelmReleaseCmd = &cobra.Command{
|
var deleteHelmReleaseCmd = &cobra.Command{
|
||||||
@@ -27,8 +28,7 @@ var deleteHelmReleaseCmd = &cobra.Command{
|
|||||||
Short: "Delete a HelmRelease resource",
|
Short: "Delete a HelmRelease resource",
|
||||||
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
|
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
|
||||||
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
||||||
flux delete hr podinfo
|
flux delete hr podinfo`,
|
||||||
`,
|
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: helmReleaseType,
|
apiType: helmReleaseType,
|
||||||
object: universalAdapter{&helmv2.HelmRelease{}},
|
object: universalAdapter{&helmv2.HelmRelease{}},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImagePolicyCmd = &cobra.Command{
|
var deleteImagePolicyCmd = &cobra.Command{
|
||||||
@@ -27,8 +27,7 @@ var deleteImagePolicyCmd = &cobra.Command{
|
|||||||
Short: "Delete an ImagePolicy object",
|
Short: "Delete an ImagePolicy object",
|
||||||
Long: "The delete 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
|
Example: ` # Delete an image policy
|
||||||
flux delete image policy alpine3.x
|
flux delete image policy alpine3.x`,
|
||||||
`,
|
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: imagePolicyType,
|
apiType: imagePolicyType,
|
||||||
object: universalAdapter{&imagev1.ImagePolicy{}},
|
object: universalAdapter{&imagev1.ImagePolicy{}},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImageRepositoryCmd = &cobra.Command{
|
var deleteImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -27,8 +27,7 @@ var deleteImageRepositoryCmd = &cobra.Command{
|
|||||||
Short: "Delete an ImageRepository object",
|
Short: "Delete an ImageRepository object",
|
||||||
Long: "The delete 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
|
Example: ` # Delete an image repository
|
||||||
flux delete image repository alpine
|
flux delete image repository alpine`,
|
||||||
`,
|
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: imageRepositoryType,
|
apiType: imageRepositoryType,
|
||||||
object: universalAdapter{&imagev1.ImageRepository{}},
|
object: universalAdapter{&imagev1.ImageRepository{}},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImageUpdateCmd = &cobra.Command{
|
var deleteImageUpdateCmd = &cobra.Command{
|
||||||
@@ -27,8 +27,7 @@ var deleteImageUpdateCmd = &cobra.Command{
|
|||||||
Short: "Delete an ImageUpdateAutomation object",
|
Short: "Delete an ImageUpdateAutomation object",
|
||||||
Long: "The delete 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
|
Example: ` # Delete an image update automation
|
||||||
flux delete image update latest-images
|
flux delete image update latest-images`,
|
||||||
`,
|
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: imageUpdateAutomationType,
|
apiType: imageUpdateAutomationType,
|
||||||
object: universalAdapter{&autov1.ImageUpdateAutomation{}},
|
object: universalAdapter{&autov1.ImageUpdateAutomation{}},
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteKsCmd = &cobra.Command{
|
var deleteKsCmd = &cobra.Command{
|
||||||
@@ -27,8 +28,7 @@ var deleteKsCmd = &cobra.Command{
|
|||||||
Short: "Delete a Kustomization resource",
|
Short: "Delete a Kustomization resource",
|
||||||
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
|
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
|
||||||
Example: ` # Delete a kustomization and the Kubernetes resources created by it
|
Example: ` # Delete a kustomization and the Kubernetes resources created by it
|
||||||
flux delete kustomization podinfo
|
flux delete kustomization podinfo`,
|
||||||
`,
|
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: kustomizationType,
|
apiType: kustomizationType,
|
||||||
object: universalAdapter{&kustomizev1.Kustomization{}},
|
object: universalAdapter{&kustomizev1.Kustomization{}},
|
||||||
|
|||||||
@@ -17,14 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/manifoldco/promptui"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,56 +27,13 @@ var deleteReceiverCmd = &cobra.Command{
|
|||||||
Short: "Delete a Receiver resource",
|
Short: "Delete a Receiver resource",
|
||||||
Long: "The delete receiver command removes the given Receiver from the cluster.",
|
Long: "The delete receiver command removes the given Receiver from the cluster.",
|
||||||
Example: ` # Delete an Receiver and the Kubernetes resources created by it
|
Example: ` # Delete an Receiver and the Kubernetes resources created by it
|
||||||
flux delete receiver main
|
flux delete receiver main`,
|
||||||
`,
|
RunE: deleteCommand{
|
||||||
RunE: deleteReceiverCmdRun,
|
apiType: receiverType,
|
||||||
|
object: universalAdapter{¬ificationv1.Receiver{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteCmd.AddCommand(deleteReceiverCmd)
|
deleteCmd.AddCommand(deleteReceiverCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("receiver 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 receiver notificationv1.Receiver
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &receiver)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !deleteArgs.silent {
|
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Are you sure you want to delete this Receiver",
|
|
||||||
IsConfirm: true,
|
|
||||||
}
|
|
||||||
if _, err := prompt.Run(); err != nil {
|
|
||||||
return fmt.Errorf("aborting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("deleting receiver %s in %s namespace", name, rootArgs.namespace)
|
|
||||||
err = kubeClient.Delete(ctx, &receiver)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("receiver deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteSourceBucketCmd = &cobra.Command{
|
var deleteSourceBucketCmd = &cobra.Command{
|
||||||
@@ -26,8 +27,7 @@ var deleteSourceBucketCmd = &cobra.Command{
|
|||||||
Short: "Delete a Bucket source",
|
Short: "Delete a Bucket source",
|
||||||
Long: "The delete source bucket command deletes the given Bucket from the cluster.",
|
Long: "The delete source bucket command deletes the given Bucket from the cluster.",
|
||||||
Example: ` # Delete a Bucket source
|
Example: ` # Delete a Bucket source
|
||||||
flux delete source bucket podinfo
|
flux delete source bucket podinfo`,
|
||||||
`,
|
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: bucketType,
|
apiType: bucketType,
|
||||||
object: universalAdapter{&sourcev1.Bucket{}},
|
object: universalAdapter{&sourcev1.Bucket{}},
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteSourceGitCmd = &cobra.Command{
|
var deleteSourceGitCmd = &cobra.Command{
|
||||||
@@ -26,8 +27,7 @@ var deleteSourceGitCmd = &cobra.Command{
|
|||||||
Short: "Delete a GitRepository source",
|
Short: "Delete a GitRepository source",
|
||||||
Long: "The delete source git command deletes the given GitRepository from the cluster.",
|
Long: "The delete source git command deletes the given GitRepository from the cluster.",
|
||||||
Example: ` # Delete a Git repository
|
Example: ` # Delete a Git repository
|
||||||
flux delete source git podinfo
|
flux delete source git podinfo`,
|
||||||
`,
|
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: gitRepositoryType,
|
apiType: gitRepositoryType,
|
||||||
object: universalAdapter{&sourcev1.GitRepository{}},
|
object: universalAdapter{&sourcev1.GitRepository{}},
|
||||||
|
|||||||
@@ -17,14 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
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"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteSourceHelmCmd = &cobra.Command{
|
var deleteSourceHelmCmd = &cobra.Command{
|
||||||
@@ -32,8 +27,7 @@ var deleteSourceHelmCmd = &cobra.Command{
|
|||||||
Short: "Delete a HelmRepository source",
|
Short: "Delete a HelmRepository source",
|
||||||
Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
|
Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
|
||||||
Example: ` # Delete a Helm repository
|
Example: ` # Delete a Helm repository
|
||||||
flux delete source helm podinfo
|
flux delete source helm podinfo`,
|
||||||
`,
|
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: helmRepositoryType,
|
apiType: helmRepositoryType,
|
||||||
object: universalAdapter{&sourcev1.HelmRepository{}},
|
object: universalAdapter{&sourcev1.HelmRepository{}},
|
||||||
@@ -43,48 +37,3 @@ var deleteSourceHelmCmd = &cobra.Command{
|
|||||||
func init() {
|
func init() {
|
||||||
deleteSourceCmd.AddCommand(deleteSourceHelmCmd)
|
deleteSourceCmd.AddCommand(deleteSourceHelmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteSourceHelmCmdRun(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 helmRepository sourcev1.HelmRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
|
|
||||||
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, &helmRepository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
69
cmd/flux/docgen.go
Normal file
69
cmd/flux/docgen.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/cobra/doc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fmTemplate = `---
|
||||||
|
title: "%s"
|
||||||
|
---
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
cmdDocPath string
|
||||||
|
)
|
||||||
|
|
||||||
|
var docgenCmd = &cobra.Command{
|
||||||
|
Use: "docgen",
|
||||||
|
Short: "Generate the documentation for the CLI commands.",
|
||||||
|
Hidden: true,
|
||||||
|
RunE: docgenCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
docgenCmd.Flags().StringVar(&cmdDocPath, "path", "./docs/cmd", "path to write the generated documentation to")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(docgenCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func docgenCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
err := doc.GenMarkdownTreeCustom(rootCmd, cmdDocPath, frontmatterPrepender, linkHandler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func frontmatterPrepender(filename string) string {
|
||||||
|
name := filepath.Base(filename)
|
||||||
|
base := strings.TrimSuffix(name, path.Ext(name))
|
||||||
|
title := strings.Replace(base, "_", " ", -1)
|
||||||
|
return fmt.Sprintf(fmTemplate, title)
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkHandler(name string) string {
|
||||||
|
base := strings.TrimSuffix(name, path.Ext(name))
|
||||||
|
return "../" + strings.ToLower(base) + "/"
|
||||||
|
}
|
||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
@@ -86,8 +85,7 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if export.list.len() == 0 {
|
if export.list.len() == 0 {
|
||||||
logger.Failuref("no objects found in %s namespace", rootArgs.namespace)
|
return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < export.list.len(); i++ {
|
for i := 0; i < export.list.len(); i++ {
|
||||||
|
|||||||
@@ -17,16 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,62 +31,18 @@ var exportAlertCmd = &cobra.Command{
|
|||||||
flux export alert --all > alerts.yaml
|
flux export alert --all > alerts.yaml
|
||||||
|
|
||||||
# Export a Alert
|
# Export a Alert
|
||||||
flux export alert main > main.yaml
|
flux export alert main > main.yaml`,
|
||||||
`,
|
RunE: exportCommand{
|
||||||
RunE: exportAlertCmdRun,
|
object: alertAdapter{¬ificationv1.Alert{}},
|
||||||
|
list: alertListAdapter{¬ificationv1.AlertList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportCmd.AddCommand(exportAlertCmd)
|
exportCmd.AddCommand(exportAlertCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
|
func exportAlert(alert *notificationv1.Alert) interface{} {
|
||||||
if !exportArgs.all && len(args) < 1 {
|
|
||||||
return fmt.Errorf("name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if exportArgs.all {
|
|
||||||
var list notificationv1.AlertList
|
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no alerts found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, alert := range list.Items {
|
|
||||||
if err := exportAlert(alert); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := args[0]
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var alert notificationv1.Alert
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &alert)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return exportAlert(alert)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportAlert(alert notificationv1.Alert) error {
|
|
||||||
gvk := notificationv1.GroupVersion.WithKind("Alert")
|
gvk := notificationv1.GroupVersion.WithKind("Alert")
|
||||||
export := notificationv1.Alert{
|
export := notificationv1.Alert{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -109,12 +58,13 @@ func exportAlert(alert notificationv1.Alert) error {
|
|||||||
Spec: alert.Spec,
|
Spec: alert.Spec,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := yaml.Marshal(export)
|
return export
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
func (ex alertAdapter) export() interface{} {
|
||||||
|
return exportAlert(ex.Alert)
|
||||||
fmt.Println("---")
|
}
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
return nil
|
func (ex alertListAdapter) exportItem(i int) interface{} {
|
||||||
|
return exportAlert(&ex.AlertList.Items[i])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,62 +31,18 @@ var exportAlertProviderCmd = &cobra.Command{
|
|||||||
flux export alert-provider --all > alert-providers.yaml
|
flux export alert-provider --all > alert-providers.yaml
|
||||||
|
|
||||||
# Export a Provider
|
# Export a Provider
|
||||||
flux export alert-provider slack > slack.yaml
|
flux export alert-provider slack > slack.yaml`,
|
||||||
`,
|
RunE: exportCommand{
|
||||||
RunE: exportAlertProviderCmdRun,
|
object: alertProviderAdapter{¬ificationv1.Provider{}},
|
||||||
|
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportCmd.AddCommand(exportAlertProviderCmd)
|
exportCmd.AddCommand(exportAlertProviderCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
func exportAlertProvider(alertProvider *notificationv1.Provider) interface{} {
|
||||||
if !exportArgs.all && len(args) < 1 {
|
|
||||||
return fmt.Errorf("name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if exportArgs.all {
|
|
||||||
var list notificationv1.ProviderList
|
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no alertproviders found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, alertProvider := range list.Items {
|
|
||||||
if err := exportAlertProvider(alertProvider); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := args[0]
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var alertProvider notificationv1.Provider
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return exportAlertProvider(alertProvider)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportAlertProvider(alertProvider notificationv1.Provider) error {
|
|
||||||
gvk := notificationv1.GroupVersion.WithKind("Provider")
|
gvk := notificationv1.GroupVersion.WithKind("Provider")
|
||||||
export := notificationv1.Provider{
|
export := notificationv1.Provider{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -108,13 +57,13 @@ func exportAlertProvider(alertProvider notificationv1.Provider) error {
|
|||||||
},
|
},
|
||||||
Spec: alertProvider.Spec,
|
Spec: alertProvider.Spec,
|
||||||
}
|
}
|
||||||
|
return export
|
||||||
data, err := yaml.Marshal(export)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
func (ex alertProviderAdapter) export() interface{} {
|
||||||
}
|
return exportAlertProvider(ex.Provider)
|
||||||
|
}
|
||||||
fmt.Println("---")
|
|
||||||
fmt.Println(resourceToString(data))
|
func (ex alertProviderListAdapter) exportItem(i int) interface{} {
|
||||||
return nil
|
return exportAlertProvider(&ex.ProviderList.Items[i])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,62 +32,18 @@ var exportHelmReleaseCmd = &cobra.Command{
|
|||||||
flux export helmrelease --all > kustomizations.yaml
|
flux export helmrelease --all > kustomizations.yaml
|
||||||
|
|
||||||
# Export a HelmRelease
|
# Export a HelmRelease
|
||||||
flux export hr my-app > app-release.yaml
|
flux export hr my-app > app-release.yaml`,
|
||||||
`,
|
RunE: exportCommand{
|
||||||
RunE: exportHelmReleaseCmdRun,
|
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
|
||||||
|
list: helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportCmd.AddCommand(exportHelmReleaseCmd)
|
exportCmd.AddCommand(exportHelmReleaseCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
func exportHelmRelease(helmRelease *helmv2.HelmRelease) interface{} {
|
||||||
if !exportArgs.all && len(args) < 1 {
|
|
||||||
return fmt.Errorf("name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if exportArgs.all {
|
|
||||||
var list helmv2.HelmReleaseList
|
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no helmrelease found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, helmRelease := range list.Items {
|
|
||||||
if err := exportHelmRelease(helmRelease); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := args[0]
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var helmRelease helmv2.HelmRelease
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return exportHelmRelease(helmRelease)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportHelmRelease(helmRelease helmv2.HelmRelease) error {
|
|
||||||
gvk := helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)
|
gvk := helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)
|
||||||
export := helmv2.HelmRelease{
|
export := helmv2.HelmRelease{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -109,13 +58,13 @@ func exportHelmRelease(helmRelease helmv2.HelmRelease) error {
|
|||||||
},
|
},
|
||||||
Spec: helmRelease.Spec,
|
Spec: helmRelease.Spec,
|
||||||
}
|
}
|
||||||
|
return export
|
||||||
data, err := yaml.Marshal(export)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
func (ex helmReleaseAdapter) export() interface{} {
|
||||||
}
|
return exportHelmRelease(ex.HelmRelease)
|
||||||
|
}
|
||||||
fmt.Println("---")
|
|
||||||
fmt.Println(resourceToString(data))
|
func (ex helmReleaseListAdapter) exportItem(i int) interface{} {
|
||||||
return nil
|
return exportHelmRelease(&ex.HelmReleaseList.Items[i])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImagePolicyCmd = &cobra.Command{
|
var exportImagePolicyCmd = &cobra.Command{
|
||||||
@@ -31,8 +31,7 @@ var exportImagePolicyCmd = &cobra.Command{
|
|||||||
flux export image policy --all > image-policies.yaml
|
flux export image policy --all > image-policies.yaml
|
||||||
|
|
||||||
# Export a specific policy
|
# Export a specific policy
|
||||||
flux export image policy alpine1x > alpine1x.yaml
|
flux export image policy alpine1x > alpine1x.yaml`,
|
||||||
`,
|
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
|
object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
|
||||||
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImageRepositoryCmd = &cobra.Command{
|
var exportImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -31,8 +31,7 @@ var exportImageRepositoryCmd = &cobra.Command{
|
|||||||
flux export image repository --all > image-repositories.yaml
|
flux export image repository --all > image-repositories.yaml
|
||||||
|
|
||||||
# Export a specific ImageRepository resource
|
# Export a specific ImageRepository resource
|
||||||
flux export image repository alpine > alpine.yaml
|
flux export image repository alpine > alpine.yaml`,
|
||||||
`,
|
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
||||||
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImageUpdateCmd = &cobra.Command{
|
var exportImageUpdateCmd = &cobra.Command{
|
||||||
@@ -31,8 +31,7 @@ var exportImageUpdateCmd = &cobra.Command{
|
|||||||
flux export image update --all > updates.yaml
|
flux export image update --all > updates.yaml
|
||||||
|
|
||||||
# Export a specific automation
|
# Export a specific automation
|
||||||
flux export image update latest-images > latest.yaml
|
flux export image update latest-images > latest.yaml`,
|
||||||
`,
|
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
||||||
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
||||||
|
|||||||
@@ -17,16 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,62 +32,18 @@ var exportKsCmd = &cobra.Command{
|
|||||||
flux export kustomization --all > kustomizations.yaml
|
flux export kustomization --all > kustomizations.yaml
|
||||||
|
|
||||||
# Export a Kustomization
|
# Export a Kustomization
|
||||||
flux export kustomization my-app > kustomization.yaml
|
flux export kustomization my-app > kustomization.yaml`,
|
||||||
`,
|
RunE: exportCommand{
|
||||||
RunE: exportKsCmdRun,
|
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
|
||||||
|
list: kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportCmd.AddCommand(exportKsCmd)
|
exportCmd.AddCommand(exportKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportKsCmdRun(cmd *cobra.Command, args []string) error {
|
func exportKs(kustomization *kustomizev1.Kustomization) interface{} {
|
||||||
if !exportArgs.all && len(args) < 1 {
|
|
||||||
return fmt.Errorf("kustomization name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if exportArgs.all {
|
|
||||||
var list kustomizev1.KustomizationList
|
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no kustomizations found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, kustomization := range list.Items {
|
|
||||||
if err := exportKs(kustomization); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := args[0]
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return exportKs(kustomization)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportKs(kustomization kustomizev1.Kustomization) error {
|
|
||||||
gvk := kustomizev1.GroupVersion.WithKind("Kustomization")
|
gvk := kustomizev1.GroupVersion.WithKind("Kustomization")
|
||||||
export := kustomizev1.Kustomization{
|
export := kustomizev1.Kustomization{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -110,12 +59,13 @@ func exportKs(kustomization kustomizev1.Kustomization) error {
|
|||||||
Spec: kustomization.Spec,
|
Spec: kustomization.Spec,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := yaml.Marshal(export)
|
return export
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
func (ex kustomizationAdapter) export() interface{} {
|
||||||
|
return exportKs(ex.Kustomization)
|
||||||
fmt.Println("---")
|
}
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
return nil
|
func (ex kustomizationListAdapter) exportItem(i int) interface{} {
|
||||||
|
return exportKs(&ex.KustomizationList.Items[i])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,62 +31,18 @@ var exportReceiverCmd = &cobra.Command{
|
|||||||
flux export receiver --all > receivers.yaml
|
flux export receiver --all > receivers.yaml
|
||||||
|
|
||||||
# Export a Receiver
|
# Export a Receiver
|
||||||
flux export receiver main > main.yaml
|
flux export receiver main > main.yaml`,
|
||||||
`,
|
RunE: exportCommand{
|
||||||
RunE: exportReceiverCmdRun,
|
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
||||||
|
object: receiverAdapter{¬ificationv1.Receiver{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportCmd.AddCommand(exportReceiverCmd)
|
exportCmd.AddCommand(exportReceiverCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
func exportReceiver(receiver *notificationv1.Receiver) interface{} {
|
||||||
if !exportArgs.all && len(args) < 1 {
|
|
||||||
return fmt.Errorf("name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if exportArgs.all {
|
|
||||||
var list notificationv1.ReceiverList
|
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no receivers found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, receiver := range list.Items {
|
|
||||||
if err := exportReceiver(receiver); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := args[0]
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var receiver notificationv1.Receiver
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &receiver)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return exportReceiver(receiver)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportReceiver(receiver notificationv1.Receiver) error {
|
|
||||||
gvk := notificationv1.GroupVersion.WithKind("Receiver")
|
gvk := notificationv1.GroupVersion.WithKind("Receiver")
|
||||||
export := notificationv1.Receiver{
|
export := notificationv1.Receiver{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -109,12 +58,13 @@ func exportReceiver(receiver notificationv1.Receiver) error {
|
|||||||
Spec: receiver.Spec,
|
Spec: receiver.Spec,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := yaml.Marshal(export)
|
return export
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
func (ex receiverAdapter) export() interface{} {
|
||||||
|
return exportReceiver(ex.Receiver)
|
||||||
fmt.Println("---")
|
}
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
return nil
|
func (ex receiverListAdapter) exportItem(i int) interface{} {
|
||||||
|
return exportReceiver(&ex.ReceiverList.Items[i])
|
||||||
}
|
}
|
||||||
|
|||||||
134
cmd/flux/export_secret.go
Normal file
134
cmd/flux/export_secret.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// exportableWithSecret represents a type that you can fetch from the Kubernetes
|
||||||
|
// API, get a secretRef from the spec, then tidy up for serialising.
|
||||||
|
type exportableWithSecret interface {
|
||||||
|
adapter
|
||||||
|
exportable
|
||||||
|
secret() *types.NamespacedName
|
||||||
|
}
|
||||||
|
|
||||||
|
// exportableWithSecretList represents a type that has a list of values, each of
|
||||||
|
// which is exportableWithSecret.
|
||||||
|
type exportableWithSecretList interface {
|
||||||
|
listAdapter
|
||||||
|
exportableList
|
||||||
|
secretItem(i int) *types.NamespacedName
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportWithSecretCommand struct {
|
||||||
|
apiType
|
||||||
|
object exportableWithSecret
|
||||||
|
list exportableWithSecretList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (export exportWithSecretCommand) run(cmd *cobra.Command, args []string) error {
|
||||||
|
if !exportArgs.all && len(args) < 1 {
|
||||||
|
return fmt.Errorf("name is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exportArgs.all {
|
||||||
|
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(rootArgs.namespace))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if export.list.len() == 0 {
|
||||||
|
return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < export.list.len(); i++ {
|
||||||
|
if err = printExport(export.list.exportItem(i)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exportSourceWithCred {
|
||||||
|
if export.list.secretItem(i) != nil {
|
||||||
|
namespacedName := *export.list.secretItem(i)
|
||||||
|
return printSecretCredentials(ctx, kubeClient, namespacedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name := args[0]
|
||||||
|
namespacedName := types.NamespacedName{
|
||||||
|
Namespace: rootArgs.namespace,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := printExport(export.object.export()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exportSourceWithCred {
|
||||||
|
if export.object.secret() != nil {
|
||||||
|
namespacedName := *export.object.secret()
|
||||||
|
return printSecretCredentials(ctx, kubeClient, namespacedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printSecretCredentials(ctx context.Context, kubeClient client.Client, nsName types.NamespacedName) error {
|
||||||
|
var cred corev1.Secret
|
||||||
|
err := kubeClient.Get(ctx, nsName, &cred)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve secret %s, error: %w", nsName.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exported := corev1.Secret{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: nsName.Name,
|
||||||
|
Namespace: nsName.Namespace,
|
||||||
|
},
|
||||||
|
Data: cred.Data,
|
||||||
|
Type: cred.Type,
|
||||||
|
}
|
||||||
|
return printExport(exported)
|
||||||
|
}
|
||||||
@@ -17,17 +17,10 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,72 +32,18 @@ var exportSourceBucketCmd = &cobra.Command{
|
|||||||
flux export source bucket --all > sources.yaml
|
flux export source bucket --all > sources.yaml
|
||||||
|
|
||||||
# Export a Bucket source including the static credentials
|
# Export a Bucket source including the static credentials
|
||||||
flux export source bucket my-bucket --with-credentials > source.yaml
|
flux export source bucket my-bucket --with-credentials > source.yaml`,
|
||||||
`,
|
RunE: exportWithSecretCommand{
|
||||||
RunE: exportSourceBucketCmdRun,
|
list: bucketListAdapter{&sourcev1.BucketList{}},
|
||||||
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportSourceCmd.AddCommand(exportSourceBucketCmd)
|
exportSourceCmd.AddCommand(exportSourceBucketCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
func exportBucket(source *sourcev1.Bucket) interface{} {
|
||||||
if !exportArgs.all && len(args) < 1 {
|
|
||||||
return fmt.Errorf("name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if exportArgs.all {
|
|
||||||
var list sourcev1.BucketList
|
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, bucket := range list.Items {
|
|
||||||
if err := exportBucket(bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if exportSourceWithCred {
|
|
||||||
if err := exportBucketCredentials(ctx, kubeClient, bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := args[0]
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var bucket sourcev1.Bucket
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := exportBucket(bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if exportSourceWithCred {
|
|
||||||
return exportBucketCredentials(ctx, kubeClient, bucket)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportBucket(source sourcev1.Bucket) error {
|
|
||||||
gvk := sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)
|
gvk := sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)
|
||||||
export := sourcev1.Bucket{
|
export := sourcev1.Bucket{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -119,49 +58,34 @@ func exportBucket(source sourcev1.Bucket) error {
|
|||||||
},
|
},
|
||||||
Spec: source.Spec,
|
Spec: source.Spec,
|
||||||
}
|
}
|
||||||
|
return export
|
||||||
data, err := yaml.Marshal(export)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("---")
|
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportBucketCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.Bucket) error {
|
func getBucketSecret(source *sourcev1.Bucket) *types.NamespacedName {
|
||||||
if source.Spec.SecretRef != nil {
|
if source.Spec.SecretRef != nil {
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: source.Namespace,
|
Namespace: source.Namespace,
|
||||||
Name: source.Spec.SecretRef.Name,
|
Name: source.Spec.SecretRef.Name,
|
||||||
}
|
}
|
||||||
var cred corev1.Secret
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, &cred)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to retrieve secret %s, error: %w", namespacedName.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exported := corev1.Secret{
|
return &namespacedName
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
APIVersion: "v1",
|
|
||||||
Kind: "Secret",
|
|
||||||
},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: namespacedName.Name,
|
|
||||||
Namespace: namespacedName.Namespace,
|
|
||||||
},
|
|
||||||
Data: cred.Data,
|
|
||||||
Type: cred.Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := yaml.Marshal(exported)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("---")
|
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ex bucketAdapter) secret() *types.NamespacedName {
|
||||||
|
return getBucketSecret(ex.Bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex bucketListAdapter) secretItem(i int) *types.NamespacedName {
|
||||||
|
return getBucketSecret(&ex.BucketList.Items[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex bucketAdapter) export() interface{} {
|
||||||
|
return exportBucket(ex.Bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex bucketListAdapter) exportItem(i int) interface{} {
|
||||||
|
return exportBucket(&ex.BucketList.Items[i])
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,17 +17,10 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,72 +32,18 @@ var exportSourceGitCmd = &cobra.Command{
|
|||||||
flux export source git --all > sources.yaml
|
flux export source git --all > sources.yaml
|
||||||
|
|
||||||
# Export a GitRepository source including the SSH key pair or basic auth credentials
|
# Export a GitRepository source including the SSH key pair or basic auth credentials
|
||||||
flux export source git my-private-repo --with-credentials > source.yaml
|
flux export source git my-private-repo --with-credentials > source.yaml`,
|
||||||
`,
|
RunE: exportWithSecretCommand{
|
||||||
RunE: exportSourceGitCmdRun,
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
|
list: gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportSourceCmd.AddCommand(exportSourceGitCmd)
|
exportSourceCmd.AddCommand(exportSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
func exportGit(source *sourcev1.GitRepository) interface{} {
|
||||||
if !exportArgs.all && len(args) < 1 {
|
|
||||||
return fmt.Errorf("name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if exportArgs.all {
|
|
||||||
var list sourcev1.GitRepositoryList
|
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, repository := range list.Items {
|
|
||||||
if err := exportGit(repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if exportSourceWithCred {
|
|
||||||
if err := exportGitCredentials(ctx, kubeClient, repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := args[0]
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.GitRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := exportGit(repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if exportSourceWithCred {
|
|
||||||
return exportGitCredentials(ctx, kubeClient, repository)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportGit(source sourcev1.GitRepository) error {
|
|
||||||
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)
|
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)
|
||||||
export := sourcev1.GitRepository{
|
export := sourcev1.GitRepository{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -120,48 +59,33 @@ func exportGit(source sourcev1.GitRepository) error {
|
|||||||
Spec: source.Spec,
|
Spec: source.Spec,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := yaml.Marshal(export)
|
return export
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("---")
|
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportGitCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.GitRepository) error {
|
func getGitSecret(source *sourcev1.GitRepository) *types.NamespacedName {
|
||||||
if source.Spec.SecretRef != nil {
|
if source.Spec.SecretRef != nil {
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: source.Namespace,
|
Namespace: source.Namespace,
|
||||||
Name: source.Spec.SecretRef.Name,
|
Name: source.Spec.SecretRef.Name,
|
||||||
}
|
}
|
||||||
var cred corev1.Secret
|
return &namespacedName
|
||||||
err := kubeClient.Get(ctx, namespacedName, &cred)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to retrieve secret %s, error: %w", namespacedName.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exported := corev1.Secret{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
APIVersion: "v1",
|
|
||||||
Kind: "Secret",
|
|
||||||
},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: namespacedName.Name,
|
|
||||||
Namespace: namespacedName.Namespace,
|
|
||||||
},
|
|
||||||
Data: cred.Data,
|
|
||||||
Type: cred.Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := yaml.Marshal(exported)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("---")
|
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ex gitRepositoryAdapter) secret() *types.NamespacedName {
|
||||||
|
return getGitSecret(ex.GitRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex gitRepositoryListAdapter) secretItem(i int) *types.NamespacedName {
|
||||||
|
return getGitSecret(&ex.GitRepositoryList.Items[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex gitRepositoryAdapter) export() interface{} {
|
||||||
|
return exportGit(ex.GitRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex gitRepositoryListAdapter) exportItem(i int) interface{} {
|
||||||
|
return exportGit(&ex.GitRepositoryList.Items[i])
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,17 +17,10 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,72 +32,18 @@ var exportSourceHelmCmd = &cobra.Command{
|
|||||||
flux export source helm --all > sources.yaml
|
flux export source helm --all > sources.yaml
|
||||||
|
|
||||||
# Export a HelmRepository source including the basic auth credentials
|
# Export a HelmRepository source including the basic auth credentials
|
||||||
flux export source helm my-private-repo --with-credentials > source.yaml
|
flux export source helm my-private-repo --with-credentials > source.yaml`,
|
||||||
`,
|
RunE: exportWithSecretCommand{
|
||||||
RunE: exportSourceHelmCmdRun,
|
list: helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
||||||
|
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportSourceCmd.AddCommand(exportSourceHelmCmd)
|
exportSourceCmd.AddCommand(exportSourceHelmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
func exportHelmRepository(source *sourcev1.HelmRepository) interface{} {
|
||||||
if !exportArgs.all && len(args) < 1 {
|
|
||||||
return fmt.Errorf("name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if exportArgs.all {
|
|
||||||
var list sourcev1.HelmRepositoryList
|
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, repository := range list.Items {
|
|
||||||
if err := exportHelmRepository(repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if exportSourceWithCred {
|
|
||||||
if err := exportHelmCredentials(ctx, kubeClient, repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := args[0]
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.HelmRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := exportHelmRepository(repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if exportSourceWithCred {
|
|
||||||
return exportHelmCredentials(ctx, kubeClient, repository)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportHelmRepository(source sourcev1.HelmRepository) error {
|
|
||||||
gvk := sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)
|
gvk := sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)
|
||||||
export := sourcev1.HelmRepository{
|
export := sourcev1.HelmRepository{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -119,49 +58,32 @@ func exportHelmRepository(source sourcev1.HelmRepository) error {
|
|||||||
},
|
},
|
||||||
Spec: source.Spec,
|
Spec: source.Spec,
|
||||||
}
|
}
|
||||||
|
return export
|
||||||
data, err := yaml.Marshal(export)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("---")
|
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportHelmCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.HelmRepository) error {
|
func getHelmSecret(source *sourcev1.HelmRepository) *types.NamespacedName {
|
||||||
if source.Spec.SecretRef != nil {
|
if source.Spec.SecretRef != nil {
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: source.Namespace,
|
Namespace: source.Namespace,
|
||||||
Name: source.Spec.SecretRef.Name,
|
Name: source.Spec.SecretRef.Name,
|
||||||
}
|
}
|
||||||
var cred corev1.Secret
|
return &namespacedName
|
||||||
err := kubeClient.Get(ctx, namespacedName, &cred)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to retrieve secret %s, error: %w", namespacedName.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exported := corev1.Secret{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
APIVersion: "v1",
|
|
||||||
Kind: "Secret",
|
|
||||||
},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: namespacedName.Name,
|
|
||||||
Namespace: namespacedName.Namespace,
|
|
||||||
},
|
|
||||||
Data: cred.Data,
|
|
||||||
Type: cred.Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := yaml.Marshal(exported)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("---")
|
|
||||||
fmt.Println(resourceToString(data))
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ex helmRepositoryAdapter) secret() *types.NamespacedName {
|
||||||
|
return getHelmSecret(ex.HelmRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex helmRepositoryListAdapter) secretItem(i int) *types.NamespacedName {
|
||||||
|
return getHelmSecret(&ex.HelmRepositoryList.Items[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex helmRepositoryAdapter) export() interface{} {
|
||||||
|
return exportHelmRepository(ex.HelmRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex helmRepositoryListAdapter) exportItem(i int) interface{} {
|
||||||
|
return exportHelmRepository(&ex.HelmRepositoryList.Items[i])
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,19 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var getAlertCmd = &cobra.Command{
|
var getAlertCmd = &cobra.Command{
|
||||||
@@ -38,66 +31,27 @@ var getAlertCmd = &cobra.Command{
|
|||||||
Short: "Get Alert statuses",
|
Short: "Get Alert statuses",
|
||||||
Long: "The get alert command prints the statuses of the resources.",
|
Long: "The get alert command prints the statuses of the resources.",
|
||||||
Example: ` # List all Alerts and their status
|
Example: ` # List all Alerts and their status
|
||||||
flux get alerts
|
flux get alerts`,
|
||||||
`,
|
RunE: getCommand{
|
||||||
RunE: getAlertCmdRun,
|
apiType: alertType,
|
||||||
|
list: &alertListAdapter{¬ificationv1.AlertList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getCmd.AddCommand(getAlertCmd)
|
getCmd.AddCommand(getAlertCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAlertCmdRun(cmd *cobra.Command, args []string) error {
|
func (s alertListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
item := s.Items[i]
|
||||||
defer cancel()
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
|
return append(nameColumns(&item, includeNamespace, includeKind), status, msg, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
func (s alertListAdapter) headers(includeNamespace bool) []string {
|
||||||
}
|
headers := []string{"Name", "Ready", "Message", "Suspended"}
|
||||||
|
if includeNamespace {
|
||||||
var listOpts []client.ListOption
|
return append(namespaceHeader, headers...)
|
||||||
if !getArgs.allNamespaces {
|
}
|
||||||
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
return headers
|
||||||
}
|
|
||||||
var list notificationv1.AlertList
|
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no alerts found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Suspended"}
|
|
||||||
if getArgs.allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, alert := range list.Items {
|
|
||||||
row := []string{}
|
|
||||||
if c := apimeta.FindStatusCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
alert.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
alert.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if getArgs.allNamespaces {
|
|
||||||
row = append([]string{alert.Namespace}, row...)
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,17 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var getAlertProviderCmd = &cobra.Command{
|
var getAlertProviderCmd = &cobra.Command{
|
||||||
@@ -36,64 +28,27 @@ var getAlertProviderCmd = &cobra.Command{
|
|||||||
Short: "Get Provider statuses",
|
Short: "Get Provider statuses",
|
||||||
Long: "The get alert-provider command prints the statuses of the resources.",
|
Long: "The get alert-provider command prints the statuses of the resources.",
|
||||||
Example: ` # List all Providers and their status
|
Example: ` # List all Providers and their status
|
||||||
flux get alert-providers
|
flux get alert-providers`,
|
||||||
`,
|
RunE: getCommand{
|
||||||
RunE: getAlertProviderCmdRun,
|
apiType: alertProviderType,
|
||||||
|
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getCmd.AddCommand(getAlertProviderCmd)
|
getCmd.AddCommand(getAlertProviderCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
func (s alertProviderListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
item := s.Items[i]
|
||||||
defer cancel()
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
|
return append(nameColumns(&item, includeNamespace, includeKind), status, msg)
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
func (s alertProviderListAdapter) headers(includeNamespace bool) []string {
|
||||||
}
|
headers := []string{"Name", "Ready", "Message"}
|
||||||
|
if includeNamespace {
|
||||||
var listOpts []client.ListOption
|
return append(namespaceHeader, headers...)
|
||||||
if !getArgs.allNamespaces {
|
}
|
||||||
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
return headers
|
||||||
}
|
|
||||||
var list notificationv1.ProviderList
|
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no providers found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message"}
|
|
||||||
if getArgs.allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, provider := range list.Items {
|
|
||||||
row := []string{}
|
|
||||||
if c := apimeta.FindStatusCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
provider.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
provider.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if getArgs.allNamespaces {
|
|
||||||
row = append([]string{provider.Namespace}, row...)
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
91
cmd/flux/get_all.go
Normal file
91
cmd/flux/get_all.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var getAllCmd = &cobra.Command{
|
||||||
|
Use: "all",
|
||||||
|
Short: "Get all resources and statuses",
|
||||||
|
Long: "The get all command print the statuses of all resources.",
|
||||||
|
Example: ` # List all resources in a namespace
|
||||||
|
flux get all --namespace=flux-system
|
||||||
|
|
||||||
|
# List all resources in all namespaces
|
||||||
|
flux get all --all-namespaces`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := getSourceAllCmd.RunE(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
logError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// all get command
|
||||||
|
var allCmd = []getCommand{
|
||||||
|
{
|
||||||
|
apiType: helmReleaseType,
|
||||||
|
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiType: kustomizationType,
|
||||||
|
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiType: receiverType,
|
||||||
|
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiType: alertProviderType,
|
||||||
|
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiType: alertType,
|
||||||
|
list: &alertListAdapter{¬ificationv1.AlertList{}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = getImageAllCmd.RunE(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
logError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range allCmd {
|
||||||
|
if err := c.run(cmd, args); err != nil {
|
||||||
|
logError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func logError(err error) {
|
||||||
|
if !strings.Contains(err.Error(), "no matches for kind") {
|
||||||
|
logger.Failuref(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
getCmd.AddCommand(getAllCmd)
|
||||||
|
}
|
||||||
@@ -30,8 +30,7 @@ var getHelmReleaseCmd = &cobra.Command{
|
|||||||
Short: "Get HelmRelease statuses",
|
Short: "Get HelmRelease statuses",
|
||||||
Long: "The get helmreleases command prints the statuses of the resources.",
|
Long: "The get helmreleases command prints the statuses of the resources.",
|
||||||
Example: ` # List all Helm releases and their status
|
Example: ` # List all Helm releases and their status
|
||||||
flux get helmreleases
|
flux get helmreleases`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: helmReleaseType,
|
apiType: helmReleaseType,
|
||||||
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
||||||
|
|||||||
@@ -17,9 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
"strings"
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
||||||
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageAllCmd = &cobra.Command{
|
var getImageAllCmd = &cobra.Command{
|
||||||
@@ -30,31 +33,29 @@ var getImageAllCmd = &cobra.Command{
|
|||||||
flux get images all --namespace=flux-system
|
flux get images all --namespace=flux-system
|
||||||
|
|
||||||
# List all image objects in all namespaces
|
# List all image objects in all namespaces
|
||||||
flux get images all --all-namespaces
|
flux get images all --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
c := getCommand{
|
var allImageCmd = []getCommand{
|
||||||
apiType: imageRepositoryType,
|
{
|
||||||
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
apiType: imageRepositoryType,
|
||||||
}
|
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
||||||
if err := c.run(cmd, args); err != nil {
|
},
|
||||||
logger.Failuref(err.Error())
|
{
|
||||||
|
apiType: imagePolicyType,
|
||||||
|
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiType: imageUpdateAutomationType,
|
||||||
|
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c = getCommand{
|
for _, c := range allImageCmd {
|
||||||
apiType: imagePolicyType,
|
if err := c.run(cmd, args); err != nil {
|
||||||
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
if !strings.Contains(err.Error(), "no matches for kind") {
|
||||||
}
|
logger.Failuref(err.Error())
|
||||||
if err := c.run(cmd, args); err != nil {
|
}
|
||||||
logger.Failuref(err.Error())
|
}
|
||||||
}
|
|
||||||
|
|
||||||
c = getCommand{
|
|
||||||
apiType: imageUpdateAutomationType,
|
|
||||||
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
|
||||||
}
|
|
||||||
if err := c.run(cmd, args); err != nil {
|
|
||||||
logger.Failuref(err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImagePolicyCmd = &cobra.Command{
|
var getImagePolicyCmd = &cobra.Command{
|
||||||
@@ -30,8 +30,7 @@ var getImagePolicyCmd = &cobra.Command{
|
|||||||
flux get image policy
|
flux get image policy
|
||||||
|
|
||||||
# List image policies from all namespaces
|
# List image policies from all namespaces
|
||||||
flux get image policy --all-namespaces
|
flux get image policy --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: imagePolicyType,
|
apiType: imagePolicyType,
|
||||||
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageRepositoryCmd = &cobra.Command{
|
var getImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -34,8 +34,7 @@ var getImageRepositoryCmd = &cobra.Command{
|
|||||||
flux get image repository
|
flux get image repository
|
||||||
|
|
||||||
# List image repositories from all namespaces
|
# List image repositories from all namespaces
|
||||||
flux get image repository --all-namespaces
|
flux get image repository --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: imageRepositoryType,
|
apiType: imageRepositoryType,
|
||||||
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageUpdateCmd = &cobra.Command{
|
var getImageUpdateCmd = &cobra.Command{
|
||||||
@@ -34,8 +34,7 @@ var getImageUpdateCmd = &cobra.Command{
|
|||||||
flux get image update
|
flux get image update
|
||||||
|
|
||||||
# List image update automations from all namespaces
|
# List image update automations from all namespaces
|
||||||
flux get image update --all-namespaces
|
flux get image update --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: imageUpdateAutomationType,
|
apiType: imageUpdateAutomationType,
|
||||||
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getKsCmd = &cobra.Command{
|
var getKsCmd = &cobra.Command{
|
||||||
@@ -30,8 +31,7 @@ var getKsCmd = &cobra.Command{
|
|||||||
Short: "Get Kustomization statuses",
|
Short: "Get Kustomization statuses",
|
||||||
Long: "The get kustomizations command prints the statuses of the resources.",
|
Long: "The get kustomizations command prints the statuses of the resources.",
|
||||||
Example: ` # List all kustomizations and their status
|
Example: ` # List all kustomizations and their status
|
||||||
flux get kustomizations
|
flux get kustomizations`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: kustomizationType,
|
apiType: kustomizationType,
|
||||||
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
||||||
|
|||||||
@@ -17,19 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var getReceiverCmd = &cobra.Command{
|
var getReceiverCmd = &cobra.Command{
|
||||||
@@ -38,63 +31,27 @@ var getReceiverCmd = &cobra.Command{
|
|||||||
Short: "Get Receiver statuses",
|
Short: "Get Receiver statuses",
|
||||||
Long: "The get receiver command prints the statuses of the resources.",
|
Long: "The get receiver command prints the statuses of the resources.",
|
||||||
Example: ` # List all Receiver and their status
|
Example: ` # List all Receiver and their status
|
||||||
flux get receivers
|
flux get receivers`,
|
||||||
`,
|
RunE: getCommand{
|
||||||
RunE: getReceiverCmdRun,
|
apiType: receiverType,
|
||||||
|
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getCmd.AddCommand(getReceiverCmd)
|
getCmd.AddCommand(getReceiverCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
func (s receiverListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
item := s.Items[i]
|
||||||
defer cancel()
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
|
return append(nameColumns(&item, includeNamespace, includeKind), status, msg, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
func (s receiverListAdapter) headers(includeNamespace bool) []string {
|
||||||
}
|
headers := []string{"Name", "Ready", "Message", "Suspended"}
|
||||||
|
if includeNamespace {
|
||||||
var listOpts []client.ListOption
|
return append(namespaceHeader, headers...)
|
||||||
if !getArgs.allNamespaces {
|
}
|
||||||
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
return headers
|
||||||
}
|
|
||||||
var list notificationv1.ReceiverList
|
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no receivers found in %s namespace", rootArgs.namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Suspended"}
|
|
||||||
if getArgs.allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, receiver := range list.Items {
|
|
||||||
var row []string
|
|
||||||
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
receiver.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
receiver.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceAllCmd = &cobra.Command{
|
var getSourceAllCmd = &cobra.Command{
|
||||||
@@ -29,39 +32,33 @@ var getSourceAllCmd = &cobra.Command{
|
|||||||
flux get sources all --namespace=flux-system
|
flux get sources all --namespace=flux-system
|
||||||
|
|
||||||
# List all sources in all namespaces
|
# List all sources in all namespaces
|
||||||
flux get sources all --all-namespaces
|
flux get sources all --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
c := getCommand{
|
var allSourceCmd = []getCommand{
|
||||||
apiType: bucketType,
|
{
|
||||||
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
apiType: bucketType,
|
||||||
}
|
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
||||||
if err := c.run(cmd, args); err != nil {
|
},
|
||||||
logger.Failuref(err.Error())
|
{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiType: helmRepositoryType,
|
||||||
|
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiType: helmChartType,
|
||||||
|
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c = getCommand{
|
for _, c := range allSourceCmd {
|
||||||
apiType: gitRepositoryType,
|
if err := c.run(cmd, args); err != nil {
|
||||||
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
if !strings.Contains(err.Error(), "no matches for kind") {
|
||||||
}
|
logger.Failuref(err.Error())
|
||||||
if err := c.run(cmd, args); err != nil {
|
}
|
||||||
logger.Failuref(err.Error())
|
}
|
||||||
}
|
|
||||||
|
|
||||||
c = getCommand{
|
|
||||||
apiType: helmRepositoryType,
|
|
||||||
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
|
||||||
}
|
|
||||||
if err := c.run(cmd, args); err != nil {
|
|
||||||
logger.Failuref(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
c = getCommand{
|
|
||||||
apiType: helmChartType,
|
|
||||||
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
|
|
||||||
}
|
|
||||||
if err := c.run(cmd, args); err != nil {
|
|
||||||
logger.Failuref(err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceBucketCmd = &cobra.Command{
|
var getSourceBucketCmd = &cobra.Command{
|
||||||
@@ -32,8 +33,7 @@ var getSourceBucketCmd = &cobra.Command{
|
|||||||
flux get sources bucket
|
flux get sources bucket
|
||||||
|
|
||||||
# List buckets from all namespaces
|
# List buckets from all namespaces
|
||||||
flux get sources helm --all-namespaces
|
flux get sources helm --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: bucketType,
|
apiType: bucketType,
|
||||||
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceHelmChartCmd = &cobra.Command{
|
var getSourceHelmChartCmd = &cobra.Command{
|
||||||
@@ -32,8 +33,7 @@ var getSourceHelmChartCmd = &cobra.Command{
|
|||||||
flux get sources chart
|
flux get sources chart
|
||||||
|
|
||||||
# List Helm charts from all namespaces
|
# List Helm charts from all namespaces
|
||||||
flux get sources chart --all-namespaces
|
flux get sources chart --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: helmChartType,
|
apiType: helmChartType,
|
||||||
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
|
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceGitCmd = &cobra.Command{
|
var getSourceGitCmd = &cobra.Command{
|
||||||
@@ -32,8 +33,7 @@ var getSourceGitCmd = &cobra.Command{
|
|||||||
flux get sources git
|
flux get sources git
|
||||||
|
|
||||||
# List Git repositories from all namespaces
|
# List Git repositories from all namespaces
|
||||||
flux get sources git --all-namespaces
|
flux get sources git --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: gitRepositoryType,
|
apiType: gitRepositoryType,
|
||||||
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceHelmCmd = &cobra.Command{
|
var getSourceHelmCmd = &cobra.Command{
|
||||||
@@ -32,8 +33,7 @@ var getSourceHelmCmd = &cobra.Command{
|
|||||||
flux get sources helm
|
flux get sources helm
|
||||||
|
|
||||||
# List Helm repositories from all namespaces
|
# List Helm repositories from all namespaces
|
||||||
flux get sources helm --all-namespaces
|
flux get sources helm --all-namespaces`,
|
||||||
`,
|
|
||||||
RunE: getCommand{
|
RunE: getCommand{
|
||||||
apiType: helmRepositoryType,
|
apiType: helmRepositoryType,
|
||||||
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// helmv2.HelmRelease
|
// helmv2.HelmRelease
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are general-purpose adapters for attaching methods to, for
|
// These are general-purpose adapters for attaching methods to, for
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ If a previous version is installed, then an in-place upgrade will be performed.`
|
|||||||
flux install --dry-run --verbose
|
flux install --dry-run --verbose
|
||||||
|
|
||||||
# Write install manifests to file
|
# Write install manifests to file
|
||||||
flux install --export > flux-system.yaml
|
flux install --export > flux-system.yaml`,
|
||||||
`,
|
|
||||||
RunE: installCmdRun,
|
RunE: installCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,15 +176,15 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("install failed: %w", err)
|
return fmt.Errorf("install failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rootArgs.verbose {
|
if installArgs.export {
|
||||||
fmt.Print(manifest.Content)
|
|
||||||
} else if installArgs.export {
|
|
||||||
fmt.Println("---")
|
fmt.Println("---")
|
||||||
fmt.Println("# Flux version:", installArgs.version)
|
fmt.Println("# Flux version:", installArgs.version)
|
||||||
fmt.Println("# Components:", strings.Join(components, ","))
|
fmt.Println("# Components:", strings.Join(components, ","))
|
||||||
fmt.Print(manifest.Content)
|
fmt.Print(manifest.Content)
|
||||||
fmt.Println("---")
|
fmt.Println("---")
|
||||||
return nil
|
return nil
|
||||||
|
} else if rootArgs.verbose {
|
||||||
|
fmt.Print(manifest.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Successf("manifests build completed")
|
logger.Successf("manifests build completed")
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// kustomizev1.Kustomization
|
// kustomizev1.Kustomization
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ func (l stderrLogger) Successf(format string, a ...interface{}) {
|
|||||||
fmt.Fprintln(l.stderr, `✔`, fmt.Sprintf(format, a...))
|
fmt.Fprintln(l.stderr, `✔`, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l stderrLogger) Warningf(format string, a ...interface{}) {
|
||||||
|
fmt.Fprintln(l.stderr, `⚠️`, fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
func (l stderrLogger) Failuref(format string, a ...interface{}) {
|
func (l stderrLogger) Failuref(format string, a ...interface{}) {
|
||||||
fmt.Fprintln(l.stderr, `✗`, fmt.Sprintf(format, a...))
|
fmt.Fprintln(l.stderr, `✗`, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/cobra/doc"
|
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||||
@@ -37,14 +36,15 @@ var rootCmd = &cobra.Command{
|
|||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
SilenceErrors: true,
|
SilenceErrors: true,
|
||||||
Short: "Command line utility for assembling Kubernetes CD pipelines",
|
Short: "Command line utility for assembling Kubernetes CD pipelines",
|
||||||
Long: `Command line utility for assembling Kubernetes CD pipelines the GitOps way.`,
|
Long: `
|
||||||
|
Command line utility for assembling Kubernetes CD pipelines the GitOps way.`,
|
||||||
Example: ` # Check prerequisites
|
Example: ` # Check prerequisites
|
||||||
flux check --pre
|
flux check --pre
|
||||||
|
|
||||||
# Install the latest version of Flux
|
# Install the latest version of Flux
|
||||||
flux install --version=master
|
flux install --version=master
|
||||||
|
|
||||||
# Create a source from a public Git repository
|
# Create a source for a public Git repository
|
||||||
flux create source git webapp-latest \
|
flux create source git webapp-latest \
|
||||||
--url=https://github.com/stefanprodan/podinfo \
|
--url=https://github.com/stefanprodan/podinfo \
|
||||||
--branch=master \
|
--branch=master \
|
||||||
@@ -89,8 +89,7 @@ var rootCmd = &cobra.Command{
|
|||||||
flux delete source git webapp-latest
|
flux delete source git webapp-latest
|
||||||
|
|
||||||
# Uninstall Flux and delete CRDs
|
# Uninstall Flux and delete CRDs
|
||||||
flux uninstall
|
flux uninstall`,
|
||||||
`,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var logger = stderrLogger{stderr: os.Stderr}
|
var logger = stderrLogger{stderr: os.Stderr}
|
||||||
@@ -111,7 +110,11 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().StringVarP(&rootArgs.namespace, "namespace", "n", rootArgs.defaults.Namespace, "the namespace scope for this operation")
|
rootCmd.PersistentFlags().StringVarP(&rootArgs.namespace, "namespace", "n", rootArgs.defaults.Namespace, "the namespace scope for this operation")
|
||||||
rootCmd.PersistentFlags().DurationVar(&rootArgs.timeout, "timeout", 5*time.Minute, "timeout for this operation")
|
rootCmd.PersistentFlags().DurationVar(&rootArgs.timeout, "timeout", 5*time.Minute, "timeout for this operation")
|
||||||
rootCmd.PersistentFlags().BoolVar(&rootArgs.verbose, "verbose", false, "print generated objects")
|
rootCmd.PersistentFlags().BoolVar(&rootArgs.verbose, "verbose", false, "print generated objects")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "",
|
||||||
|
"absolute path to the kubeconfig file")
|
||||||
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubecontext, "context", "", "", "kubernetes context to use")
|
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubecontext, "context", "", "", "kubernetes context to use")
|
||||||
|
|
||||||
|
rootCmd.DisableAutoGenTag = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRootFlags() rootFlags {
|
func NewRootFlags() rootFlags {
|
||||||
@@ -125,39 +128,22 @@ func NewRootFlags() rootFlags {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
generateDocs()
|
configureKubeconfig()
|
||||||
kubeconfigFlag()
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
logger.Failuref("%v", err)
|
logger.Failuref("%v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func kubeconfigFlag() {
|
func configureKubeconfig() {
|
||||||
if home := homeDir(); home != "" {
|
switch {
|
||||||
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", filepath.Join(home, ".kube", "config"),
|
case len(rootArgs.kubeconfig) > 0:
|
||||||
"path to the kubeconfig file")
|
case len(os.Getenv("KUBECONFIG")) > 0:
|
||||||
} else {
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "",
|
|
||||||
"absolute path to the kubeconfig file")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(os.Getenv("KUBECONFIG")) > 0 {
|
|
||||||
rootArgs.kubeconfig = os.Getenv("KUBECONFIG")
|
rootArgs.kubeconfig = os.Getenv("KUBECONFIG")
|
||||||
}
|
default:
|
||||||
}
|
if home := homeDir(); len(home) > 0 {
|
||||||
|
rootArgs.kubeconfig = filepath.Join(home, ".kube", "config")
|
||||||
func generateDocs() {
|
|
||||||
args := os.Args[1:]
|
|
||||||
if len(args) > 0 && args[0] == "docgen" {
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "~/.kube/config",
|
|
||||||
"path to the kubeconfig file")
|
|
||||||
rootCmd.DisableAutoGenTag = true
|
|
||||||
err := doc.GenMarkdownTree(rootCmd, "./docs/cmd")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
52
cmd/flux/receiver.go
Normal file
52
cmd/flux/receiver.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// notificationv1.Receiver
|
||||||
|
|
||||||
|
var receiverType = apiType{
|
||||||
|
kind: notificationv1.ReceiverKind,
|
||||||
|
humanKind: "receiver",
|
||||||
|
}
|
||||||
|
|
||||||
|
type receiverAdapter struct {
|
||||||
|
*notificationv1.Receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a receiverAdapter) asClientObject() client.Object {
|
||||||
|
return a.Receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
// notificationv1.Receiver
|
||||||
|
|
||||||
|
type receiverListAdapter struct {
|
||||||
|
*notificationv1.ReceiverList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a receiverListAdapter) asClientList() client.ObjectList {
|
||||||
|
return a.ReceiverList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a receiverListAdapter) len() int {
|
||||||
|
return len(a.ReceiverList.Items)
|
||||||
|
}
|
||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -30,6 +29,9 @@ import (
|
|||||||
"k8s.io/client-go/util/retry"
|
"k8s.io/client-go/util/retry"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
"github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,12 +99,23 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
logger.Successf("%s annotated", reconcile.kind)
|
logger.Successf("%s annotated", reconcile.kind)
|
||||||
|
|
||||||
|
if reconcile.kind == v1beta1.AlertKind || reconcile.kind == v1beta1.ReceiverKind {
|
||||||
|
if err = wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
|
isReconcileReady(ctx, kubeClient, namespacedName, reconcile.object)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Successf(reconcile.object.successMessage())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
|
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
|
||||||
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
|
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
|
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Successf("%s reconciliation completed", reconcile.kind)
|
logger.Successf("%s reconciliation completed", reconcile.kind)
|
||||||
|
|
||||||
if apimeta.IsStatusConditionFalse(*reconcile.object.GetStatusConditions(), meta.ReadyCondition) {
|
if apimeta.IsStatusConditionFalse(*reconcile.object.GetStatusConditions(), meta.ReadyCondition) {
|
||||||
@@ -140,3 +153,23 @@ func requestReconciliation(ctx context.Context, kubeClient client.Client,
|
|||||||
return kubeClient.Update(ctx, obj.asClientObject())
|
return kubeClient.Update(ctx, obj.asClientObject())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isReconcileReady(ctx context.Context, kubeClient client.Client,
|
||||||
|
namespacedName types.NamespacedName, obj reconcilable) wait.ConditionFunc {
|
||||||
|
return func() (bool, error) {
|
||||||
|
err := kubeClient.Get(ctx, namespacedName, obj.asClientObject())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c := apimeta.FindStatusCondition(*obj.GetStatusConditions(), 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,16 +17,7 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -36,63 +27,17 @@ var reconcileAlertCmd = &cobra.Command{
|
|||||||
Short: "Reconcile an Alert",
|
Short: "Reconcile an Alert",
|
||||||
Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`,
|
Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger a reconciliation for an existing alert
|
Example: ` # Trigger a reconciliation for an existing alert
|
||||||
flux reconcile alert main
|
flux reconcile alert main`,
|
||||||
`,
|
RunE: reconcileCommand{
|
||||||
RunE: reconcileAlertCmdRun,
|
apiType: alertType,
|
||||||
|
object: alertAdapter{¬ificationv1.Alert{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
reconcileCmd.AddCommand(reconcileAlertCmd)
|
reconcileCmd.AddCommand(reconcileAlertCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj alertAdapter) lastHandledReconcileRequest() string {
|
||||||
if len(args) < 1 {
|
return ""
|
||||||
return fmt.Errorf("Alert 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 alert notificationv1.Alert
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &alert)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if alert.Spec.Suspend {
|
|
||||||
return fmt.Errorf("resource is suspended")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("annotating Alert %s in %s namespace", name, rootArgs.namespace)
|
|
||||||
if alert.Annotations == nil {
|
|
||||||
alert.Annotations = map[string]string{
|
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := kubeClient.Update(ctx, &alert); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Alert annotated")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for reconciliation")
|
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
|
||||||
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Alert reconciliation completed")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileAlertProviderCmd = &cobra.Command{
|
var reconcileAlertProviderCmd = &cobra.Command{
|
||||||
@@ -36,8 +36,7 @@ var reconcileAlertProviderCmd = &cobra.Command{
|
|||||||
Short: "Reconcile a Provider",
|
Short: "Reconcile a Provider",
|
||||||
Long: `The reconcile alert-provider command triggers a reconciliation of a Provider resource and waits for it to finish.`,
|
Long: `The reconcile alert-provider command triggers a reconciliation of a Provider resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger a reconciliation for an existing provider
|
Example: ` # Trigger a reconciliation for an existing provider
|
||||||
flux reconcile alert-provider slack
|
flux reconcile alert-provider slack`,
|
||||||
`,
|
|
||||||
RunE: reconcileAlertProviderCmdRun,
|
RunE: reconcileAlertProviderCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,20 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"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/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/client-go/util/retry"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
@@ -46,9 +34,11 @@ The reconcile kustomization command triggers a reconciliation of a HelmRelease r
|
|||||||
flux reconcile hr podinfo
|
flux reconcile hr podinfo
|
||||||
|
|
||||||
# Trigger a reconciliation of the HelmRelease's source and apply changes
|
# Trigger a reconciliation of the HelmRelease's source and apply changes
|
||||||
flux reconcile hr podinfo --with-source
|
flux reconcile hr podinfo --with-source`,
|
||||||
`,
|
RunE: reconcileWithSourceCommand{
|
||||||
RunE: reconcileHrCmdRun,
|
apiType: helmReleaseType,
|
||||||
|
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
type reconcileHelmReleaseFlags struct {
|
type reconcileHelmReleaseFlags struct {
|
||||||
@@ -63,117 +53,36 @@ func init() {
|
|||||||
reconcileCmd.AddCommand(reconcileHrCmd)
|
reconcileCmd.AddCommand(reconcileHrCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj helmReleaseAdapter) lastHandledReconcileRequest() string {
|
||||||
if len(args) < 1 {
|
return obj.Status.GetLastHandledReconcileRequest()
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if helmRelease.Spec.Suspend {
|
|
||||||
return fmt.Errorf("resource is suspended")
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = reconcileCommand{
|
|
||||||
apiType: helmRepositoryType,
|
|
||||||
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
|
||||||
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
|
||||||
case sourcev1.GitRepositoryKind:
|
|
||||||
err = reconcileCommand{
|
|
||||||
apiType: gitRepositoryType,
|
|
||||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
|
||||||
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
|
||||||
case sourcev1.BucketKind:
|
|
||||||
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
|
|
||||||
logger.Actionf("annotating HelmRelease %s in %s namespace", name, rootArgs.namespace)
|
|
||||||
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmRelease annotated")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmRelease reconciliation")
|
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
|
||||||
helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmRelease reconciliation completed")
|
|
||||||
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c := apimeta.FindStatusCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return fmt.Errorf("HelmRelease reconciliation failed: %s", c.Message)
|
|
||||||
default:
|
|
||||||
logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func helmReleaseReconciliationHandled(ctx context.Context, kubeClient client.Client,
|
func (obj helmReleaseAdapter) reconcileSource() bool {
|
||||||
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease, lastHandledReconcileAt string) wait.ConditionFunc {
|
return rhrArgs.syncHrWithSource
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, helmRelease)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return helmRelease.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestHelmReleaseReconciliation(ctx context.Context, kubeClient client.Client,
|
func (obj helmReleaseAdapter) getSource() (reconcileCommand, types.NamespacedName) {
|
||||||
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) error {
|
var cmd reconcileCommand
|
||||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
switch obj.Spec.Chart.Spec.SourceRef.Kind {
|
||||||
if err := kubeClient.Get(ctx, namespacedName, helmRelease); err != nil {
|
case sourcev1.HelmRepositoryKind:
|
||||||
return err
|
cmd = reconcileCommand{
|
||||||
|
apiType: helmRepositoryType,
|
||||||
|
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||||
}
|
}
|
||||||
if helmRelease.Annotations == nil {
|
case sourcev1.GitRepositoryKind:
|
||||||
helmRelease.Annotations = map[string]string{
|
cmd = reconcileCommand{
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
apiType: gitRepositoryType,
|
||||||
}
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
} else {
|
|
||||||
helmRelease.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
|
||||||
}
|
}
|
||||||
return kubeClient.Update(ctx, helmRelease)
|
case sourcev1.BucketKind:
|
||||||
})
|
cmd = reconcileCommand{
|
||||||
|
apiType: bucketType,
|
||||||
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd, types.NamespacedName{
|
||||||
|
Name: obj.Spec.Chart.Spec.SourceRef.Name,
|
||||||
|
Namespace: obj.Spec.Chart.Spec.SourceRef.Namespace,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileImageRepositoryCmd = &cobra.Command{
|
var reconcileImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -29,8 +29,7 @@ var reconcileImageRepositoryCmd = &cobra.Command{
|
|||||||
Short: "Reconcile an ImageRepository",
|
Short: "Reconcile an ImageRepository",
|
||||||
Long: `The reconcile image repository command triggers a reconciliation of an ImageRepository resource and waits for it to finish.`,
|
Long: `The reconcile image repository command triggers a reconciliation of an ImageRepository resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger an scan for an existing image repository
|
Example: ` # Trigger an scan for an existing image repository
|
||||||
flux reconcile image repository alpine
|
flux reconcile image repository alpine`,
|
||||||
`,
|
|
||||||
RunE: reconcileCommand{
|
RunE: reconcileCommand{
|
||||||
apiType: imageRepositoryType,
|
apiType: imageRepositoryType,
|
||||||
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
||||||
meta "github.com/fluxcd/pkg/apis/meta"
|
meta "github.com/fluxcd/pkg/apis/meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,8 +31,7 @@ var reconcileImageUpdateCmd = &cobra.Command{
|
|||||||
Short: "Reconcile an ImageUpdateAutomation",
|
Short: "Reconcile an ImageUpdateAutomation",
|
||||||
Long: `The reconcile image update command triggers a reconciliation of an ImageUpdateAutomation resource and waits for it to finish.`,
|
Long: `The reconcile image update command triggers a reconciliation of an ImageUpdateAutomation resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger an automation run for an existing image update automation
|
Example: ` # Trigger an automation run for an existing image update automation
|
||||||
flux reconcile image update latest-images
|
flux reconcile image update latest-images`,
|
||||||
`,
|
|
||||||
RunE: reconcileCommand{
|
RunE: reconcileCommand{
|
||||||
apiType: imageUpdateAutomationType,
|
apiType: imageUpdateAutomationType,
|
||||||
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
||||||
|
|||||||
@@ -17,19 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
"k8s.io/client-go/util/retry"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
@@ -45,9 +34,11 @@ The reconcile kustomization command triggers a reconciliation of a Kustomization
|
|||||||
flux reconcile kustomization podinfo
|
flux reconcile kustomization podinfo
|
||||||
|
|
||||||
# Trigger a sync of the Kustomization's source and apply changes
|
# Trigger a sync of the Kustomization's source and apply changes
|
||||||
flux reconcile kustomization podinfo --with-source
|
flux reconcile kustomization podinfo --with-source`,
|
||||||
`,
|
RunE: reconcileWithSourceCommand{
|
||||||
RunE: reconcileKsCmdRun,
|
apiType: kustomizationType,
|
||||||
|
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
type reconcileKsFlags struct {
|
type reconcileKsFlags struct {
|
||||||
@@ -62,104 +53,31 @@ func init() {
|
|||||||
reconcileCmd.AddCommand(reconcileKsCmd)
|
reconcileCmd.AddCommand(reconcileKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj kustomizationAdapter) lastHandledReconcileRequest() string {
|
||||||
if len(args) < 1 {
|
return obj.Status.GetLastHandledReconcileRequest()
|
||||||
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 kustomization.Spec.Suspend {
|
|
||||||
return fmt.Errorf("resource is suspended")
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = reconcileCommand{
|
|
||||||
apiType: gitRepositoryType,
|
|
||||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
|
||||||
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
|
|
||||||
case sourcev1.BucketKind:
|
|
||||||
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
|
|
||||||
logger.Actionf("annotating Kustomization %s in %s namespace", name, rootArgs.namespace)
|
|
||||||
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Kustomization annotated")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for Kustomization reconciliation")
|
|
||||||
if err := wait.PollImmediate(
|
|
||||||
rootArgs.pollInterval, rootArgs.timeout,
|
|
||||||
kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Kustomization reconciliation completed")
|
|
||||||
|
|
||||||
if apimeta.IsStatusConditionFalse(kustomization.Status.Conditions, meta.ReadyCondition) {
|
|
||||||
return fmt.Errorf("Kustomization reconciliation failed")
|
|
||||||
}
|
|
||||||
logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func kustomizeReconciliationHandled(ctx context.Context, kubeClient client.Client,
|
func (obj kustomizationAdapter) reconcileSource() bool {
|
||||||
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization, lastHandledReconcileAt string) wait.ConditionFunc {
|
return rksArgs.syncKsWithSource
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, kustomization)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return kustomization.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Client,
|
func (obj kustomizationAdapter) getSource() (reconcileCommand, types.NamespacedName) {
|
||||||
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) error {
|
var cmd reconcileCommand
|
||||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
switch obj.Spec.SourceRef.Kind {
|
||||||
if err := kubeClient.Get(ctx, namespacedName, kustomization); err != nil {
|
case sourcev1.GitRepositoryKind:
|
||||||
return err
|
cmd = reconcileCommand{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
}
|
}
|
||||||
if kustomization.Annotations == nil {
|
case sourcev1.BucketKind:
|
||||||
kustomization.Annotations = map[string]string{
|
cmd = reconcileCommand{
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
apiType: bucketType,
|
||||||
}
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
} else {
|
|
||||||
kustomization.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
|
||||||
}
|
}
|
||||||
return kubeClient.Update(ctx, kustomization)
|
}
|
||||||
})
|
|
||||||
|
return cmd, types.NamespacedName{
|
||||||
|
Name: obj.Spec.SourceRef.Name,
|
||||||
|
Namespace: obj.Spec.SourceRef.Namespace,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileReceiverCmd = &cobra.Command{
|
var reconcileReceiverCmd = &cobra.Command{
|
||||||
@@ -36,8 +36,7 @@ var reconcileReceiverCmd = &cobra.Command{
|
|||||||
Short: "Reconcile a Receiver",
|
Short: "Reconcile a Receiver",
|
||||||
Long: `The reconcile receiver command triggers a reconciliation of a Receiver resource and waits for it to finish.`,
|
Long: `The reconcile receiver command triggers a reconciliation of a Receiver resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger a reconciliation for an existing receiver
|
Example: ` # Trigger a reconciliation for an existing receiver
|
||||||
flux reconcile receiver main
|
flux reconcile receiver main`,
|
||||||
`,
|
|
||||||
RunE: reconcileReceiverCmdRun,
|
RunE: reconcileReceiverCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,8 +36,7 @@ var reconcileSourceBucketCmd = &cobra.Command{
|
|||||||
Short: "Reconcile a Bucket source",
|
Short: "Reconcile a Bucket source",
|
||||||
Long: `The reconcile source command triggers a reconciliation of a Bucket resource and waits for it to finish.`,
|
Long: `The reconcile source command triggers a reconciliation of a Bucket resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger a reconciliation for an existing source
|
Example: ` # Trigger a reconciliation for an existing source
|
||||||
flux reconcile source bucket podinfo
|
flux reconcile source bucket podinfo`,
|
||||||
`,
|
|
||||||
RunE: reconcileCommand{
|
RunE: reconcileCommand{
|
||||||
apiType: bucketType,
|
apiType: bucketType,
|
||||||
object: bucketAdapter{&sourcev1.Bucket{}},
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileSourceGitCmd = &cobra.Command{
|
var reconcileSourceGitCmd = &cobra.Command{
|
||||||
@@ -27,8 +29,7 @@ var reconcileSourceGitCmd = &cobra.Command{
|
|||||||
Short: "Reconcile a GitRepository source",
|
Short: "Reconcile a GitRepository source",
|
||||||
Long: `The reconcile source command triggers a reconciliation of a GitRepository resource and waits for it to finish.`,
|
Long: `The reconcile source command triggers a reconciliation of a GitRepository resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger a git pull for an existing source
|
Example: ` # Trigger a git pull for an existing source
|
||||||
flux reconcile source git podinfo
|
flux reconcile source git podinfo`,
|
||||||
`,
|
|
||||||
RunE: reconcileCommand{
|
RunE: reconcileCommand{
|
||||||
apiType: gitRepositoryType,
|
apiType: gitRepositoryType,
|
||||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileSourceHelmCmd = &cobra.Command{
|
var reconcileSourceHelmCmd = &cobra.Command{
|
||||||
@@ -27,8 +29,7 @@ var reconcileSourceHelmCmd = &cobra.Command{
|
|||||||
Short: "Reconcile a HelmRepository source",
|
Short: "Reconcile a HelmRepository source",
|
||||||
Long: `The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish.`,
|
Long: `The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish.`,
|
||||||
Example: ` # Trigger a reconciliation for an existing source
|
Example: ` # Trigger a reconciliation for an existing source
|
||||||
flux reconcile source helm podinfo
|
flux reconcile source helm podinfo`,
|
||||||
`,
|
|
||||||
RunE: reconcileCommand{
|
RunE: reconcileCommand{
|
||||||
apiType: helmRepositoryType,
|
apiType: helmRepositoryType,
|
||||||
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
|||||||
90
cmd/flux/reconcile_with_source.go
Normal file
90
cmd/flux/reconcile_with_source.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reconcileWithSource interface {
|
||||||
|
adapter
|
||||||
|
reconcilable
|
||||||
|
reconcileSource() bool
|
||||||
|
getSource() (reconcileCommand, types.NamespacedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
type reconcileWithSourceCommand struct {
|
||||||
|
apiType
|
||||||
|
object reconcileWithSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (reconcile reconcileWithSourceCommand) run(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("%s name is required", reconcile.kind)
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kubeClient.Get(ctx, namespacedName, reconcile.object.asClientObject())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reconcile.object.isSuspended() {
|
||||||
|
return fmt.Errorf("resource is suspended")
|
||||||
|
}
|
||||||
|
|
||||||
|
if reconcile.object.reconcileSource() {
|
||||||
|
reconcileCmd, nsName := reconcile.object.getSource()
|
||||||
|
nsCopy := rootArgs.namespace
|
||||||
|
if nsName.Namespace != "" {
|
||||||
|
rootArgs.namespace = nsName.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
err := reconcileCmd.run(nil, []string{nsName.Name})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rootArgs.namespace = nsCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, rootArgs.namespace)
|
||||||
|
if err := requestReconciliation(ctx, kubeClient, namespacedName, reconcile.object); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Successf("%s annotated", reconcile.kind)
|
||||||
|
|
||||||
|
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
|
||||||
|
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
|
||||||
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
|
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Successf("%s reconciliation completed", reconcile.kind)
|
||||||
|
|
||||||
|
if apimeta.IsStatusConditionFalse(*reconcile.object.GetStatusConditions(), meta.ReadyCondition) {
|
||||||
|
return fmt.Errorf("%s reconciliation failed", reconcile.kind)
|
||||||
|
}
|
||||||
|
logger.Successf(reconcile.object.successMessage())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
@@ -33,7 +34,15 @@ var resumeCmd = &cobra.Command{
|
|||||||
Long: "The resume sub-commands resume a suspended resource.",
|
Long: "The resume sub-commands resume a suspended resource.",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResumeFlags struct {
|
||||||
|
all bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var resumeArgs ResumeFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
resumeCmd.PersistentFlags().BoolVarP(&resumeArgs.all, "all", "", false,
|
||||||
|
"suspend all resources in that namespace")
|
||||||
rootCmd.AddCommand(resumeCmd)
|
rootCmd.AddCommand(resumeCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,13 +56,18 @@ type resumable interface {
|
|||||||
type resumeCommand struct {
|
type resumeCommand struct {
|
||||||
apiType
|
apiType
|
||||||
object resumable
|
object resumable
|
||||||
|
list listResumable
|
||||||
|
}
|
||||||
|
|
||||||
|
type listResumable interface {
|
||||||
|
listAdapter
|
||||||
|
resumeItem(i int) resumable
|
||||||
}
|
}
|
||||||
|
|
||||||
func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
|
func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 && !resumeArgs.all {
|
||||||
return fmt.Errorf("%s name is required", resume.humanKind)
|
return fmt.Errorf("%s name is required", resume.humanKind)
|
||||||
}
|
}
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -63,29 +77,46 @@ func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
var listOpts []client.ListOption
|
||||||
Namespace: rootArgs.namespace,
|
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
||||||
Name: name,
|
if len(args) > 0 {
|
||||||
|
listOpts = append(listOpts, client.MatchingFields{
|
||||||
|
"metadata.name": args[0],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, resume.object.asClientObject())
|
err = kubeClient.List(ctx, resume.list.asClientList(), listOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, name, rootArgs.namespace)
|
if resume.list.len() == 0 {
|
||||||
resume.object.setUnsuspended()
|
logger.Failuref("no %s objects found in %s namespace", resume.kind, rootArgs.namespace)
|
||||||
if err := kubeClient.Update(ctx, resume.object.asClientObject()); err != nil {
|
return nil
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
logger.Successf("%s resumed", resume.humanKind)
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for %s reconciliation", resume.kind)
|
for i := 0; i < resume.list.len(); i++ {
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, resume.list.resumeItem(i).asClientObject().GetName(), rootArgs.namespace)
|
||||||
isReady(ctx, kubeClient, namespacedName, resume.object)); err != nil {
|
resume.list.resumeItem(i).setUnsuspended()
|
||||||
return err
|
if err := kubeClient.Update(ctx, resume.list.resumeItem(i).asClientObject()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Successf("%s resumed", resume.humanKind)
|
||||||
|
|
||||||
|
namespacedName := types.NamespacedName{
|
||||||
|
Name: resume.list.resumeItem(i).asClientObject().GetName(),
|
||||||
|
Namespace: rootArgs.namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Waitingf("waiting for %s reconciliation", resume.kind)
|
||||||
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
|
isReady(ctx, kubeClient, namespacedName, resume.list.resumeItem(i))); err != nil {
|
||||||
|
logger.Failuref(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Successf("%s reconciliation completed", resume.kind)
|
||||||
|
logger.Successf(resume.list.resumeItem(i).successMessage())
|
||||||
}
|
}
|
||||||
logger.Successf("%s reconciliation completed", resume.kind)
|
|
||||||
logger.Successf(resume.object.successMessage())
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,18 +17,7 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"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"
|
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -39,74 +28,30 @@ var resumeAlertCmd = &cobra.Command{
|
|||||||
Long: `The resume command marks a previously suspended Alert resource for reconciliation and waits for it to
|
Long: `The resume command marks a previously suspended Alert resource for reconciliation and waits for it to
|
||||||
finish the apply.`,
|
finish the apply.`,
|
||||||
Example: ` # Resume reconciliation for an existing Alert
|
Example: ` # Resume reconciliation for an existing Alert
|
||||||
flux resume alert main
|
flux resume alert main`,
|
||||||
`,
|
RunE: resumeCommand{
|
||||||
RunE: resumeAlertCmdRun,
|
apiType: alertType,
|
||||||
|
object: alertAdapter{¬ificationv1.Alert{}},
|
||||||
|
list: &alertListAdapter{¬ificationv1.AlertList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resumeCmd.AddCommand(resumeAlertCmd)
|
resumeCmd.AddCommand(resumeAlertCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj alertAdapter) getObservedGeneration() int64 {
|
||||||
if len(args) < 1 {
|
return obj.Alert.Status.ObservedGeneration
|
||||||
return fmt.Errorf("Alert 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 alert notificationv1.Alert
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &alert)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("resuming Alert %s in %s namespace", name, rootArgs.namespace)
|
|
||||||
alert.Spec.Suspend = false
|
|
||||||
if err := kubeClient.Update(ctx, &alert); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Alert resumed")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for Alert reconciliation")
|
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
|
||||||
isAlertResumed(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Alert reconciliation completed")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAlertResumed(ctx context.Context, kubeClient client.Client,
|
func (obj alertAdapter) setUnsuspended() {
|
||||||
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
|
obj.Alert.Spec.Suspend = false
|
||||||
return func() (bool, error) {
|
}
|
||||||
err := kubeClient.Get(ctx, namespacedName, alert)
|
|
||||||
if err != nil {
|
func (obj alertAdapter) successMessage() string {
|
||||||
return false, err
|
return "Alert reconciliation completed"
|
||||||
}
|
}
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
|
func (a alertListAdapter) resumeItem(i int) resumable {
|
||||||
switch c.Status {
|
return &alertAdapter{&a.AlertList.Items[i]}
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
if c.Reason == meta.SuspendedReason {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeHrCmd = &cobra.Command{
|
var resumeHrCmd = &cobra.Command{
|
||||||
@@ -29,11 +31,11 @@ var resumeHrCmd = &cobra.Command{
|
|||||||
Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to
|
Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to
|
||||||
finish the apply.`,
|
finish the apply.`,
|
||||||
Example: ` # Resume reconciliation for an existing Helm release
|
Example: ` # Resume reconciliation for an existing Helm release
|
||||||
flux resume hr podinfo
|
flux resume hr podinfo`,
|
||||||
`,
|
|
||||||
RunE: resumeCommand{
|
RunE: resumeCommand{
|
||||||
apiType: helmReleaseType,
|
apiType: helmReleaseType,
|
||||||
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
|
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
|
||||||
|
list: helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
||||||
}.run,
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,3 +54,7 @@ func (obj helmReleaseAdapter) setUnsuspended() {
|
|||||||
func (obj helmReleaseAdapter) successMessage() string {
|
func (obj helmReleaseAdapter) successMessage() string {
|
||||||
return fmt.Sprintf("applied revision %s", obj.Status.LastAppliedRevision)
|
return fmt.Sprintf("applied revision %s", obj.Status.LastAppliedRevision)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a helmReleaseListAdapter) resumeItem(i int) resumable {
|
||||||
|
return &helmReleaseAdapter{&a.HelmReleaseList.Items[i]}
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user