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

Compare commits

..

317 Commits

Author SHA1 Message Date
Stefan Prodan
cb96bca6aa Merge pull request #1672 from souleb/main
Adds a watch flag to the get command
2021-08-06 16:16:34 +03:00
Soule BA
c18d0b9217 Adds a watch flag to the get command
The new flag fetch and display the request ressource and then continue
watching the ressource until timeout or cancellation.

A single ressource/ressource type is supported.

Signed-off-by: Soule BA <soule@weave.works>
2021-08-06 13:44:06 +02:00
Hidde Beydals
d1970185b9 Merge pull request #1690 from fluxcd/update-components
Update toolkit components
2021-08-05 19:03:02 +02:00
fluxcdbot
794d3ee2f5 Update toolkit components
- helm-controller to v0.11.2
  https://github.com/fluxcd/helm-controller/blob/v0.11.2/CHANGELOG.md
- kustomize-controller to v0.13.3
  https://github.com/fluxcd/kustomize-controller/blob/v0.13.3/CHANGELOG.md
- source-controller to v0.15.4
  https://github.com/fluxcd/source-controller/blob/v0.15.4/CHANGELOG.md
- notification-controller to v0.15.1
  https://github.com/fluxcd/notification-controller/blob/v0.15.1/CHANGELOG.md
- image-reflector-controller to v0.11.1
  https://github.com/fluxcd/image-reflector-controller/blob/v0.11.1/CHANGELOG.md
- image-automation-controller to v0.14.1
  https://github.com/fluxcd/image-automation-controller/blob/v0.14.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-08-05 16:48:34 +00:00
Hidde Beydals
daeef98dfb Merge pull request #1688 from allenporter/flux-test-main
Replace init() with TestMain()
2021-08-05 18:46:56 +02:00
Allen Porter
4146df1f02 Replace init() with TestMain()
Signed-off-by: Allen Porter <allen@thebends.org>
2021-08-04 08:14:22 -07:00
Hidde Beydals
78f4dfa48d Merge pull request #1687 from allenporter/flux-cmd-ioutil
Remove deprecated io/ioutil usage
2021-08-04 16:58:51 +02:00
Allen Porter
8b68d7d7e2 Remove deprecated io/ioutil usage
Issue #1658

Signed-off-by: Allen Porter <allen@thebends.org>
2021-08-04 07:46:25 -07:00
Stefan Prodan
ba1bba17ad Merge pull request #1683 from charles-woshicai/print-success-message
feat: display success message while create secrets via `flux` cli.
2021-08-04 17:40:17 +03:00
Charles Cai
70f2b5028f feat: display success message while create secrets via flux cli.
Signed-off-by: Charles Cai <charles.cai@sap.com>
2021-08-04 21:31:57 +08:00
Hidde Beydals
4f4f0d70a4 Merge pull request #1609 from fluxcd/patch-reconcile-req
Request reconcile using patch instead of update
2021-08-04 13:29:01 +02:00
Hidde Beydals
9607b07e65 Request reconcile using patch instead of update
This should prevent the generation of the object getting bumped, as
observed on a GKE K8s 1.18 cluster using the  logic before this commit.

We only want to generation to increase when there are actual changes to
the `spec` of a resource, as some controllers use the `generation`
value to make assumptions about what they should do during a
reconciliation.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-08-04 13:08:36 +02:00
Stefan Prodan
3570fab0f9 Merge pull request #1682 from fluxcd/refactor-tests
Refactor test helpers
2021-08-04 11:15:33 +03:00
Stefan Prodan
dd0f17d7a5 Refactor test helpers
- move test helpers to main
- add support for inline golden values
- add test for `flux --version`

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-08-04 10:47:33 +03:00
Stefan Prodan
2290880389 Merge pull request #1671 from allenporter/flux-trace
Add tests for flux trace command
2021-08-04 10:18:54 +03:00
Allen Porter
351d287d88 Add tests for flux trace command
Add tests for flux trace command that fake out the kubernetes client,
load objects from a yaml file and create them in the client, and
assert on the output of the trace command to an expected golden file.

This is a follow up from the suggestions in PR https://github.com/fluxcd/flux2/pull/1626 which suggested that additional
testing would be helpful. This test approach is modeled after the helm command tests.

This required some changes to the kubernetes client setup to make it
possible to use a fake. If we agree this pattern makes sense, it can be
applied to other commands.

Signed-off-by: Allen Porter <allen@thebends.org>
2021-08-03 06:35:13 -07:00
Hidde Beydals
eba6706f15 Merge pull request #1668 from dmitrika/chore/remove-deprecated-ioutil
chore: remove deprecated io/ioutil
2021-07-30 21:11:56 +02:00
Dmitry Rybin
7f425efa6b chore: remove deprecated io/ioutil
Signed-off-by: Dmitry Rybin <ayrowa@yandex.ru>
2021-07-30 20:51:46 +02:00
Stefan Prodan
d40685ab62 Merge pull request #1653 from fluxcd/gh-bug-template
Provide suggestion for some fields in bug report
2021-07-28 18:21:45 +03:00
Hidde Beydals
f795e3eeb8 Provide suggestion for some fields in bug report
This commit adds suggestion for some of the required fields because
there can be a few exceptions in which these values can not be
provided. For example when the `flux` binary can not be installed, or
the set of controllers has been installed with just an `install.yaml`
file.

We use this approach instead of making the fields optional because it
guides people to provide the information whenever they can, which
should result in higher quality bug reports.

As a tiny addition, the reference to "Flux v2" has been renamed to just
"Flux" as we are slowly transitioning "Flux v1" into "Flux Legacy".

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-07-27 13:25:17 +02:00
Hidde Beydals
d3944c0204 Merge pull request #1651 from fluxcd/gh-bug-template
Transform GitHub issue template to new format
2021-07-26 16:04:40 +02:00
Hidde Beydals
4086ab15fa Transform GitHub issue template to new format
This will help users to provide bug reports of high quality, as the
requested information is now rendered into dedicated fields, while
allowing us to mark fields as "required".

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-07-26 15:38:38 +02:00
Hidde Beydals
446a367094 Merge pull request #1626 from allenporter/flux-trace 2021-07-23 16:03:44 +02:00
Allen Porter
bd250c9871 Fix template optional field check in trace
Signed-off-by: Allen Porter <allen@thebends.org>
2021-07-22 20:20:21 -07:00
Allen Porter
91b3788362 Fix trace for optional GitRepository.Spec.Reference
Check for existence of GitRepository.Spec.Reference when displaying a trace to
avoid error:

✗ template: tmpl:28:21: executing "tmpl" at <.GitRepository.Spec.Reference.Tag>: nil pointer evaluating *v1beta1.GitRepositoryRef.Tag

Fixes issue #1621
Manually tested using the use cases highlighted in the issue.

Signed-off-by: Allen Porter <allen@thebends.org>
2021-07-22 20:20:21 -07:00
Hidde Beydals
658dbb9ea8 Merge pull request #1628 from darkowlzz/utils-tests
internal/utils: Add unit tests
2021-07-22 12:15:55 +02:00
Sunny
1257b9cbc8 internal/utils: Add unit tests
Add unit tests for various utils functions.

Signed-off-by: Sunny <darkowlzz@protonmail.com>
2021-07-19 02:05:49 +05:30
Stefan Prodan
0ae39d5a0a Merge pull request #1594 from chanwit/status-selector
Add status-selector flag for get commands to filter results based on status conditions
2021-07-08 10:49:58 +03:00
Chanwit Kaewkasi
d8911e0c77 add an example to the status-selector flag's description
Co-authored-by: Stefan Prodan <stefan.prodan@gmail.com>
Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
2021-07-07 14:21:26 +07:00
Chanwit Kaewkasi
1be006a45f implement status selector
Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
2021-07-06 22:59:03 +07:00
Stefan Prodan
b95e75ddb4 Merge pull request #1591 from fluxcd/update-components
Update kustomize-controller to v0.13.2
2021-07-06 11:15:14 +03:00
fluxcdbot
15a5f75fe7 Update toolkit components
- kustomize-controller to v0.13.2
  https://github.com/fluxcd/kustomize-controller/blob/v0.13.2/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-07-06 07:53:43 +00:00
Stefan Prodan
b01e27f50f Merge pull request #1590 from alex-petrov-vt/iss1585
Add no-header flag for get commands to omit printing the header
2021-07-06 10:53:16 +03:00
Alex Petrov
b1a9583262 Add no-header flag for get commands to omit printing the header
Signed-off-by: Alex Petrov <alex.petrov.vt@gmail.com>
2021-07-05 20:04:37 -04:00
Stefan Prodan
dd5e6377f8 Merge pull request #1579 from paulfantom/patch-1
Update podmonitor example
2021-07-02 14:36:12 +03:00
Paweł Krupa
fcb73554c9 Update podmonitor.yaml
`targetPort` is deprecated since prometheus-operator 0.38.0 as per https://github.com/prometheus-operator/prometheus-operator/blob/master/CHANGELOG.md#0380--2020-03-20

Signed-off-by: paulfantom <pawel@krupa.net.pl>
2021-07-01 17:27:54 +02:00
Stefan Prodan
5c4b3d1080 Merge pull request #1575 from dminca/feature/simplify-panel-label
fix Control Plane dashboard legend
2021-06-30 18:27:00 +03:00
Daniel-Andrei Minca
c98cd10621 fix Control Plane dashboard legend
The legend was not showing the Pod name, instead the whole resource in
the dashboard

As a result, use the correct Prometheus label

Resolves:
Related:
Signed-off-by: Daniel-Andrei Minca <mandrei17@gmail.com>
2021-06-30 16:10:53 +02:00
Stefan Prodan
e4af8ddcc8 Merge pull request #1574 from fluxcd/update-components
Update kustomize-controller to v0.13.1
2021-06-30 15:29:30 +03:00
fluxcdbot
b5ce02b8cf Update toolkit components
- kustomize-controller to v0.13.1
  https://github.com/fluxcd/kustomize-controller/blob/v0.13.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-06-30 12:08:03 +00:00
Stefan Prodan
cd42b3fb44 Merge pull request #1569 from fluxcd/update-components
Update source-controller to v0.15.3
2021-06-29 18:17:53 +03:00
fluxcdbot
6dedb767c3 Update toolkit components
- source-controller to v0.15.3
  https://github.com/fluxcd/source-controller/blob/v0.15.3/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-06-29 14:29:00 +00:00
Stefan Prodan
4de3268f3b Merge pull request #1570 from fluxcd/ci-debug
e2e: Update Kubernetes to v1.21.1
2021-06-29 17:28:10 +03:00
Stefan Prodan
a72ff06d79 e2e: Update Kubernetes to v1.21.1
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-29 17:15:59 +03:00
Stefan Prodan
cf239deb3d Print pods status on e2e failures
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-29 15:57:09 +03:00
Stefan Prodan
8735229745 Merge pull request #1555 from fluxcd/trace-cmd
Implement flux trace command
2021-06-29 11:22:45 +03:00
Stefan Prodan
3f081ba2d9 Add flux trace to e2e tests
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-29 11:01:20 +03:00
Stefan Prodan
3f613341cb Extend tracing to owner references
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-29 11:01:01 +03:00
Stefan Prodan
4305b8a77d Implement flux trace command
The trace command allows Flux users to point the CLI to a Kubernetes object in-cluster and get a detailed report about the GitOps pipeline that manages that particular object.

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-29 11:01:00 +03:00
Stefan Prodan
fab91d44c3 Merge pull request #1568 from fluxcd/image-v1beta1
Use image API v1beta1
2021-06-29 11:00:26 +03:00
Michael Bridgen
34484734d8 Use image API v1beta1
Signed-off-by: Michael Bridgen <michael@weave.works>
2021-06-28 16:20:05 +01:00
Stefan Prodan
10c58bb007 Merge pull request #1567 from fluxcd/update-components
Update toolkit components
2021-06-28 17:58:05 +03:00
fluxcdbot
1c1fd62a91 Update toolkit components
- image-reflector-controller to v0.11.0
  https://github.com/fluxcd/image-reflector-controller/blob/v0.11.0/CHANGELOG.md
- image-automation-controller to v0.14.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.14.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-06-28 14:45:58 +00:00
Stefan Prodan
637ac1e3fe Merge pull request #1559 from fluxcd/gh-action-arm
Add ARM and ARM64 support to flux GitHub Action
2021-06-25 15:19:12 +03:00
Stefan Prodan
f52387e849 Add ARM and ARM64 support to flux GitHub Action
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-25 13:32:39 +03:00
Stefan Prodan
9efc4986f9 Merge pull request #1553 from fluxcd/bootstrap-libgit2-test
Add libgit2 test to bootstrap workflow
2021-06-22 18:49:38 +03:00
Stefan Prodan
fdc366ec1c Increase wait time for the helm e2e test
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-22 18:37:04 +03:00
Stefan Prodan
d455db444c Add libgit2 test to bootstrap workflow
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-22 18:24:10 +03:00
Stefan Prodan
17418c9858 Merge pull request #1554 from fluxcd/update-components
Update toolkit components
2021-06-22 18:21:13 +03:00
fluxcdbot
5da7eb7d0d Update toolkit components
- source-controller to v0.15.2
  https://github.com/fluxcd/source-controller/blob/v0.15.2/CHANGELOG.md
- image-automation-controller to v0.13.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.13.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-06-22 14:00:42 +00:00
Stefan Prodan
9da977ee7a Merge pull request #1547 from fluxcd/gh-amr64-runner
Add provisioning script for ARM64 GitHub runners
2021-06-21 09:41:44 +03:00
Stefan Prodan
8e4b2ead9b Add provisioning script for ARM64 GitHub runners
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-19 14:58:57 +03:00
Hidde Beydals
fd364828a1 Merge pull request #1544 from fluxcd/create-target-namespace
Add create target namespace arg to helmrelease cmd
2021-06-18 17:00:05 +02:00
Hidde Beydals
afa58d8c08 Merge pull request #1541 from fluxcd/update-components
Update toolkit components
2021-06-18 16:58:35 +02:00
Stefan Prodan
179062876e Add create target namespace arg to helmrelease cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-18 17:39:31 +03:00
fluxcdbot
a796f3609f Update toolkit components
- helm-controller to v0.11.1
  https://github.com/fluxcd/helm-controller/blob/v0.11.1/CHANGELOG.md
- source-controller to v0.15.1
  https://github.com/fluxcd/source-controller/blob/v0.15.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-06-18 13:44:52 +00:00
Hidde Beydals
b7c6db74d2 Merge pull request #1542 from fluxcd/update-deps
Update source-controller to v0.15.1
2021-06-18 15:21:57 +02:00
Hidde Beydals
4f7b040405 Update source-controller to v0.15.1
This includes an introduction of a `--pass-credentials` flag for the
`flux create source helm` command to allow configuring the new
option introduced.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-06-18 15:04:48 +02:00
Stefan Prodan
34ca29830e Merge pull request #1540 from fluxcd/e2e-arm64
Run conformance tests on ARM64 Kubernetes clusters
2021-06-18 14:14:39 +03:00
Stefan Prodan
78f1b634fa Run end-to-end tests on Ampere ARM64
GitHub self-hosted runner info:
- Owner: Stefan Prodan
- VM: Oracle Cloud VM.Standard.A1.Flex 4CPU 24GB RAM
- OS: Linux 5.4.0-1045-oracle #49-Ubuntu SMP aarch64
- Packages: docker, kind, kubectl, kustomize

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-18 13:07:58 +03:00
Stefan Prodan
044bc64ad9 Merge pull request #1528 from NissesSenap/bug/arc-sync
Remove resourceNames in integration secrets
2021-06-18 10:25:22 +03:00
Edvin N
091f439498 Merge branch 'main' into bug/arc-sync 2021-06-18 08:47:58 +02:00
Stefan Prodan
a17b0a1ce0 Merge pull request #1535 from fluxcd/update-components
Update source-controller to v0.15.0
2021-06-17 20:29:52 +03:00
fluxcdbot
354cd5e177 Update toolkit components
- source-controller to v0.15.0
  https://github.com/fluxcd/source-controller/blob/v0.15.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-06-17 17:05:08 +00:00
Stefan Prodan
4e8f1221f7 Merge pull request #1534 from fluxcd/skip-deploy-key-prompt
Allow disabling the deploy key prompt for bootstrap git
2021-06-17 16:49:00 +03:00
Stefan Prodan
6b179aa7d9 Allow disabling the deploy key prompt for bootstrap git
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-17 15:20:49 +03:00
Edvin Norling
f748114dfa Remove resourceNames in integration secrets
* Solves #1524
* We remove resourceName due to the following:
  Note: You cannot restrict create or deletecollection requests by resourceName.
  For create, this limitation is because the object name is not known at authorization time.
* Fix typo in azure-registry cronjob
Signed-off-by: Edvin Norling <edvin.norling@xenit.se>
2021-06-16 14:45:30 +02:00
Stefan Prodan
5de83f015a Merge pull request #1519 from fluxcd/kustomize-v4
Update to Kustomize v4
2021-06-15 11:40:49 +03:00
Stefan Prodan
a6620e478a Update to Kustomize v4
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-15 11:25:57 +03:00
Stefan Prodan
c7fcffdd8e Merge pull request #1490 from fluxcd/update-components
Update toolkit components
2021-06-14 18:22:15 +03:00
fluxcdbot
160f59a984 Update toolkit components
- helm-controller to v0.11.0
  https://github.com/fluxcd/helm-controller/blob/v0.11.0/CHANGELOG.md
- kustomize-controller to v0.13.0
  https://github.com/fluxcd/kustomize-controller/blob/v0.13.0/CHANGELOG.md
- source-controller to v0.14.0
  https://github.com/fluxcd/source-controller/blob/v0.14.0/CHANGELOG.md
- notification-controller to v0.15.0
  https://github.com/fluxcd/notification-controller/blob/v0.15.0/CHANGELOG.md
- image-reflector-controller to v0.10.0
  https://github.com/fluxcd/image-reflector-controller/blob/v0.10.0/CHANGELOG.md
- image-automation-controller to v0.12.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.12.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-06-14 15:02:31 +00:00
Stefan Prodan
d38d487c2a Merge pull request #1505 from fluxcd/fix-yq-example
Fix yq example for create secret git
2021-06-11 14:00:44 +03:00
Stefan Prodan
db28907543 Fix yq example for create secret git
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-11 12:58:46 +03:00
Stefan Prodan
c4261399b5 Merge pull request #1472 from fluxcd/go-git-v5.4.2
Update go-git to v5.4.2
2021-06-02 20:58:39 +03:00
Stefan Prodan
b4edb46269 Update go-git to v5.4.2
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-06-02 20:20:02 +03:00
Stefan Prodan
a20ed0e630 Merge pull request #1471 from fluxcd/update-components
Update toolkit components
2021-06-02 20:17:46 +03:00
fluxcdbot
cea869e285 Update toolkit components
- kustomize-controller to v0.12.2
  https://github.com/fluxcd/kustomize-controller/blob/v0.12.2/CHANGELOG.md
- source-controller to v0.13.2
  https://github.com/fluxcd/source-controller/blob/v0.13.2/CHANGELOG.md
- image-automation-controller to v0.11.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.11.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-06-02 16:46:24 +00:00
Stefan Prodan
e12db14d1e Merge pull request #1469 from stealthybox/integrations-fixes
Fix and Refactor integrations
2021-06-02 18:38:47 +03:00
leigh capili
296bf3cc6c Fix eventhub integration config patches
Signed-off-by: leigh capili <leigh@null.net>
2021-06-01 14:42:02 -06:00
leigh capili
1789aa180d Remove unused kustomizeconfigs from integrations
Signed-off-by: leigh capili <leigh@null.net>
2021-06-01 14:42:02 -06:00
leigh capili
bd255800db Template AzureIdentityBinding using $(AZ_IDENTITY_NAME) for integrations
Signed-off-by: leigh capili <leigh@null.net>
2021-06-01 14:42:02 -06:00
leigh capili
1355962b3c Fix GCP integration container image
Signed-off-by: leigh capili <leigh@null.net>
2021-06-01 14:42:01 -06:00
leigh capili
bb0114e379 Remove per-cloud /kbin/kubectl patches
Signed-off-by: leigh capili <leigh@null.net>
2021-06-01 14:42:01 -06:00
leigh capili
f9622a5b9e Add /kbin/kubectl to _base integrations
Signed-off-by: leigh capili <leigh@null.net>
2021-06-01 14:42:01 -06:00
leigh capili
3a74fcd75c Add Makefile to test integrations
Signed-off-by: leigh capili <leigh@null.net>
2021-06-01 14:42:00 -06:00
Stefan Prodan
7265276cc2 Merge pull request #1454 from fluxcd/gofish
Add GoFish as an install option for Flux CLI
2021-05-28 13:08:47 +03:00
Stefan Prodan
b98027b528 Add GoFish as an install option for Flux CLI
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-05-28 12:57:02 +03:00
Stefan Prodan
b6ae7d2cdd Merge pull request #1453 from fluxcd/update-components
Update source-controller to v0.13.1
2021-05-28 12:49:09 +03:00
fluxcdbot
aa887c61c3 Update toolkit components
- source-controller to v0.13.1
  https://github.com/fluxcd/source-controller/blob/v0.13.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-05-28 09:30:21 +00:00
Hidde Beydals
700cef0989 Merge pull request #1349 from fluxcd/fix-throttling
Avoid throttling when some Flux CRDs are not registered
2021-05-26 17:42:22 +02:00
Stefan Prodan
3ed3e553e7 Avoid throttling when some Flux CRDs are not registered
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-05-26 18:29:04 +03:00
Hidde Beydals
d68158ddc9 Merge pull request #1408 from fluxcd/update-components
Update toolkit components
2021-05-26 17:06:31 +02:00
fluxcdbot
9f83a69242 Update toolkit components
- kustomize-controller to v0.12.1
  https://github.com/fluxcd/kustomize-controller/blob/v0.12.1/CHANGELOG.md
- source-controller to v0.13.0
  https://github.com/fluxcd/source-controller/blob/v0.13.0/CHANGELOG.md
- notification-controller to v0.14.1
  https://github.com/fluxcd/notification-controller/blob/v0.14.1/CHANGELOG.md
- image-automation-controller to v0.10.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.10.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-05-26 14:53:26 +00:00
Hidde Beydals
bf69dbd43d Merge pull request #1449 from fluxcd/update-go-git
Update go-git to v5.4.1
2021-05-26 16:15:36 +02:00
Hidde Beydals
465ea5ccfd Update go-git to v5.4.1
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-05-26 15:56:50 +02:00
Stefan Prodan
92ef39e2ad Merge pull request #1411 from NissesSenap/feature/azure-eventhub
Add example manifests for Azure eventhub credentials renewal
2021-05-25 16:35:12 +03:00
Edvin Norling
0404790df9 How to automatically renew Azure eventhub
To use JWT to communicate with Azure eventhub we need to renew the JWT credentials
from time to time. This example yaml helps out with that
* Supports both deployment and cronjob based renewal
  * static service principal
  * aad-pod-identity in azure

Signed-off-by: Edvin Norling <edvin.norling@xenit.se>
2021-05-25 13:43:18 +02:00
Stefan Prodan
f880e93df4 Merge pull request #1415 from allymparker/main
Fix service account name in registry-credentials-sync deployment kustomization
2021-05-14 20:06:59 +03:00
Ally Parker
4697b1101d Fix service account
Signed-off-by: Ally Parker <ally.parker@red-gate.com>
2021-05-14 16:40:30 +01:00
Stefan Prodan
50ff2accd2 Merge pull request #1412 from fluxcd/enable-crd-upgrades
Enable CRDs upgrade for kube-prometheus-stack
2021-05-12 19:06:49 +03:00
Stefan Prodan
c7d876eb8f Enable CRDs upgrade for kube-prometheus-stack
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-05-12 18:40:00 +03:00
Stefan Prodan
eda392dfcd Merge pull request #1399 from SomtochiAma/kube-prometheus
Replace monitoring stack with kube-prometheus-stack
2021-05-12 09:21:34 +03:00
Somtochi Onyekwere
3b91e14f6d Use kube-prometheus-stack for monitoring
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-05-12 06:53:21 +01:00
Hidde Beydals
17e3c57d7e Merge pull request #1405 from fluxcd/update-components
Update toolkit components
2021-05-10 18:10:50 +02:00
fluxcdbot
1c744a0f97 Update toolkit components
- helm-controller to v0.10.1
  https://github.com/fluxcd/helm-controller/blob/v0.10.1/CHANGELOG.md
- source-controller to v0.12.2
  https://github.com/fluxcd/source-controller/blob/v0.12.2/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-05-10 15:58:12 +00:00
Hidde Beydals
99bdb20aeb Merge pull request #1404 from fluxcd/private-key-password 2021-05-10 16:02:06 +02:00
Hidde Beydals
fbe7050cb8 Switch to crypto/ssh for parsing of private keys
This changes the logic for the parsing of private keys, as already
done for the source-controller, so that it is able to recognize and
work with a wider range of key formats instead of returning a vague
error:

```console
$ flux bootstrap git [..]
✗ ssh: this private key is passphrase protected
```

A patch for this was already submitted and merged in `go-git/go-git`,
but is not made available in a release yet:
https://github.com/go-git/go-git/pull/298

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-05-10 15:30:25 +02:00
Hidde Beydals
12ea028aa9 Merge pull request #1379 from tjakobsson/fix-git-bootstrap-hostname
Use proper Host configuration for SSH
2021-05-10 15:23:59 +02:00
Tobias Jakobsson
ea62cb5fc9 Use proper Host configuration for SSH
This removes the usage of Hostname() which does not honor configured SSH
port to be used.

Resolves: #1377
See also: #1101, #1102

Signed-off-by: Tobias Jakobsson <jakobsson.tobias@gmail.com>
2021-05-10 15:04:09 +02:00
Stefan Prodan
d27c2164b2 Merge pull request #1394 from fluxcd/update-components
Update image-automation-controller to v0.9.1
2021-05-06 17:27:23 +03:00
fluxcdbot
ef8f5cb87d Update toolkit components
- image-automation-controller to v0.9.1
  https://github.com/fluxcd/image-automation-controller/blob/v0.9.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-05-06 14:15:14 +00:00
Stefan Prodan
378a2c2a0e Merge pull request #1393 from fluxcd/git-ref-required
Make the Git ref required
2021-05-06 16:30:16 +03:00
Stefan Prodan
2597ad0f73 Make the Git ref required
Remove the default branch value from `flux create source git` and validate that one of the ref options are specified.

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-05-06 15:28:55 +03:00
Stefan Prodan
0df34bed59 Merge pull request #1391 from SomtochiAma/grafana-dash
Update Prometheus labels and dashboard
2021-05-06 15:05:49 +03:00
Somtochi Onyekwere
be65cf8052 Change labels in prometheus and grafana dashboard
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-05-06 12:33:41 +01:00
Stefan Prodan
8922753591 Merge pull request #1390 from Callisto13/string-slice-var
Switch StringArrayVar flags to use StringSliceVar
2021-05-06 14:27:51 +03:00
Claudia Beresford
87e11ed653 Switch StringArrayVar flags to use StringSliceVar
StringSliceVar allows for more flexibility when passing vars to list
flags.
Both formats will be supported:
- '--foo=one --foo=two'
- '--foo=one,two'

Signed-off-by: Claudia Beresford <claudiaberesford@gmail.com>
2021-05-06 10:09:36 +01:00
Stefan Prodan
ab34771b3d Merge pull request #1384 from fluxcd/go-git-providers-v0.1.0
Update go-git-providers to v0.1
2021-05-06 11:31:00 +03:00
Stefan Prodan
e733c4f55a Update go-git-providers to v0.1.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-05-06 11:03:52 +03:00
Daniel Holbach
37b60666c4 Merge pull request #1389 from dholbach/fix-1388
Remove ' command' from Flux CLI docs title
2021-05-06 10:00:56 +02:00
Daniel Holbach
734d736bdf Remove ' command' from Flux CLI docs title
Fixes: #1388

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-05-06 08:52:36 +02:00
Daniel Holbach
8ca65059f7 Merge pull request #1381 from dholbach/toolkit-website-followup
Update more toolkit.fluxcd.io redirects
2021-05-04 22:16:41 +02:00
Daniel Holbach
086f174463 Update more toolkit.fluxcd.io redirects
Following up on #1380 some more docs links which
	now live under fluxcd.io itself.

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-05-04 17:04:44 +02:00
Daniel Holbach
48fd70fc09 Merge pull request #1380 from fluxcd/fix-links
Migrate the GitOps toolkit links to the new docs website
2021-05-04 16:59:09 +02:00
Stefan Prodan
606266e976 Migrate the GitOps toolkit links to the new docs website
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-05-04 17:46:25 +03:00
Stefan Prodan
f7006a8172 Merge pull request #1369 from fluxcd/update-components
Update toolkit components
2021-04-29 15:05:43 +03:00
fluxcdbot
653dcc8d78 Update toolkit components
- kustomize-controller to v0.12.0
  https://github.com/fluxcd/kustomize-controller/blob/v0.12.0/CHANGELOG.md
- image-reflector-controller to v0.9.1
  https://github.com/fluxcd/image-reflector-controller/blob/v0.9.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-04-29 10:07:39 +00:00
Stefan Prodan
8df140c713 Merge pull request #1365 from dholbach/fix-1221
Remove content which has moved to f/website
2021-04-29 10:24:01 +03:00
Daniel Holbach
089af9cc90 remove docs/index.md as well - it has been pulled into f/website now too
Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-28 17:18:00 +02:00
Daniel Holbach
695fb55b13 Remove Flux CLI docs
They are imported into the docs like so:
	https://github.com/fluxcd/website/blob/main/hack/import-flux2-assets.sh#L139

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-28 12:10:48 +02:00
Daniel Holbach
ec21eedd56 remove content which has moved to f/website
Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-28 12:07:34 +02:00
Stefan Prodan
5ba3774fd5 Merge pull request #1358 from SomtochiAma/suspend-all
Add suspend/resume  --all cmd
2021-04-28 12:58:46 +03:00
Somtochi Onyekwere
12a2100fcf Adds suspend and resume all cmd
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-04-28 10:44:51 +01:00
Daniel Holbach
68074d3543 Merge pull request #1364 from dholbach/update-install-url
Update install script URL
2021-04-27 16:40:33 +02:00
Daniel Holbach
18849e36c7 Update install script URL
As we don't pass '-L' to curl, the redirect is
	not followed.

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-27 16:26:06 +02:00
Hidde Beydals
671fe274da Merge pull request #1363 from dholbach/explain-docs
Explain where the docs went
2021-04-27 15:28:21 +02:00
Daniel Holbach
af1d9102b9 explain where docs are
keep netlify config for now (disabled in the app), but only make it ship the _redirects file
add rule for 'install.sh'
move _redirects file into docs/ directory
document redirects and Netlify

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-27 15:16:30 +02:00
Daniel Holbach
9dc10ef7d1 Merge pull request #1361 from dholbach/fix-1135
Stop deploy to Github pages
2021-04-27 14:32:57 +02:00
Daniel Holbach
86a3cf20e7 Stop deploying to Github pages
Fixes: #1135

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-27 14:20:28 +02:00
Daniel Holbach
27a42ecd8e Merge pull request #1360 from dholbach/add-redirects
add redirects file
2021-04-27 14:19:17 +02:00
Daniel Holbach
ae7a59fbb4 try out redirects file
Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-27 13:55:16 +02:00
Hidde Beydals
598dfc32e8 Merge pull request #1359 from dholbach/add-components-docs-script
Copy docs assets for Netlify build
2021-04-27 11:36:31 +02:00
Daniel Holbach
a40d124e23 add script to copy docs assets for Netlify build
Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-27 11:22:55 +02:00
Daniel Holbach
9df3fcab18 Merge pull request #1353 from dholbach/netlify-build
Specify netlify build
2021-04-27 11:09:33 +02:00
Daniel Holbach
b6ce969d1b Specify netlify build
Addresses: #1135

	Follow https://www.starfallprojects.co.uk/posts/deploy-mkdocs-netlify/
	to eventually fix #1135.

	I realise this litters the main directory somewhat, but I hope
	that once the publication fully works and we turn the site into
	redirects, we can remove these files again. So only a temporary
	measure.

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-04-27 09:16:06 +02:00
Stefan Prodan
6a37649ee6 Merge pull request #1352 from SomtochiAma/handle-error
Remove redundant getCommand for image repository
2021-04-26 17:50:56 +03:00
Somtochi Onyekwere
8926095660 remove redundant getCommand for image repository
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-04-26 15:28:51 +01:00
Hidde Beydals
bd34870334 Merge pull request #1336 from fluxcd/update-components 2021-04-23 12:51:40 +02:00
Hidde Beydals
a56ce1f867 build: tidy after go mod edit
To ensure the `go.sum` is always up-to-date when the following `go mod
edit` is executed.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-23 12:36:09 +02:00
fluxcdbot
dab5bbd393 Update toolkit components
- source-controller to v0.12.1
  https://github.com/fluxcd/source-controller/blob/v0.12.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-04-23 12:34:41 +02:00
Stefan Prodan
15ebfd7eb6 Merge pull request #1334 from fluxcd/helm-CRDsPolicy
Add upgrade CRDs policy arg to create helmrelease cmd
2021-04-23 12:29:24 +03:00
Stefan Prodan
5ab8dd2557 Add upgrade CRDs policy to create helmrelease cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-23 10:46:46 +03:00
Stefan Prodan
9164914d16 Merge pull request #1215 from Frederik-Baetens/main
update sortable image tag guide with github.run_number
2021-04-23 09:25:20 +03:00
Frederik Baetens
c9e0bc0807 add github.run number github actions workflow example
Signed-off-by: Frederik Baetens <baetens.fr@gmail.com>
2021-04-22 22:27:46 +02:00
Frederik Baetens
61439adf9b describe github.run number as a reliable increasing build number
Signed-off-by: Frederik Baetens <baetens.fr@gmail.com>
2021-04-22 22:27:46 +02:00
Hidde Beydals
e4d7450643 Merge pull request #1332 from fluxcd/update-guide-v1alpha2
Update image automation guides to v1alpha2
2021-04-22 19:54:25 +02:00
Stefan Prodan
0fbcfded57 Update image automation migration guide to v1alpha2 APIs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-22 20:40:24 +03:00
Stefan Prodan
fb3a434f95 Update image automation guide to v1alpha2 APIs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-22 20:40:17 +03:00
Hidde Beydals
4f66da84d6 Merge pull request #1208 from defenestration/main
fix link to Mozilla SOPS Azure Guide
2021-04-22 19:38:51 +02:00
Alan B
b67e8aafab Fix Mozilla SOPS link in Azure guide
Signed-off-by: Alan B <a.brevick@techsmith.com>
2021-04-22 19:21:26 +02:00
Hidde Beydals
0b4f1d30a6 Merge pull request #1314 from SomtochiAma/ssh-key-with-password
Add password for ssh private key to create secret git
2021-04-22 19:17:33 +02:00
Somtochi Onyekwere
c494e6bf7e Inject password in create secret git if specified
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-04-22 18:33:41 +02:00
Hidde Beydals
b8c57c7901 Merge pull request #1300 from fluxcd/kustomize-bootstrap
Allow pre-bootstrap customisation of Flux components
2021-04-22 17:33:57 +02:00
Hidde Beydals
6aed4631e7 Register v1alpha2 APIs in runtime Scheme
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-22 17:10:42 +02:00
Stefan Prodan
5df9118365 Add pre-bootstrap customisation to install docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-22 17:10:42 +02:00
Stefan Prodan
4a4af94d6c Allow pre-bootstrap customisation of Flux components
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-22 17:10:42 +02:00
Hidde Beydals
baa54fb84a Merge pull request #1330 from fluxcd/patch-update-workflow
Fix update automation and update CRDs
2021-04-22 16:48:35 +02:00
Hidde Beydals
cb6470f817 Merge pull request #1328 from fluxcd/bootstrap-team-prnt-fix
Change permission grant error print conditons
2021-04-22 16:25:26 +02:00
Hidde Beydals
1a904e138f Fix update automation and update CRDs
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-22 16:24:36 +02:00
Hidde Beydals
3b482529ff Merge pull request #1323 from fluxcd/update-apis
Update the APIs docs
2021-04-22 16:11:16 +02:00
Hidde Beydals
67997437db Change permission grant error print conditons
Based on observations in
https://github.com/fluxcd/flux2/runs/2410633975:

1. Print error correctly by switching from `%w` to `%s`
2. Only print the change messsage if there has not been an error.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-22 16:10:14 +02:00
Hidde Beydals
a5541eddca Merge pull request #1327 from fluxcd/update-components
Update toolkit components
2021-04-22 15:57:00 +02:00
Stefan Prodan
203157e525 Update the APIs docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-22 15:56:49 +02:00
fluxcdbot
655c2261ba Update toolkit components
- helm-controller to v0.10.0
  https://github.com/fluxcd/helm-controller/blob/v0.10.0/CHANGELOG.md
- kustomize-controller to v0.11.1
  https://github.com/fluxcd/kustomize-controller/blob/v0.11.1/CHANGELOG.md
- source-controller to v0.12.0
  https://github.com/fluxcd/source-controller/blob/v0.12.0/CHANGELOG.md
- notification-controller to v0.13.0
  https://github.com/fluxcd/notification-controller/blob/v0.13.0/CHANGELOG.md
- image-reflector-controller to v0.9.0
  https://github.com/fluxcd/image-reflector-controller/blob/v0.9.0/CHANGELOG.md
- image-automation-controller to v0.9.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.9.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-04-22 15:42:12 +02:00
Hidde Beydals
45e446eb00 Merge pull request #1325 from fluxcd/build/include-crd-update
build: incl CRD version change in component update
2021-04-22 15:34:52 +02:00
Hidde Beydals
68abe37648 Merge pull request #1200 from kingdonb/jsonnet 2021-04-22 15:22:26 +02:00
Hidde Beydals
df6a0a3762 build: incl CRD version change in component update
This includes updating the version in the `manifests/crds` directory
for the component thas has a newer latest version.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-22 15:21:24 +02:00
Kingdon Barrett
c35bae577f Add (vestigial) reference to configMap.yaml
This part of the jsonnet example was missing a reference and needed a
bit more explanation to accompany the missing reference.

Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-04-22 15:04:07 +02:00
Kingdon Barrett
3567941eda flux recommends real version numbers*
Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-04-22 15:04:07 +02:00
Kingdon Barrett
3c95fe6380 change one word
Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-04-22 15:04:07 +02:00
Kingdon Barrett
d07f0d003c add word or two
Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-04-22 15:04:07 +02:00
Kingdon Barrett
86774309db minor formatting/verbal fixes
Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-04-22 15:04:07 +02:00
Kingdon Barrett
427f23e32d add missing link to 04-update-fleet-infra
Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-04-22 15:04:07 +02:00
Kingdon Barrett
70cddde16c Jsonnet examples
It is probably not elegant or idiomatic jsonnet. I am learning Jsonnet.

I believe the explanation is correct, but I still have to retry this
example for repeatability and check for completeness.

* Jsonnet example - gutted

* take some personalize things away
* clean up awkward sentence
* for real gitops

Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-04-22 15:04:05 +02:00
Hidde Beydals
e86789b643 Merge pull request #1255 from scottrigby/use-cases-helm 2021-04-22 14:52:56 +02:00
Scott Rigby
f52fec66bd Overriding helm values, managing secrets and configmaps with kustomize plus SOPS, semver range policies, and auto uninstalls and rollbacks
Signed-off-by: Scott Rigby <scott@r6by.com>
2021-04-22 14:19:39 +02:00
Scott Rigby
f4926d1e45 Operator->Controller
Signed-off-by: Scott Rigby <scott@r6by.com>

Co-authored-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-22 14:19:39 +02:00
Scott Rigby
2774c1a5cd Add helm use case intro page
Signed-off-by: Scott Rigby <scott@r6by.com>
2021-04-22 14:19:39 +02:00
Hidde Beydals
8274bc0ea3 Merge pull request #1324 from fluxcd/image-update-v1alpha2
Move to `v1alpha2` image update APIs
2021-04-22 14:11:47 +02:00
Stefan Prodan
e9531e4d57 Merge pull request #1296 from arbourd/values-files
Add `ValuesFiles` documentation
2021-04-22 14:52:47 +03:00
Hidde Beydals
3a8aad7e5c Move to v1alpha2 image update APIs
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-22 13:52:03 +02:00
Dylan Arbour
ef079c5b58 Add ValuesFiles documentation
Signed-off-by: Dylan Arbour <arbourd@users.noreply.github.com>
2021-04-22 13:34:19 +02:00
Hidde Beydals
50332aa2ee Merge pull request #1310 from fluxcd/update-components
Update toolkit components
2021-04-22 13:18:08 +02:00
fluxcdbot
b47f3a57dc Update toolkit components
- helm-controller to v0.10.0
  https://github.com/fluxcd/helm-controller/blob/v0.10.0/CHANGELOG.md
- kustomize-controller to v0.11.1
  https://github.com/fluxcd/kustomize-controller/blob/v0.11.1/CHANGELOG.md
- source-controller to v0.12.0
  https://github.com/fluxcd/source-controller/blob/v0.12.0/CHANGELOG.md
- notification-controller to v0.13.0
  https://github.com/fluxcd/notification-controller/blob/v0.13.0/CHANGELOG.md
- image-reflector-controller to v0.9.0
  https://github.com/fluxcd/image-reflector-controller/blob/v0.9.0/CHANGELOG.md
- image-automation-controller to v0.9.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.9.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-04-22 09:49:12 +00:00
Stefan Prodan
9a928744cc Merge pull request #1264 from SomtochiAma/ssh-key-with-password
Add support for password protected SSH keys to bootstrap
2021-04-21 19:39:10 +03:00
Somtochi Onyekwere
328d403507 Set password in secret
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-04-21 17:21:07 +01:00
Stefan Prodan
76ffd76bd3 Merge pull request #1287 from SomtochiAma/get-all-cmd
Add get all command
2021-04-21 18:39:47 +03:00
Somtochi Onyekwere
951589e652 Add get all command
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-04-21 15:45:38 +01:00
Hidde Beydals
7bb0704401 Merge pull request #1308 from fluxcd/windows-path-boot-git
Use slash target path in Git bootstrap sync opts
2021-04-21 12:08:52 +02:00
Hidde Beydals
3aa45e72e7 Use slash target path in Git bootstrap sync opts
As otherwise (comparisons to) cluster configuration will fail due to
Separator differences. Was already fixed for provider implementations.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-21 11:48:36 +02:00
Hidde Beydals
67691e92e3 Merge pull request #1307 from fluxcd/boot-drop-org-repo-autoinit
Drop AutoInit from Org repository create
2021-04-21 11:44:17 +02:00
Hidde Beydals
43388ec67b Drop AutoInit from Org repository create
Pushing the first branch is sufficient to set a default, and the
`README.md` (and/or LICENSE) can better be pushed later on so commit
author and templates be configured.

This was already done for User in an earlier patch release.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-21 11:24:59 +02:00
Stefan Prodan
2fe3934491 Merge pull request #1280 from kaizentm/main
Keep network policy order as defined
2021-04-20 10:13:30 +03:00
Eugene
f60ba95b4c Keep policy order as defined
Signed-off-by: Eugene <eugene.fedor@gmail.com>
2021-04-19 13:21:32 -07:00
Hidde Beydals
4ecf541748 Merge pull request #1291 from jlengelsen/bug-install-script-binary-ownership
Fix ownership issue in bash install script
2021-04-19 15:01:25 +02:00
Julian Lengelsen
7994829765 Fix ownership issue in bash install script
When using tar with sudo the extracted files will retain the ownership
of the files in the archive. When using the bash install script the flux
binary is owned by user ID 1001 and group docker after installation.

This commit fixes the ownership by appending the -o option to the tar
command which will extract files with the correct ownership, namely user
root and group root.

Signed-off-by: Julian Lengelsen <julian.lengelsen@th-koeln.de>
2021-04-18 13:30:09 +02:00
Hidde Beydals
ce14951436 Merge pull request #1288 from sa-spag/doc 2021-04-16 16:35:30 +02:00
Alexis Gauthiez
a5ce8221a3 Suggest an alternative gradual migration technique
Signed-off-by: Alexis Gauthiez <alexis.gauthiez@blablacar.com>
2021-04-16 14:45:50 +02:00
Alexis Gauthiez
e6344ef18e Fix documentation typo
Signed-off-by: Alexis Gauthiez <alexis.gauthiez@blablacar.com>
2021-04-16 14:45:50 +02:00
Stefan Prodan
99e60634ad Merge pull request #1285 from SomtochiAma/reconcile-diff-ns
Ensure kustomization/helmrelease is reconciled when source is in a different namespace
2021-04-16 11:25:03 +03:00
Somtochi Onyekwere
6c656b7366 Fix reconcile with source in a different namespace
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-04-16 08:53:30 +01:00
Stefan Prodan
fc3a09b5ad Merge pull request #1278 from alisondy/create-alert-es-ns
Change createAlertCmdRun parsing to include namespace
2021-04-16 08:50:26 +03:00
Alison Dowdney
b1484f2f24 Change createAlertCmdRun parsing to include namespace
Signed-off-by: Alison Dowdney <alison@alisondowdney.com>
2021-04-15 22:52:15 +01:00
Hidde Beydals
7dcf884e38 Merge pull request #1262 from fluxcd/reconcile-opt
Put potentially destructive reconcile behind flag
2021-04-13 17:38:35 +02:00
Hidde Beydals
b6d349da8c Put potentially destructive reconcile behind flag
The behavior introduced during the introduction of go-git-providers
was more strict, and has proven pretty quickly to not be useful to
all users. Therefore, the reconciliation behavior for repository
configuration has been put behind an opt-in flag, so that it does
not overwrite people their configs by accident.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-13 17:19:19 +02:00
Stefan Prodan
40ce3d50c2 Merge pull request #1256 from fluxcd/openapi2jsonschema
Publish OpenAPI schemas for Flux CRDs
2021-04-13 15:17:38 +03:00
Stefan Prodan
68046067c5 Generate OpenAPI schema in CI
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-13 13:17:36 +03:00
Stefan Prodan
e3b12a8a24 Merge pull request #1253 from fluxcd/btstrp-private-flag
Change private flag description
2021-04-13 09:26:29 +03:00
Hidde Beydals
f123b9d3cb Change private flag description
To highlight the fact that it configures the repository as defined,
which was not _really_ clear to some users and has resulted in public
repositories accidentally being changed to private (losing important
goodies like stars and linked forks).

Discussion on this is ongoing and there will likely be other
improvements in the near future to protect users against this.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-12 13:19:46 +02:00
Hidde Beydals
f4ce89ae26 Merge pull request #1242 from fluxcd/bootstrap-health-tweaks
Always report components health in bootstrap
2021-04-09 16:21:37 +02:00
Hidde Beydals
ea451e7e49 Always report components health in bootstrap
This is useful in case the `Kustomization` does not reconcile
successfully because for example the controller(s) are in a crash loop,
which is not visible in the resource itself.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-09 15:20:04 +02:00
Hidde Beydals
d434575047 Merge pull request #1240 from fluxcd/to-slash-to-rescue 2021-04-09 11:09:37 +02:00
Hidde Beydals
e627634184 Detect suspended Kustomization in bootstrap
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-09 10:53:00 +02:00
Hidde Beydals
e0dd12505f Normalize paths to forward slashes
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-09 10:52:59 +02:00
Hidde Beydals
5a67f94380 Merge pull request #1241 from fluxcd/private-key-bug
Correctly load private key by not decoding PEM twice
2021-04-09 10:37:30 +02:00
Hidde Beydals
5f9dd7a5a5 Correctly load private key by not decoding PEM 2x
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-09 10:05:45 +02:00
Stefan Prodan
bce0da2806 Merge pull request #1226 from fluxcd/bootstrap-git-docs
Revamp bootstrap documentation
2021-04-08 15:23:42 +03:00
Stefan Prodan
a58c40f2d7 Add note about providing a SSH key to bootstrap
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-08 14:06:42 +03:00
Stefan Prodan
65d5cadf29 Update the alert providers list in notifications guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-08 13:31:21 +03:00
Stefan Prodan
1ea5d4d2e3 Remove sourceignore from SOPS guide
No longer needed due to https://github.com/fluxcd/source-controller/pull/329

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-08 13:31:16 +03:00
Stefan Prodan
719ef3c44c Add flux CLI container image to docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-08 13:31:16 +03:00
Stefan Prodan
f4adfc3029 Add bootstrap git to install docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-08 13:31:16 +03:00
Stefan Prodan
d8d08091cc Move Azure DevOps bootstrap to Azure docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-08 13:31:15 +03:00
Stefan Prodan
de4b3ef3dc Merge pull request #1231 from fluxcd/git-custom-pk
Take private key from file into account in Git bootstrap
2021-04-08 13:30:51 +03:00
Hidde Beydals
7bd6aedb73 Take PK from file into account in Git bootstrap
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-08 12:06:33 +02:00
Stefan Prodan
fffe40fbd4 Merge pull request #1222 from fluxcd/fix-git-http
Fix create source git auth for non-HTTPS repos
2021-04-08 11:11:03 +03:00
Stefan Prodan
74feda73af Add Warningf to logger interface amd impl
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-08 10:48:27 +03:00
Stefan Prodan
8b5583930e Fix create source git auth for non-HTTPS repos
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-08 10:29:23 +03:00
Hidde Beydals
466fdae70e Merge pull request #1225 from SomtochiAma/refactor-last-cmd
Format go imports in cmd/flux
2021-04-07 18:05:30 +02:00
Somtochi Onyekwere
054a62fb30 Format go imports
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-04-07 16:40:16 +01:00
Hidde Beydals
c694b570e0 Merge pull request #1224 from SomtochiAma/refactor-last-cmd
Refactor reconcile and resume cmd for alert and receiver
2021-04-07 15:02:22 +02:00
Somtochi Onyekwere
4204ec1d43 Refactor reconcile and resume cmd for alert and receiver
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-04-07 13:35:12 +01:00
Stefan Prodan
6d4e37ccb2 Merge pull request #1223 from fluxcd/cii-badge
Add CII Best Practices badge
2021-04-07 14:39:05 +03:00
Stefan Prodan
eef06c993e Add CII Best Practices badge
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-07 14:13:23 +03:00
Stefan Prodan
58362fbbb9 Merge pull request #1191 from fluxcd/recurse-submodules
Add recurse submodules arg to create source git and bootstrap cmd
2021-04-07 13:12:05 +03:00
Stefan Prodan
b872e595ae Add recurse submodules arg to bootstrap cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-07 12:56:51 +03:00
Stefan Prodan
18c3f79319 Add recurse submodules arg to create source git cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-07 12:01:25 +03:00
Stefan Prodan
8f0cd35d7a Allow self-signed certs when using go-git
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-04-07 12:00:48 +03:00
Stefan Prodan
c8bcf19f32 Merge pull request #1194 from fluxcd/update-components
Update toolkit components
2021-04-07 11:53:33 +03:00
fluxcdbot
5bee3047ac Update toolkit components
- kustomize-controller to v0.11.0
  https://github.com/fluxcd/kustomize-controller/blob/v0.11.0/CHANGELOG.md
- notification-controller to v0.12.0
  https://github.com/fluxcd/notification-controller/blob/v0.12.0/CHANGELOG.md
- image-reflector-controller to v0.8.0
  https://github.com/fluxcd/image-reflector-controller/blob/v0.8.0/CHANGELOG.md
- image-automation-controller to v0.8.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.8.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-04-07 08:41:09 +00:00
Hidde Beydals
0d2f6bf02d Merge pull request #968 from fluxcd/go-git-providers-bootstrap 2021-04-07 10:40:30 +02:00
Hidde Beydals
7481c6beb0 Retry reconcile and clone actions once
We have observed that the code at times outperforms GitHub mechanics,
resulting in not found errors that are only true for a millisecond.
Retrying those actions once with a 2 second delay should be more
friendly to users.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
4ece12348b Ignore broken symlinks and outside path, in commit
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
e65a5beaae Work around custom client domain issue
With this commit comes a lot of evil.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
ef576128e3 Use correct hostname argument for secret gen
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
7f0bc2ada2 Provide option to add appendix to commit messages
Using the `--commit-message-appendix` flag a string can be added to the
commit messages made by the bootstrapper process to for example skip CI
actions from executing using e.g. `[skip ci]`.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
96c373d045 Properly configure sync URL based on auth settings
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
22648cae3b Add command to bootstrap to generic Git server
This command makes it possible to bootstrap to a generic Git server
using the local SSH agent, or a given password or private key file.

If a private key is generated, the user is prompted to give the
generated key access to the repository.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
f57ce14754 Implement bootstrap package in commands
This includes making a lot of things configurable (e.g. SSH key
algorithm, RSA bit size, etc.) that used to be static.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
1d3a381389 Test giving access to team in bootstrap e2e
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
9055e753a9 Add app.kubernetes.io/part-of: flux label
To be used in a future version of Flux to better select Flux components
in a namespace, as the namespace value for the
`app.kubernetes.io/instance` could be used by non Flux related
workloads.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
6390812cbb Factor bootstrap logic into bootstrap package
This commit factors out the bootstrap logic into a new `bootstrap`
package, while also moving to `go-git-providers` to handle things
around Git providers (e.g. repository creation, deploy key
upsertions).

The `GitProviderBootstrapper` is a superset of the
`PlainGitBootstrapper` that besides `Reconciler` also implements the
`RepositoryReconciler`.

The Git actions rely on an interface, making it easier to support
other implementations than `go-git` at a later moment, to for example
support bootstrapping to Git servers that only support the v2 protocol.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-04-07 10:24:08 +02:00
Hidde Beydals
fa46f05423 Merge pull request #1219 from frankgu968/main
fix: install command flags logic bug
2021-04-06 17:13:06 +02:00
Frank Gu
6b0ffe0b13 fix: install command flags logic bug
Signed-off-by: Frank Gu <frank.gu968@outlook.com>
2021-04-06 07:57:36 -07:00
Stefan Prodan
e724d90202 Merge pull request #1190 from fluxcd/update-components
Update source-controller to v0.11.0
2021-04-01 09:22:42 +03:00
fluxcdbot
6129943685 Update toolkit components
- source-controller to v0.11.0
  https://github.com/fluxcd/source-controller/blob/v0.11.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-03-31 17:38:37 +00:00
Hidde Beydals
d4e37cbda5 Merge pull request #1179 from hiddeco/docker-images
Publish AMD64, ARM64, ARMv7 images for binary
2021-03-30 12:02:06 +02:00
Hidde Beydals
cccfb3a560 Merge pull request #1176 from kaaboaye/patch-2
Fix reocncile typo
2021-03-30 11:23:17 +02:00
Hidde Beydals
d0403038ed Enable QEMU and Docker Buildx in release action
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-30 11:22:31 +02:00
Hidde Beydals
a5a7d7970f Publish AMD64, ARM64, ARMv7 images for binary
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-30 11:22:31 +02:00
Mieszko Wawrzyniak
62b9377f15 Fix reocncile typo
Signed-off-by: kaaboaye <kaaboaye@gmail.com>
2021-03-30 10:20:37 +02:00
Stefan Prodan
ec2c71f9ef Merge pull request #1173 from fluxcd/source-namespace
Add source namespace to create commands
2021-03-29 13:22:08 +03:00
Stefan Prodan
b54fd2c6b3 Add source namespace to create commands
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-29 11:42:04 +03:00
Stefan Prodan
e5066c3712 Merge pull request #1171 from Legion2/patch-1
Updated automation migration guide filter tags crd
2021-03-29 10:25:22 +03:00
Leon Kiefer
fd1c038303 Updated automation migration guide filter tags crd
Signed-off-by: Leon Kiefer <leon.k97@gmx.de>
2021-03-28 21:09:10 +02:00
Hidde Beydals
64e7a857b8 Merge pull request #1164 from fluxcd/update-components
Update toolkit components
2021-03-26 16:55:26 +01:00
fluxcdbot
7da24932ab Update toolkit components
- helm-controller to v0.9.0
  https://github.com/fluxcd/helm-controller/blob/v0.9.0/CHANGELOG.md
- kustomize-controller to v0.10.0
  https://github.com/fluxcd/kustomize-controller/blob/v0.10.0/CHANGELOG.md
- source-controller to v0.10.0
  https://github.com/fluxcd/source-controller/blob/v0.10.0/CHANGELOG.md
- notification-controller to v0.11.0
  https://github.com/fluxcd/notification-controller/blob/v0.11.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-03-26 15:41:29 +00:00
Daniel Holbach
1a2ea8407b Merge pull request #1163 from dholbach/fix-cmd-links
Fix cmd links
2021-03-26 16:40:59 +01:00
Daniel Holbach
32f94bab97 fix links
- change links in cli docs to be relative (making mkdocs AND hugo happy)
	- run 'make docs'
	- fix other links

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-03-26 16:28:33 +01:00
Hidde Beydals
dea4a67639 Merge pull request #1162 from fluxcd/tidy-cmd-docs
Tidy up command descriptions
2021-03-26 10:38:02 +01:00
Hidde Beydals
236ffd1767 Tidy up command descriptions
Rewordings and removal of superfluous newlines.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-26 10:20:42 +01:00
Hidde Beydals
1b2ffad2f1 Merge pull request #1157 from fluxcd/create-secret-source-git-pk
Allow supplying PK from file for Git source/secret
2021-03-26 10:02:23 +01:00
Hidde Beydals
4750d0d81c Allow supplying PK from file for Git source/secret
This commit adds support for supplying a path to an existing private
key file to both the `flux create secret git` and `flux create source
git` commands.

If a path is given, any private key generation configuration options
are ignored by the manifest generator. The SSH host will however still
be scanned for server keys.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-26 09:49:30 +01:00
Hidde Beydals
63a210a0b2 Merge pull request #1149 from SomtochiAma/refactor-cmd 2021-03-25 22:39:46 +01:00
Somtochi Onyekwere
465eaa24d3 Refactor all remaining create, delete, export, get command to use adapter
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-03-25 21:54:58 +01:00
Hidde Beydals
c23e8c7ee1 Merge pull request #1156 from Legion2/patch-1
Fix CRD deletion instruction in Helm Operator migration docs
2021-03-25 18:41:35 +01:00
Leon Kiefer
974f01cb46 fix crd deletion command in v2 migration docs
Signed-off-by: Leon Kiefer <leon.k97@gmx.de>
2021-03-25 18:27:33 +01:00
Stefan Prodan
16fa9b2753 Merge pull request #1150 from fluxcd/dev-guide-update
Update dev guide to controller-runtime v0.8
2021-03-24 15:15:20 +02:00
Stefan Prodan
9deab1c415 Update dev guide to controller-runtime v0.8
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-24 14:59:17 +02:00
Hidde Beydals
7c01eeb115 Merge pull request #1141 from fluxcd/cmd-docs-frontmatter
Add frontmatter to command documentation
2021-03-24 13:53:06 +01:00
Hidde Beydals
998f0c7d53 Add frontmatter to command documentation
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-24 13:31:14 +01:00
Hidde Beydals
bd41406aaa Merge pull request #1134 from kingdonb/fixup-azure-doc 2021-03-22 10:23:15 +01:00
Kingdon Barrett
f17801753d Fixup a broken reference and a typo in Azure doc
Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-03-21 13:53:27 -04:00
Hidde Beydals
92891fd340 Merge pull request #1128 from jestallin/patch-1 2021-03-20 21:22:04 +01:00
Jim Stallings
aa122455f7 Remove branch switch for image update cmd in guide
Signed-off-by: James Stallings <jstallings@constantcontact.com>
2021-03-20 15:35:31 -04:00
Stefan Prodan
880e70c19c Merge pull request #1122 from fluxcd/aws-sops
Add AWS IAM role binding example to SOPS guide
2021-03-19 12:19:42 +02:00
Stefan Prodan
968f249562 Move GOTK diagram to docs/files
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-19 12:02:09 +02:00
Stefan Prodan
bb9f476be2 Fix typo in image automation guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-19 12:01:33 +02:00
Stefan Prodan
276f43fdeb Add AWS IAM role binding example to SOPS guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-19 12:01:01 +02:00
Stefan Prodan
150d1c2a5a Merge pull request #1064 from stealthybox/azure
Document Azure + Flux Installs
2021-03-19 10:47:37 +02:00
leigh capili
1bf3814701 Cleanup note sections
Signed-off-by: leigh capili <leigh@null.net>
2021-03-18 12:29:08 -06:00
leigh capili
166181c745 Add Azure Use-Case doc to new section
Signed-off-by: leigh capili <leigh@null.net>
2021-03-18 12:29:07 -06:00
leigh capili
bfff977d41 Improve Azure DevOps install notes
Signed-off-by: leigh capili <leigh@null.net>
2021-03-18 12:29:07 -06:00
leigh capili
787d755261 Document Flux + SOPS + Azure Key Vault (#851)
Signed-off-by: leigh capili <leigh@null.net>
2021-03-18 12:29:04 -06:00
Hidde Beydals
acf7173959 Merge pull request #1119 from anovateam/azure-acr-secret-reconcile-script 2021-03-18 18:35:21 +01:00
Marco Amador
e6132e36ba fix: revert azure patch and fix the order parameters in the function
Signed-off-by: Marco Amador <amador.marco@gmail.com>
2021-03-18 16:59:17 +00:00
Marco Amador
585b97c462 fix: parameter order
Signed-off-by: Marco Amador <amador.marco@gmail.com>
2021-03-18 15:01:58 +00:00
416 changed files with 8035 additions and 16034 deletions

View File

@@ -1,46 +0,0 @@
---
name: Bug report
about: Create a report to help us improve Flux v2
title: ''
assignees: ''
---
<!--
Find out more about your support options and getting help at
https://fluxcd.io/support/
-->
### Describe the bug
A clear and concise description of what the bug is.
### To Reproduce
Steps to reproduce the behaviour:
1. Provide Flux install instructions
2. Provide a GitHub repository with Kubernetes manifests
### Expected behavior
A clear and concise description of what you expected to happen.
### Additional context
- Kubernetes version:
- Git provider:
- Container registry provider:
Below please provide the output of the following commands:
```cli
flux --version
flux check
kubectl -n <namespace> get all
kubectl -n <namespace> logs deploy/source-controller
kubectl -n <namespace> logs deploy/kustomize-controller
```

85
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@@ -0,0 +1,85 @@
---
name: Bug report
description: Create a report to help us improve Flux
body:
- type: markdown
attributes:
value: |
## Support
Find out more about your support options and getting help at: https://fluxcd.io/support/
- type: textarea
validations:
required: true
attributes:
label: Describe the bug
description: A clear description of what the bug is.
- type: textarea
validations:
required: true
attributes:
label: Steps to reproduce
description: |
Steps to reproduce the problem.
placeholder: |
For example:
1. Install Flux with the additional image automation controllers
2. Run command '...'
3. See error
- type: textarea
validations:
required: true
attributes:
label: Expected behavior
description: A brief description of what you expected to happen.
- type: textarea
attributes:
label: Screenshots and recordings
description: |
If applicable, add screenshots to help explain your problem. You can also record an asciinema session: https://asciinema.org/
- type: input
validations:
required: true
attributes:
label: OS / Distro
description: The OS / distro you are executing `flux` on. If not applicable, write `N/A`.
placeholder: e.g. Windows 10, Ubuntu 20.04, Arch Linux, macOS 10.15...
- type: input
validations:
required: true
attributes:
label: Flux version
description: Run `flux --version` to check. If not applicable, write `N/A`.
placeholder: e.g. 0.16.1
- type: textarea
validations:
required: true
attributes:
label: Flux check
description: Run `flux check` to check. If not applicable, write `N/A`.
placeholder: |
For example:
► checking prerequisites
✔ kubectl 1.21.0 >=1.18.0-0
✔ Kubernetes 1.21.1 >=1.16.0-0
► checking controllers
✔ all checks passed
- type: input
attributes:
label: Git provider
description: If applicable, add the Git provider you are having problems with, e.g. GitHub (Enterprise), GitLab, etc.
- type: input
attributes:
label: Container Registry provider
description: If applicable, add the Container Registry provider you are having problems with, e.g. DockerHub, GitHub Packages, Quay.io, etc.
- type: textarea
attributes:
label: Additional context
description: Add any other context about the problem here. This can be logs (e.g. output from `flux logs`), environment specific caveats, etc.
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/fluxcd/.github/blob/main/CODE_OF_CONDUCT.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true

42
.github/runners/README.md vendored Normal file
View File

@@ -0,0 +1,42 @@
# Flux GitHub runners
How to provision GitHub Actions self-hosted runners for Flux conformance testing.
## ARM64 Instance specs
In order to add a new runner to the GitHub Actions pool,
first create an instance on Oracle Cloud with the following configuration:
- OS: Canonical Ubuntu 20.04
- Shape: VM.Standard.A1.Flex
- OCPU Count: 2
- Memory (GB): 12
- Network Bandwidth (Gbps): 2
- Local Disk: Block Storage Only
Note that the instance image source must be **Canonical Ubuntu** instead of the default Oracle Linux.
## ARM64 Instance setup
- SSH into a newly created instance
```shell
ssh ubuntu@<instance-public-IP>
```
- Create the action runner dir
```shell
mkdir -p actions-runner && cd actions-runner
```
- Download the provisioning script
```shell
curl -sL https://raw.githubusercontent.com/fluxcd/flux2/main/.github/runners/arm64.sh > arm64.sh \
&& chmod +x ./arm64.sh
```
- Retrieve the GitHub runner token from the repository [settings page](https://github.com/fluxcd/flux2/settings/actions/runners/new?arch=arm64&os=linux)
- Run the provisioning script passing the token as the first argument
```shell
sudo ./arm64.sh <TOKEN>
```
- Reboot the instance
```shell
sudo reboot
```
- Navigate to the GitHub repository [runners page](https://github.com/fluxcd/flux2/settings/actions/runners) and check the runner status

73
.github/runners/arm64.sh vendored Executable file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# Copyright 2021 The Flux authors. All rights reserved.
#
# 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.
# This script is meant to be run locally and in CI to validate the Kubernetes
# manifests (including Flux custom resources) before changes are merged into
# the branch synced by Flux in-cluster.
set -eu
REPOSITORY_TOKEN=$1
REPOSITORY_URL=${2:-https://github.com/fluxcd/flux2}
KIND_VERSION=0.11.1
KUBECTL_VERSION=1.21.2
KUSTOMIZE_VERSION=4.1.3
GITHUB_RUNNER_VERSION=2.278.0
PACKAGES="apt-transport-https ca-certificates software-properties-common build-essential libssl-dev gnupg lsb-release jq"
# install prerequisites
apt-get update \
&& apt-get install -y -q ${PACKAGES} \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# install docker
curl -fsSL https://get.docker.com -o get-docker.sh \
&& chmod +x get-docker.sh
./get-docker.sh
systemctl enable docker.service
systemctl enable containerd.service
usermod -aG docker ubuntu
# install kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-linux-arm64
install -o root -g root -m 0755 kind /usr/local/bin/kind
# install kubectl
curl -LO "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/arm64/kubectl"
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
# install kustomize
curl -Lo ./kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_linux_arm64.tar.gz \
&& tar -zxvf kustomize.tar.gz \
&& rm kustomize.tar.gz
install -o root -g root -m 0755 kustomize /usr/local/bin/kustomize
# download runner
curl -o actions-runner-linux-arm64.tar.gz -L https://github.com/actions/runner/releases/download/v${GITHUB_RUNNER_VERSION}/actions-runner-linux-arm64-${GITHUB_RUNNER_VERSION}.tar.gz \
&& tar xzf actions-runner-linux-arm64.tar.gz \
&& rm actions-runner-linux-arm64.tar.gz
# install runner dependencies
./bin/installdependencies.sh
# register runner with GitHub
sudo -u ubuntu ./config.sh --unattended --url ${REPOSITORY_URL} --token ${REPOSITORY_TOKEN}
# start runner
./svc.sh install
./svc.sh start

View File

@@ -26,6 +26,9 @@ jobs:
go-version: 1.16.x
- name: Setup Kubernetes
uses: engineerd/setup-kind@v0.5.0
with:
version: v0.11.1
image: kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6
- name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@main
- name: Build
@@ -47,7 +50,8 @@ jobs:
--owner=fluxcd-testing \
--repository=${{ steps.vars.outputs.test_repo_name }} \
--branch=main \
--path=test-cluster
--path=test-cluster \
--team=team-z
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: bootstrap no-op
@@ -56,9 +60,17 @@ jobs:
--owner=fluxcd-testing \
--repository=${{ steps.vars.outputs.test_repo_name }} \
--branch=main \
--path=test-cluster
--path=test-cluster \
--team=team-z
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: libgit2
run: |
/tmp/flux create source git test-libgit2 \
--url=ssh://git@github.com/fluxcd-testing/${{ steps.vars.outputs.test_repo_name }} \
--git-implementation=libgit2 \
--secret-ref=flux-system \
--branch=main
- name: uninstall
run: |
/tmp/flux uninstall -s --keep-namespace
@@ -69,7 +81,8 @@ jobs:
--owner=fluxcd-testing \
--repository=${{ steps.vars.outputs.test_repo_name }} \
--branch=main \
--path=test-cluster
--path=test-cluster \
--team=team-z
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: delete repository

View File

@@ -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

108
.github/workflows/e2e-arm64.yaml vendored Normal file
View File

@@ -0,0 +1,108 @@
name: e2e-arm64
on:
workflow_dispatch:
push:
branches: [ main, update-components ]
jobs:
ampere:
# Runner info
# Owner: Stefan Prodan
# Docs: https://github.com/fluxcd/flux2/tree/main/.github/runners
runs-on: [self-hosted, Linux, ARM64]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.16.x
- name: Prepare
id: prep
run: |
echo ::set-output name=CLUSTER::arm64-${GITHUB_SHA:0:7}-$(date +%s)
echo ::set-output name=CONTEXT::kind-arm64-${GITHUB_SHA:0:7}-$(date +%s)
- name: Run unit tests
run: make test
- name: Check if working tree is dirty
run: |
if [[ $(git diff --stat) != '' ]]; then
git diff
echo 'run make test and commit changes'
exit 1
fi
- name: Build
run: |
go build -o /tmp/flux ./cmd/flux
- name: Setup Kubernetes Kind
run: |
kind create cluster --name ${{ steps.prep.outputs.CLUSTER }}
- name: flux check --pre
run: |
/tmp/flux check --pre \
--context ${{ steps.prep.outputs.CONTEXT }}
- name: flux install
run: |
/tmp/flux install \
--components-extra=image-reflector-controller,image-automation-controller \
--context ${{ steps.prep.outputs.CONTEXT }}
- name: flux create source git
run: |
/tmp/flux create source git podinfo-gogit \
--git-implementation=go-git \
--url https://github.com/stefanprodan/podinfo \
--tag-semver=">1.0.0" \
--context ${{ steps.prep.outputs.CONTEXT }}
/tmp/flux create source git podinfo-libgit2 \
--git-implementation=libgit2 \
--url https://github.com/stefanprodan/podinfo \
--branch="master" \
--context ${{ steps.prep.outputs.CONTEXT }}
- name: flux create kustomization
run: |
/tmp/flux create kustomization podinfo \
--source=podinfo-gogit \
--path="./deploy/overlays/dev" \
--prune=true \
--interval=5m \
--validation=client \
--health-check="Deployment/frontend.dev" \
--health-check="Deployment/backend.dev" \
--health-check-timeout=3m \
--context ${{ steps.prep.outputs.CONTEXT }}
- name: flux create tenant
run: |
/tmp/flux create tenant dev-team \
--with-namespace=apps \
--context ${{ steps.prep.outputs.CONTEXT }}
- name: flux create helmrelease
run: |
/tmp/flux -n apps create source helm podinfo \
--url https://stefanprodan.github.io/podinfo \
--context ${{ steps.prep.outputs.CONTEXT }}
/tmp/flux -n apps create hr podinfo-helm \
--source=HelmRepository/podinfo \
--chart=podinfo \
--chart-version="6.0.x" \
--service-account=dev-team \
--context ${{ steps.prep.outputs.CONTEXT }}
- name: flux get all
run: |
/tmp/flux get all --all-namespaces \
--context ${{ steps.prep.outputs.CONTEXT }}
- name: flux uninstall
run: |
/tmp/flux uninstall -s \
--context ${{ steps.prep.outputs.CONTEXT }}
- name: Debug failure
if: failure()
run: |
kubectl --context ${{ steps.prep.outputs.CONTEXT }} -n flux-system get all
kubectl --context ${{ steps.prep.outputs.CONTEXT }} -n flux-system describe pods
/tmp/flux logs --all-namespaces
- name: Cleanup
if: always()
run: |
kind delete cluster --name ${{ steps.prep.outputs.CLUSTER }}

View File

@@ -26,8 +26,8 @@ jobs:
- name: Setup Kubernetes
uses: engineerd/setup-kind@v0.5.0
with:
version: "v0.10.0"
image: kindest/node:v1.20.2@sha256:8f7ea6e7642c0da54f04a7ee10431549c0257315b3a634f6ef2fecaaedb19bab
version: v0.11.1
image: kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6
config: .github/kind/config.yaml # disable KIND-net
- name: Setup Calico for network policy
run: |
@@ -91,6 +91,12 @@ jobs:
--health-check="Deployment/frontend.dev" \
--health-check="Deployment/backend.dev" \
--health-check-timeout=3m
- name: flux trace
run: |
/tmp/flux trace frontend \
--kind=deployment \
--api-version=apps/v1 \
--namespace=dev
- name: flux reconcile kustomization --with-source
run: |
/tmp/flux reconcile kustomization podinfo --with-source
@@ -121,7 +127,7 @@ jobs:
run: |
/tmp/flux create hr podinfo-helm \
--target-namespace=default \
--source=HelmRepository/podinfo \
--source=HelmRepository/podinfo.flux-system \
--chart=podinfo \
--chart-version=">4.0.0 <5.0.0"
- name: flux create helmrelease --source=GitRepository/podinfo
@@ -188,11 +194,12 @@ jobs:
run: |
/tmp/flux create source git flux-system \
--url=https://github.com/fluxcd/flux2-kustomize-helm-example \
--branch=main
--branch=main \
--recurse-submodules
/tmp/flux create kustomization flux-system \
--source=flux-system \
--path=./clusters/staging
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=2m
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=5m
- name: flux check
run: |
/tmp/flux check
@@ -204,6 +211,7 @@ jobs:
run: |
kubectl version --client --short
kubectl -n flux-system get all
kubectl -n flux-system describe pods
kubectl -n flux-system get kustomizations -oyaml
kubectl -n flux-system logs deploy/source-controller
kubectl -n flux-system logs deploy/kustomize-controller

View File

@@ -16,6 +16,26 @@ jobs:
uses: actions/setup-go@v2
with:
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
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
@@ -33,6 +53,17 @@ jobs:
make cmd/flux/manifests
./manifests/scripts/bundle.sh "" ./output manifests.tar.gz
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
uses: goreleaser/goreleaser-action@v1
with:

View File

@@ -23,20 +23,32 @@ jobs:
PR_BODY=""
bump_version() {
local RELEASE_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
local CURRENT_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
local LATEST_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
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
# bump kustomize
sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${RELEASE_VERSION}\2/g" "manifests/bases/$1/kustomization.yaml"
local changed=false
if [[ ! -z $(grep "github.com/fluxcd/$1/api" go.mod | awk '{print $2}') ]]; then
# bump go mod
go mod edit -require="github.com/fluxcd/$1/api@${RELEASE_VERSION}"
fi
if [[ "${CTRL_VERSION}" != "${LATEST_VERSION}" ]]; then
sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${LATEST_VERSION}\2/g" "manifests/bases/$1/kustomization.yaml"
changed=true
fi
# NB: special URL encoded formatting required for newlines
PR_BODY="$PR_BODY- $1 to ${RELEASE_VERSION}%0A https://github.com/fluxcd/$1/blob/${RELEASE_VERSION}/CHANGELOG.md%0A"
if [[ "${CRD_VERSION}" != "${LATEST_VERSION}" ]]; then
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
}
@@ -49,9 +61,6 @@ jobs:
bump_version image-reflector-controller
bump_version image-automation-controller
# add missing and remove unused modules
go mod tidy
# diff change
git diff
@@ -63,21 +72,21 @@ jobs:
id: cpr
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.BOT_GITHUB_TOKEN }}
commit-message: |
Update toolkit components
token: ${{ secrets.BOT_GITHUB_TOKEN }}
commit-message: |
Update toolkit components
${{ steps.update.outputs.pr_body }}
committer: GitHub <noreply@github.com>
author: fluxcdbot <fluxcdbot@users.noreply.github.com>
signoff: true
branch: update-components
title: Update toolkit components
body: |
${{ steps.update.outputs.pr_body }}
labels: |
area/build
reviewers: ${{ secrets.ASSIGNEES }}
${{ steps.update.outputs.pr_body }}
committer: GitHub <noreply@github.com>
author: fluxcdbot <fluxcdbot@users.noreply.github.com>
signoff: true
branch: update-components
title: Update toolkit components
body: |
${{ steps.update.outputs.pr_body }}
labels: |
area/build
reviewers: ${{ secrets.ASSIGNEES }}
- name: Check output
run: |

6
.gitignore vendored
View File

@@ -11,8 +11,14 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Release
dist/
# Dependency directories (remove the comment below to include it)
# vendor/
bin/
output/
cmd/flux/manifests/
# Docs
site/

View File

@@ -47,7 +47,7 @@ brews:
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
folder: Formula
homepage: "https://toolkit.fluxcd.io/"
homepage: "https://fluxcd.io/"
description: "Flux CLI"
dependencies:
- name: kubectl
@@ -72,5 +72,67 @@ publishers:
.github/aur/flux-go/publish.sh {{ .Version }}
release:
extra_files:
- glob: ./output/crd-schemas.tar.gz
- glob: ./output/manifests.tar.gz
- 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'

View File

@@ -59,7 +59,7 @@ This project is composed of:
### Understanding the code
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
for source changes.

23
Dockerfile Normal file
View 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" ]

View File

@@ -14,7 +14,7 @@ fmt:
vet:
go vet ./...
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet docs
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet
go test ./... -coverprofile cover.out
$(EMBEDDED_MANIFESTS_TARGET): $(call rwildcard,manifests/,*.yaml *.json)
@@ -26,10 +26,5 @@ build: $(EMBEDDED_MANIFESTS_TARGET)
install:
go install cmd/flux
.PHONY: docs
docs:
rm -rf docs/cmd/*
mkdir -p ./docs/cmd && go run ./cmd/flux/ docgen
install-dev:
CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/flux

View File

@@ -1,5 +1,6 @@
# Flux version 2
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4782/badge)](https://bestpractices.coreinfrastructure.org/projects/4782)
[![e2e](https://github.com/fluxcd/flux2/workflows/e2e/badge.svg)](https://github.com/fluxcd/flux2/actions)
[![report](https://goreportcard.com/badge/github.com/fluxcd/flux2)](https://goreportcard.com/report/github.com/fluxcd/flux2)
[![license](https://img.shields.io/github/license/fluxcd/flux2.svg)](https://github.com/fluxcd/flux2/blob/main/LICENSE)
@@ -21,16 +22,22 @@ Delivery on top of Kubernetes.
## Flux installation
With Homebrew:
With [Homebrew](https://brew.sh) for macOS and Linux:
```sh
brew install fluxcd/tap/flux
```
With Bash:
With [GoFish](https://gofi.sh) for Windows, macOS and Linux:
```sh
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
gofish install flux
```
With Bash for macOS and Linux:
```sh
curl -s https://fluxcd.io/install.sh | sudo bash
# enable completions in ~/.bash_profile
. <(flux completion bash)
@@ -45,8 +52,13 @@ Arch Linux (AUR) packages:
- [flux-scm](https://aur.archlinux.org/packages/flux-scm): build the latest
(unstable) version from source code from our git `main` branch
Binaries for macOS, Windows and Linux AMD64/ARM are available to download on the
[release page](https://github.com/fluxcd/flux2/releases).
Binaries for macOS AMD64/ARM64, Linux AMD64/ARM/ARM64 and Windows are available to
download on the [release page](https://github.com/fluxcd/flux2/releases).
A multi-arch 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:
@@ -57,13 +69,13 @@ flux check --pre
## Get started
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:
- [Get started with Flux](https://toolkit.fluxcd.io/get-started/)
- [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/)
- [Automate image updates to Git](https://toolkit.fluxcd.io/guides/image-update/)
- [Manage Kubernetes secrets with Mozilla SOPS](https://toolkit.fluxcd.io/guides/mozilla-sops/)
- [Get started with Flux](https://fluxcd.io/docs/get-started/)
- [Manage Helm Releases](https://fluxcd.io/docs/guides/helmreleases/)
- [Automate image updates to Git](https://fluxcd.io/docs/guides/image-update/)
- [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/)**.
@@ -74,31 +86,31 @@ runtime for Flux v2. The APIs comprise Kubernetes custom resources,
which can be created and updated by a cluster user, or by other
automation tooling.
![overview](docs/diagrams/gitops-toolkit.png)
![overview](docs/_files/gitops-toolkit.png)
You can use the toolkit to extend Flux, or to build your own systems
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
- [Source Controller](https://toolkit.fluxcd.io/components/source/controller/)
- [GitRepository CRD](https://toolkit.fluxcd.io/components/source/gitrepositories/)
- [HelmRepository CRD](https://toolkit.fluxcd.io/components/source/helmrepositories/)
- [HelmChart CRD](https://toolkit.fluxcd.io/components/source/helmcharts/)
- [Bucket CRD](https://toolkit.fluxcd.io/components/source/buckets/)
- [Kustomize Controller](https://toolkit.fluxcd.io/components/kustomize/controller/)
- [Kustomization CRD](https://toolkit.fluxcd.io/components/kustomize/kustomization/)
- [Helm Controller](https://toolkit.fluxcd.io/components/helm/controller/)
- [HelmRelease CRD](https://toolkit.fluxcd.io/components/helm/helmreleases/)
- [Notification Controller](https://toolkit.fluxcd.io/components/notification/controller/)
- [Provider CRD](https://toolkit.fluxcd.io/components/notification/provider/)
- [Alert CRD](https://toolkit.fluxcd.io/components/notification/alert/)
- [Receiver CRD](https://toolkit.fluxcd.io/components/notification/receiver/)
- [Image Automation Controllers](https://toolkit.fluxcd.io/components/image/controller/)
- [ImageRepository CRD](https://toolkit.fluxcd.io/components/image/imagerepositories/)
- [ImagePolicy CRD](https://toolkit.fluxcd.io/components/image/imagepolicies/)
- [ImageUpdateAutomation CRD](https://toolkit.fluxcd.io/components/image/imageupdateautomations/)
- [Source Controller](https://fluxcd.io/docs/components/source/)
- [GitRepository CRD](https://fluxcd.io/docs/components/source/gitrepositories/)
- [HelmRepository CRD](https://fluxcd.io/docs/components/source/helmrepositories/)
- [HelmChart CRD](https://fluxcd.io/docs/components/source/helmcharts/)
- [Bucket CRD](https://fluxcd.io/docs/components/source/buckets/)
- [Kustomize Controller](https://fluxcd.io/docs/components/kustomize/)
- [Kustomization CRD](https://fluxcd.io/docs/components/kustomize/kustomization/)
- [Helm Controller](https://fluxcd.io/docs/components/helm/)
- [HelmRelease CRD](https://fluxcd.io/docs/components/helm/helmreleases/)
- [Notification Controller](https://fluxcd.io/docs/components/notification/)
- [Provider CRD](https://fluxcd.io/docs/components/notification/provider/)
- [Alert CRD](https://fluxcd.io/docs/components/notification/alert/)
- [Receiver CRD](https://fluxcd.io/docs/components/notification/receiver/)
- [Image Automation Controllers](https://fluxcd.io/docs/components/image/)
- [ImageRepository CRD](https://fluxcd.io/docs/components/image/imagerepositories/)
- [ImagePolicy CRD](https://fluxcd.io/docs/components/image/imagepolicies/)
- [ImageUpdateAutomation CRD](https://fluxcd.io/docs/components/image/imageupdateautomations/)
## Community
@@ -106,7 +118,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.
- 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?
- 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/)

View File

@@ -10,11 +10,21 @@ Usage:
run: flux -v
```
Note that this action can only be used on GitHub **Linux AMD64** runners.
The latest stable version of the `flux` binary is downloaded from
GitHub [releases](https://github.com/fluxcd/flux2/releases)
and placed at `/usr/local/bin/flux`.
Note that this action can only be used on GitHub **Linux** runners.
You can change the arch (defaults to `amd64`) with:
```yaml
steps:
- name: Setup Flux CLI
uses: fluxcd/flux2/action@main
with:
arch: arm64 # can be amd64, arm64 or arm
```
You can download a specific version with:
```yaml

View File

@@ -8,19 +8,24 @@ inputs:
version:
description: "Flux version e.g. 0.8.0 (defaults to latest stable release)"
required: false
arch:
description: "arch can be amd64, arm64 or arm"
required: true
default: "amd64"
runs:
using: composite
steps:
- name: "Download flux binary to tmp"
shell: bash
run: |
ARCH=${{ inputs.arch }}
VERSION=${{ inputs.version }}
if [ -z $VERSION ]; then
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
fi
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_amd64.tar.gz"
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_${ARCH}.tar.gz"
curl -sL ${BIN_URL} -o /tmp/flux.tar.gz
mkdir -p /tmp/flux
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz

56
cmd/flux/alert.go Normal file
View File

@@ -0,0 +1,56 @@
/*
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
}
func (a alertAdapter) deepCopyClientObject() client.Object {
return a.Alert.DeepCopy()
}
// 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)
}

View File

@@ -0,0 +1,56 @@
/*
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
}
func (a alertProviderAdapter) deepCopyClientObject() client.Object {
return a.Provider.DeepCopy()
}
// 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)
}

View File

@@ -17,26 +17,15 @@ limitations under the License.
package main
import (
"context"
"crypto/elliptic"
"fmt"
"path/filepath"
"time"
"os"
"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/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
kus "github.com/fluxcd/flux2/pkg/manifestgen/kustomization"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
"github.com/fluxcd/flux2/pkg/status"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
)
var bootstrapCmd = &cobra.Command{
@@ -46,21 +35,39 @@ var bootstrapCmd = &cobra.Command{
}
type bootstrapFlags struct {
version string
version string
arch flags.Arch
logLevel flags.LogLevel
branch string
recurseSubmodules bool
manifestsPath string
defaultComponents []string
extraComponents []string
registry string
imagePullSecret string
branch string
requiredComponents []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
networkPolicy bool
manifestsPath string
arch flags.Arch
logLevel flags.LogLevel
requiredComponents []string
tokenAuth bool
clusterDomain string
tolerationKeys []string
authorName string
authorEmail string
commitMessageAppendix string
}
const (
@@ -72,17 +79,23 @@ var bootstrapArgs = NewBootstrapFlags()
func init() {
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "",
"toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
"container registry where the toolkit images are published")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
"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,
"default branch (for GitHub this must match the default branch setting for the organization)")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch, "Git branch")
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,
"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,
@@ -90,12 +103,27 @@ func init() {
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false,
"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().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
bootstrapCmd.PersistentFlags().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().MarkHidden("manifests")
rootCmd.AddCommand(bootstrapCmd)
}
@@ -103,6 +131,9 @@ func NewBootstrapFlags() bootstrapFlags {
return bootstrapFlags{
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
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...)
}
func buildEmbeddedManifestBase() (string, error) {
if !isEmbeddedVersion(bootstrapArgs.version) {
return "", nil
}
tmpBaseDir, err := os.MkdirTemp("", "flux-manifests-")
if err != nil {
return "", err
}
if err := writeEmbeddedManifests(tmpBaseDir); err != nil {
return "", err
}
return tmpBaseDir, nil
}
func bootstrapValidate() error {
components := bootstrapComponents()
for _, component := range bootstrapArgs.requiredComponents {
@@ -125,179 +170,10 @@ func bootstrapValidate() error {
return nil
}
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
if ver, err := getVersion(bootstrapArgs.version); err != nil {
return "", err
} else {
bootstrapArgs.version = ver
func mapTeamSlice(s []string, defaultPermission string) map[string]string {
m := make(map[string]string, len(s))
for _, v := range s {
m[v] = defaultPermission
}
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
return m
}

263
cmd/flux/bootstrap_git.go Normal file
View File

@@ -0,0 +1,263 @@
/*
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"
"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
silent bool
}
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")
bootstrapGitCmd.Flags().BoolVarP(&gitArgs.silent, "silent", "s", false, "assumes the deploy key is already setup, skips confirmation")
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 := os.MkdirTemp("", "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))
if !gitArgs.silent {
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
}

View File

@@ -19,21 +19,20 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"time"
"github.com/fluxcd/pkg/git"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"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/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
)
var bootstrapGitHubCmd = &cobra.Command{
@@ -47,44 +46,46 @@ the bootstrap command will perform an upgrade if needed.`,
Example: ` # Create a GitHub personal access token and export it as an env var
export GITHUB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitHub organization
flux bootstrap github --owner=<organization> --repository=<repo name>
# Run bootstrap for a private repository owned by a GitHub organization
flux bootstrap github --owner=<organization> --repository=<repository name>
# Run bootstrap for a private repo and assign organization teams to it
flux bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
# Run bootstrap for a private repository and assign organization teams to it
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug> --team=<team2 slug>
# 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
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
flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> --ssh-hostname=<domain>
# Run bootstrap for a private repository hosted on GitHub Enterprise using SSH auth
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
flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> --token-auth
# Run bootstrap for a private repository hosted on GitHub Enterprise using HTTPS 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
flux bootstrap github --owner=<organization> --repository=<repo name> --branch=main
`,
# Run bootstrap for an existing repository with a branch named main
flux bootstrap github --owner=<organization> --repository=<repository name> --branch=main`,
RunE: bootstrapGitHubCmdRun,
}
type githubFlags struct {
owner string
repository string
interval time.Duration
personal bool
private bool
hostname string
path flags.SafeRelativePath
teams []string
sshHostname string
owner string
repository string
interval time.Duration
personal bool
private bool
hostname string
path flags.SafeRelativePath
teams []string
readWriteKey bool
reconcile bool
}
const (
ghDefaultPermission = "maintain"
ghDefaultDomain = "github.com"
ghTokenEnvVar = "GITHUB_TOKEN"
)
var githubArgs githubFlags
@@ -92,21 +93,22 @@ var githubArgs githubFlags
func init() {
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository name")
bootstrapGitHubCmd.Flags().StringArrayVar(&githubArgs.teams, "team", []string{}, "GitHub team to be given maintainer access")
bootstrapGitHubCmd.Flags().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.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().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", ghDefaultDomain, "GitHub hostname")
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)
}
func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
ghToken := os.Getenv(git.GitHubTokenName)
ghToken := os.Getenv(ghTokenEnvVar)
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 {
@@ -121,205 +123,129 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
return err
}
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(
ctx,
kubeClient,
rootArgs.namespace,
filepath.ToSlash(githubArgs.path.String()),
)
if bootstrapPathDiffers {
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
// 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)
repository, err := git.NewRepository(
githubArgs.repository,
githubArgs.owner,
githubArgs.hostname,
ghToken,
"flux",
githubArgs.owner+"@users.noreply.github.com",
)
// Build GitHub provider
providerCfg := provider.Config{
Provider: provider.GitProviderGitHub,
Hostname: githubArgs.hostname,
Token: ghToken,
}
providerClient, err := provider.BuildGitProvider(providerCfg)
if err != nil {
return err
}
if githubArgs.sshHostname != "" {
repository.SSHHost = githubArgs.sshHostname
}
provider := &git.GithubProvider{
IsPrivate: githubArgs.private,
IsPersonal: githubArgs.personal,
}
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
// Lazy go-git repository
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
if err != nil {
return err
return fmt.Errorf("failed to create temporary working dir: %w", err)
}
defer os.RemoveAll(tmpDir)
gitClient := gogit.New(tmpDir, &http.BasicAuth{
Username: githubArgs.owner,
Password: ghToken,
})
// create GitHub repository if doesn't exists
logger.Actionf("connecting to %s", githubArgs.hostname)
changed, err := provider.CreateRepository(ctx, repository)
if err != nil {
return err
// 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: githubArgs.path.ToSlash(),
ClusterDomain: bootstrapArgs.clusterDomain,
TolerationKeys: bootstrapArgs.tolerationKeys,
}
if changed {
logger.Successf("repository created")
if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
installOptions.BaseURL = customBaseURL
}
withErrors := false
// 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()
// Source generation and secret config
secretOpts := sourcesecret.Options{
Name: rootArgs.namespace,
Namespace: rootArgs.namespace,
Name: bootstrapArgs.secretName,
Namespace: rootArgs.namespace,
TargetPath: githubArgs.path.ToSlash(),
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
}
if bootstrapArgs.tokenAuth {
// Setup HTTPS token auth
repoURL = repository.GetURL()
secretOpts.Username = "git"
secretOpts.Password = ghToken
} else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
// Setup SSH auth
u, err := url.Parse(repoURL)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
if bootstrapArgs.caFile != "" {
secretOpts.CAFilePath = bootstrapArgs.caFile
}
secretOpts.SSHHostname = u.Host
secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm
secretOpts.RSAKeyBits = 2048
}
} else {
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
secretOpts.SSHHostname = githubArgs.hostname
secret, err := sourcesecret.Generate(secretOpts)
if err != nil {
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")
}
if bootstrapArgs.sshHostname != "" {
secretOpts.SSHHostname = bootstrapArgs.sshHostname
}
}
// configure repo synchronization
logger.Actionf("generating sync manifests")
syncManifests, err := generateSyncManifests(
repoURL,
bootstrapArgs.branch,
rootArgs.namespace,
rootArgs.namespace,
filepath.ToSlash(githubArgs.path.String()),
tmpDir,
githubArgs.interval,
)
// Sync manifest config
syncOpts := sync.Options{
Interval: githubArgs.interval,
Name: rootArgs.namespace,
Namespace: rootArgs.namespace,
Branch: bootstrapArgs.branch,
Secret: bootstrapArgs.secretName,
TargetPath: githubArgs.path.ToSlash(),
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
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 {
return err
}
// commit and push manifests
if changed, err = repository.Commit(
ctx,
path.Join(githubArgs.path.String(), rootArgs.namespace),
fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version),
); err != nil {
return err
} else if changed {
if err := repository.Push(ctx); err != nil {
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
// Run
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
}

View File

@@ -19,23 +19,22 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"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/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
)
var bootstrapGitLabCmd = &cobra.Command{
@@ -49,40 +48,44 @@ the bootstrap command will perform an upgrade if needed.`,
Example: ` # Create a GitLab API token and export it as an env var
export GITLAB_TOKEN=<my-token>
# Run bootstrap for a private repo using HTTPS token authentication
flux bootstrap gitlab --owner=<group> --repository=<repo name> --token-auth
# Run bootstrap for a private repository using HTTPS token authentication
flux bootstrap gitlab --owner=<group> --repository=<repository name> --token-auth
# Run bootstrap for a private repo using SSH authentication
flux bootstrap gitlab --owner=<group> --repository=<repo name>
# Run bootstrap for a private repository using SSH authentication
flux bootstrap gitlab --owner=<group> --repository=<repository name>
# 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
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
flux bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain> --token-auth
# Run bootstrap for a private repository hosted on a GitLab server
flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
# 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,
}
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 {
owner string
repository string
interval time.Duration
personal bool
private bool
hostname string
sshHostname string
path flags.SafeRelativePath
owner string
repository string
interval time.Duration
personal bool
private bool
hostname string
path flags.SafeRelativePath
teams []string
readWriteKey bool
reconcile bool
}
var gitlabArgs gitlabFlags
@@ -90,29 +93,30 @@ var gitlabArgs gitlabFlags
func init() {
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name")
bootstrapGitLabCmd.Flags().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.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().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.sshHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", glDefaultDomain, "GitLab hostname")
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)
}
func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
glToken := os.Getenv(git.GitLabTokenName)
glToken := os.Getenv(glTokenEnvVar)
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 err != nil {
if projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository); err != nil || !projectNameIsValid {
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
}
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 {
return err
@@ -126,183 +130,138 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
return err
}
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String()))
if bootstrapPathDiffers {
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
// 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)
repository, err := git.NewRepository(
gitlabArgs.repository,
gitlabArgs.owner,
gitlabArgs.hostname,
glToken,
"flux",
gitlabArgs.owner+"@users.noreply.gitlab.com",
)
// Build GitLab provider
providerCfg := provider.Config{
Provider: provider.GitProviderGitLab,
Hostname: gitlabArgs.hostname,
Token: glToken,
}
// 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 {
return err
}
if gitlabArgs.sshHostname != "" {
repository.SSHHost = gitlabArgs.sshHostname
}
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
// Lazy go-git repository
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
if err != nil {
return err
return fmt.Errorf("failed to create temporary working dir: %w", err)
}
defer os.RemoveAll(tmpDir)
gitClient := gogit.New(tmpDir, &http.BasicAuth{
Username: gitlabArgs.owner,
Password: glToken,
})
provider := &git.GitLabProvider{
IsPrivate: gitlabArgs.private,
IsPersonal: gitlabArgs.personal,
// 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: gitlabArgs.path.ToSlash(),
ClusterDomain: bootstrapArgs.clusterDomain,
TolerationKeys: bootstrapArgs.tolerationKeys,
}
if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
installOptions.BaseURL = customBaseURL
}
// create GitLab project if doesn't exists
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()
// Source generation and secret config
secretOpts := sourcesecret.Options{
Name: rootArgs.namespace,
Namespace: rootArgs.namespace,
Name: bootstrapArgs.secretName,
Namespace: rootArgs.namespace,
TargetPath: gitlabArgs.path.String(),
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
}
if bootstrapArgs.tokenAuth {
// Setup HTTPS token auth
repoURL = repository.GetURL()
secretOpts.Username = "git"
secretOpts.Password = glToken
} else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
// Setup SSH auth
u, err := url.Parse(repoURL)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
if bootstrapArgs.caFile != "" {
secretOpts.CAFilePath = bootstrapArgs.caFile
}
secretOpts.SSHHostname = u.Host
secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm
secretOpts.RSAKeyBits = 2048
}
} else {
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
secretOpts.SSHHostname = gitlabArgs.hostname
secret, err := sourcesecret.Generate(secretOpts)
if err != nil {
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.privateKeyFile != "" {
secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
}
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
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")
}
if bootstrapArgs.sshHostname != "" {
secretOpts.SSHHostname = bootstrapArgs.sshHostname
}
}
// configure repo synchronization
logger.Actionf("generating sync manifests")
syncManifests, err := generateSyncManifests(
repoURL,
bootstrapArgs.branch,
rootArgs.namespace,
rootArgs.namespace,
filepath.ToSlash(gitlabArgs.path.String()),
tmpDir,
gitlabArgs.interval,
)
// Sync manifest config
syncOpts := sync.Options{
Interval: gitlabArgs.interval,
Name: rootArgs.namespace,
Namespace: rootArgs.namespace,
Branch: bootstrapArgs.branch,
Secret: bootstrapArgs.secretName,
TargetPath: gitlabArgs.path.ToSlash(),
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
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 {
return err
}
// commit and push manifests
if changed, err = repository.Commit(
ctx,
path.Join(gitlabArgs.path.String(), rootArgs.namespace),
fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version),
); err != nil {
return err
} else if changed {
if err := repository.Push(ctx); err != nil {
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
// Run
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
}

View File

@@ -46,8 +46,7 @@ the local environment is configured correctly and if the installed components ar
flux check --pre
# Run installation checks
flux check
`,
flux check`,
RunE: runCheckCmd,
}

View File

@@ -32,8 +32,7 @@ var completionBashCmd = &cobra.Command{
To configure your bash shell to load completions for each session add to your bashrc
# ~/.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) {
rootCmd.GenBashCompletion(os.Stdout)
},

View File

@@ -29,8 +29,7 @@ var completionFishCmd = &cobra.Command{
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) {
rootCmd.GenFishCompletion(os.Stdout, true)
},

View File

@@ -39,8 +39,7 @@ flux completion >> flux-completion.ps1
Linux:
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) {
rootCmd.GenPowerShellCompletion(os.Stdout)
},

View File

@@ -40,8 +40,7 @@ echo "${fpath// /\n}" | grep -i completion
flux completion zsh > _flux
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) {
rootCmd.GenZshCompletion(os.Stdout)
},

View File

@@ -43,8 +43,7 @@ var createAlertCmd = &cobra.Command{
--event-severity info \
--event-source Kustomization/flux-system \
--provider-ref slack \
flux-system
`,
flux-system`,
RunE: createAlertCmdRun,
}
@@ -59,7 +58,7 @@ var alertArgs alertFlags
func init() {
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().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)
}
@@ -75,14 +74,15 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
eventSources := []notificationv1.CrossNamespaceObjectReference{}
for _, eventSource := range alertArgs.eventSources {
kind, name := utils.ParseObjectKindName(eventSource)
kind, name, namespace := utils.ParseObjectKindNameNamespace(eventSource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
}
eventSources = append(eventSources, notificationv1.CrossNamespaceObjectReference{
Kind: kind,
Name: name,
Kind: kind,
Name: name,
Namespace: namespace,
})
}
@@ -116,7 +116,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
}
if createArgs.export {
return exportAlert(alert)
return printExport(exportAlert(&alert))
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -49,8 +49,7 @@ var createAlertProviderCmd = &cobra.Command{
flux create alert-provider github-podinfo \
--type github \
--address https://github.com/stefanprodan/podinfo \
--secret-ref github-token
`,
--secret-ref github-token`,
RunE: createAlertProviderCmdRun,
}
@@ -113,7 +112,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
if createArgs.export {
return exportAlertProvider(provider)
return printExport(exportAlertProvider(&provider))
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -20,7 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
@@ -87,17 +87,23 @@ var createHelmReleaseCmd = &cobra.Command{
# Create a HelmRelease targeting another namespace than the resource
flux create hr podinfo \
--target-namespace=default \
--target-namespace=test \
--create-target-namespace=true \
--source=HelmRepository/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
flux create hr podinfo \
--source=HelmRepository/podinfo \
--chart=podinfo \
--values=./values.yaml \
--export > podinfo-release.yaml
`,
--export > podinfo-release.yaml`,
RunE: createHelmReleaseCmdRun,
}
@@ -108,9 +114,11 @@ type helmReleaseFlags struct {
chart string
chartVersion string
targetNamespace string
valuesFile []string
createNamespace bool
valuesFiles []string
valuesFrom flags.HelmReleaseValuesFrom
saName string
crds flags.CRDsPolicy
}
var helmReleaseArgs helmReleaseFlags
@@ -120,11 +128,13 @@ func init() {
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.source, "source", helmReleaseArgs.source.Description())
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().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().BoolVar(&helmReleaseArgs.createNamespace, "create-target-namespace", false, "create the target namespace if it does not exist")
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
createHelmReleaseCmd.Flags().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.crds, "crds", helmReleaseArgs.crds.Description())
createCmd.AddCommand(createHelmReleaseCmd)
}
@@ -160,16 +170,21 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
Duration: createArgs.interval,
},
TargetNamespace: helmReleaseArgs.targetNamespace,
Chart: helmv2.HelmChartTemplate{
Spec: helmv2.HelmChartTemplateSpec{
Chart: helmReleaseArgs.chart,
Version: helmReleaseArgs.chartVersion,
SourceRef: helmv2.CrossNamespaceObjectReference{
Kind: helmReleaseArgs.source.Kind,
Name: helmReleaseArgs.source.Name,
Kind: helmReleaseArgs.source.Kind,
Name: helmReleaseArgs.source.Name,
Namespace: helmReleaseArgs.source.Namespace,
},
},
},
Install: &helmv2.Install{
CreateNamespace: helmReleaseArgs.createNamespace,
},
Suspend: false,
},
}
@@ -178,10 +193,15 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
}
if len(helmReleaseArgs.valuesFile) > 0 {
var valuesMap map[string]interface{}
for _, v := range helmReleaseArgs.valuesFile {
data, err := ioutil.ReadFile(v)
if helmReleaseArgs.crds != "" {
helmRelease.Spec.Install.CRDs = helmv2.Create
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 := os.ReadFile(v)
if err != nil {
return fmt.Errorf("reading values from %s failed: %w", v, err)
}
@@ -196,11 +216,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("unmarshaling values from %s failed: %w", v, err)
}
if valuesMap == nil {
valuesMap = jsonMap
} else {
valuesMap = transform.MergeMaps(valuesMap, jsonMap)
}
valuesMap = transform.MergeMaps(valuesMap, jsonMap)
}
jsonRaw, err := json.Marshal(valuesMap)
@@ -219,7 +235,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}
if createArgs.export {
return exportHelmRelease(helmRelease)
return printExport(exportHelmRelease(&helmRelease))
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -17,20 +17,17 @@ limitations under the License.
package main
import (
"strings"
"github.com/spf13/cobra"
)
const createImageLong = `
The create image sub-commands work with image automation objects; that is,
const createImageLong = `The create image sub-commands work with image automation objects; that is,
object controlling updates to git based on e.g., new container images
being available.`
var createImageCmd = &cobra.Command{
Use: "image",
Short: "Create or update resources dealing with image automation",
Long: strings.TrimSpace(createImageLong),
Long: createImageLong,
}
func init() {

View File

@@ -28,7 +28,7 @@ import (
"github.com/fluxcd/pkg/apis/meta"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var createImagePolicyCmd = &cobra.Command{
@@ -50,8 +50,7 @@ the status of the object.`,
--image-ref=podinfo \
--select-numeric=asc \
--filter-regex='^main-[a-f0-9]+-(?P<ts>[0-9]+)' \
--filter-extract='$ts'
`,
--filter-extract='$ts'`,
RunE: createImagePolicyRun}
type imagePolicyFlags struct {

View File

@@ -26,7 +26,7 @@ import (
"github.com/fluxcd/pkg/apis/meta"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
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
flux create image repository app-repo \
--cert-secret-ref client-cert \
--image registry.example.com/private/app --interval 5m
`,
--image registry.example.com/private/app --interval 5m`,
RunE: createImageRepositoryRun,
}

View File

@@ -22,9 +22,8 @@ import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/pkg/apis/meta"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var createImageUpdateCmd = &cobra.Command{
@@ -50,8 +49,7 @@ mentioned in YAMLs in a git repository.`,
--push-branch=image-updates \
--author-name=flux \
--author-email=flux@example.com \
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}"
`,
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}"`,
RunE: createImageUpdateRun,
}
@@ -114,25 +112,33 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
Labels: labels,
},
Spec: autov1.ImageUpdateAutomationSpec{
Checkout: autov1.GitCheckoutSpec{
GitRepositoryRef: meta.LocalObjectReference{
Name: imageUpdateArgs.gitRepoRef,
SourceRef: autov1.SourceReference{
Kind: sourcev1.GitRepositoryKind,
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{
Duration: createArgs.interval,
},
Commit: autov1.CommitSpec{
AuthorName: imageUpdateArgs.authorName,
AuthorEmail: imageUpdateArgs.authorEmail,
MessageTemplate: imageUpdateArgs.commitTemplate,
},
},
}
if imageUpdateArgs.pushBranch != "" {
update.Spec.Push = &autov1.PushSpec{
update.Spec.GitSpec.Push = &autov1.PushSpec{
Branch: imageUpdateArgs.pushBranch,
}
}

View File

@@ -19,7 +19,6 @@ package main
import (
"context"
"fmt"
"path/filepath"
"strings"
"time"
@@ -46,7 +45,7 @@ var createKsCmd = &cobra.Command{
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
flux create kustomization contour \
--source=contour \
--source=GitRepository/contour \
--path="./examples/contour/" \
--prune=true \
--interval=10m \
@@ -58,7 +57,16 @@ var createKsCmd = &cobra.Command{
# Create a Kustomization resource that depends on the previous one
flux create kustomization webapp \
--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" \
--prune=true \
--interval=5m \
@@ -68,8 +76,7 @@ var createKsCmd = &cobra.Command{
flux create kustomization secrets \
--source=Bucket/secrets \
--prune=true \
--interval=5m
`,
--interval=5m`,
RunE: createKsCmdRun,
}
@@ -93,10 +100,10 @@ func init() {
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().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().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().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")
@@ -143,11 +150,12 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
Interval: metav1.Duration{
Duration: createArgs.interval,
},
Path: filepath.ToSlash(kustomizationArgs.path.String()),
Path: kustomizationArgs.path.ToSlash(),
Prune: kustomizationArgs.prune,
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: kustomizationArgs.source.Kind,
Name: kustomizationArgs.source.Name,
Kind: kustomizationArgs.source.Kind,
Name: kustomizationArgs.source.Name,
Namespace: kustomizationArgs.source.Namespace,
},
Suspend: false,
Validation: kustomizationArgs.validation,
@@ -211,7 +219,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}
if createArgs.export {
return exportKs(kustomization)
return printExport(exportKs(&kustomization))
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -45,8 +45,7 @@ var createReceiverCmd = &cobra.Command{
--event push \
--secret-ref webhook-token \
--resource GitRepository/webapp \
--resource HelmRepository/webapp
`,
--resource HelmRepository/webapp`,
RunE: createReceiverCmdRun,
}
@@ -62,8 +61,8 @@ var receiverArgs receiverFlags
func init() {
createReceiverCmd.Flags().StringVar(&receiverArgs.receiverType, "type", "", "")
createReceiverCmd.Flags().StringVar(&receiverArgs.secretRef, "secret-ref", "", "")
createReceiverCmd.Flags().StringArrayVar(&receiverArgs.events, "event", []string{}, "")
createReceiverCmd.Flags().StringArrayVar(&receiverArgs.resources, "resource", []string{}, "")
createReceiverCmd.Flags().StringSliceVar(&receiverArgs.events, "event", []string{}, "also accepts comma-separated values")
createReceiverCmd.Flags().StringSliceVar(&receiverArgs.resources, "resource", []string{}, "also accepts comma-separated values")
createCmd.AddCommand(createReceiverCmd)
}
@@ -125,7 +124,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
if createArgs.export {
return exportReceiver(receiver)
return printExport(exportReceiver(&receiver))
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -34,8 +34,7 @@ import (
var createSecretGitCmd = &cobra.Command{
Use: "git [name]",
Short: "Create or update a Kubernetes secret for Git authentication",
Long: `
The create secret git command generates a Kubernetes secret with Git credentials.
Long: `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 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
@@ -45,39 +44,48 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
--ssh-key-algorithm=ecdsa \
--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
flux create secret git podinfo-auth \
--url=https://github.com/stefanprodan/podinfo \
--username=username \
--password=password
# Create a Git SSH secret on disk and print the deploy key
# Create a Git SSH secret on disk
flux create secret git podinfo-auth \
--url=ssh://git@github.com/stefanprodan/podinfo \
--export > podinfo-auth.yaml
yq read podinfo-auth.yaml 'data."identity.pub"' | base64 --decode
# Create a Git SSH secret on disk and encrypt it with Mozilla SOPS
flux create secret git podinfo-auth \
--namespace=apps \
--url=ssh://git@github.com/stefanprodan/podinfo \
--export > podinfo-auth.yaml
# Print the deploy key
yq eval '.stringData."identity.pub"' podinfo-auth.yaml
# Encrypt the secret on disk with Mozilla SOPS
sops --encrypt --encrypted-regex '^(data|stringData)$' \
--in-place podinfo-auth.yaml
`,
--in-place podinfo-auth.yaml`,
RunE: createSecretGitCmdRun,
}
type secretGitFlags struct {
url string
username string
password string
keyAlgorithm flags.PublicKeyAlgorithm
rsaBits flags.RSAKeyBits
ecdsaCurve flags.ECDSACurve
caFile string
url string
username string
password string
keyAlgorithm flags.PublicKeyAlgorithm
rsaBits flags.RSAKeyBits
ecdsaCurve flags.ECDSACurve
caFile string
privateKeyFile string
}
var secretGitArgs = NewSecretGitFlags()
@@ -90,6 +98,7 @@ func init() {
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
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)
}
@@ -130,9 +139,11 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
switch u.Scheme {
case "ssh":
opts.SSHHostname = u.Host
opts.PrivateKeyPath = secretGitArgs.privateKeyFile
opts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(secretGitArgs.keyAlgorithm)
opts.RSAKeyBits = int(secretGitArgs.rsaBits)
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
opts.Password = secretGitArgs.password
case "http", "https":
if secretGitArgs.username == "" || secretGitArgs.password == "" {
return fmt.Errorf("for Git over HTTP/S the username and password are required")
@@ -172,7 +183,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err
}
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
logger.Actionf("git secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil
}

View File

@@ -31,10 +31,8 @@ import (
var createSecretHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Create or update a Kubernetes secret for Helm repository authentication",
Long: `
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
Long: `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
flux create secret helm repo-auth \
--namespace=my-namespace \
--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)$' \
--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 \
--username=username \
--password=password \
--cert-file=./cert.crt \
--key-file=./key.crt \
--ca-file=./ca.crt
`,
--ca-file=./ca.crt`,
RunE: createSecretHelmCmdRun,
}
@@ -115,5 +112,6 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
return err
}
logger.Actionf("helm secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil
}

View File

@@ -32,10 +32,8 @@ import (
var createSecretTLSCmd = &cobra.Command{
Use: "tls [name]",
Short: "Create or update a Kubernetes secret with TLS certificates",
Long: `
The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`,
Example: `
# Create a TLS secret on disk and encrypt it with Mozilla SOPS.
Long: `The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`,
Example: ` # Create a TLS secret on disk and encrypt it with Mozilla SOPS.
# Files are expected to be PEM-encoded.
flux create secret tls certs \
--namespace=my-namespace \
@@ -44,8 +42,7 @@ The create secret tls command generates a Kubernetes secret with certificates fo
--export > certs.yaml
sops --encrypt --encrypted-regex '^(data|stringData)$' \
--in-place certs.yaml
`,
--in-place certs.yaml`,
RunE: createSecretTLSCmdRun,
}
@@ -112,5 +109,6 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
return err
}
logger.Actionf("tls secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil
}

View File

@@ -19,7 +19,6 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"os"
"github.com/spf13/cobra"
@@ -40,10 +39,9 @@ import (
var createSourceBucketCmd = &cobra.Command{
Use: "bucket [name]",
Short: "Create or update a Bucket source",
Long: `
The create source bucket command generates a Bucket resource and waits for it to be downloaded.
Long: `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.`,
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 \
--bucket-name=podinfo \
--endpoint=minio.minio.svc.cluster.local:9000 \
@@ -52,14 +50,13 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
--secret-key=mysecretkey \
--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 \
--bucket-name=podinfo \
--provider=aws \
--endpoint=s3.amazonaws.com \
--region=us-east-1 \
--interval=10m
`,
--interval=10m`,
RunE: createSourceBucketCmdRun,
}
@@ -114,7 +111,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
return err
}
tmpDir, err := ioutil.TempDir("", name)
tmpDir, err := os.MkdirTemp("", name)
if err != nil {
return err
}
@@ -144,7 +141,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
}
if createArgs.export {
return exportBucket(*bucket)
return printExport(exportBucket(bucket))
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -20,7 +20,6 @@ import (
"context"
"crypto/elliptic"
"fmt"
"io/ioutil"
"net/url"
"os"
@@ -49,19 +48,20 @@ type sourceGitFlags struct {
semver string
username string
password string
caFile string
keyAlgorithm flags.PublicKeyAlgorithm
keyRSABits flags.RSAKeyBits
keyECDSACurve flags.ECDSACurve
secretRef string
gitImplementation flags.GitImplementation
caFile string
privateKeyFile string
recurseSubmodules bool
}
var createSourceGitCmd = &cobra.Command{
Use: "git [name]",
Short: "Create or update a GitRepository source",
Long: `
The create source git command generates a GitRepository resource and waits for it to sync.
Long: `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 private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a public Git repository master branch
@@ -69,7 +69,7 @@ For private Git repositories, the basic authentication credentials are stored in
--url=https://github.com/stefanprodan/podinfo \
--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 \
--url=https://github.com/stefanprodan/podinfo \
--tag="3.2.3"
@@ -79,12 +79,12 @@ For private Git repositories, the basic authentication credentials are stored in
--url=https://github.com/stefanprodan/podinfo \
--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 \
--url=ssh://git@github.com/stefanprodan/podinfo \
--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
flux create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \
@@ -92,12 +92,28 @@ For private Git repositories, the basic authentication credentials are stored in
--ssh-key-algorithm=ecdsa \
--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 \
--url=https://github.com/stefanprodan/podinfo \
--username=username \
--password=password
`,
--password=password`,
RunE: createSourceGitCmdRun,
}
@@ -105,7 +121,7 @@ var sourceGitArgs = newSourceGitFlags()
func init() {
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.semver, "tag-semver", "", "git tag semver range")
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.username, "username", "u", "", "basic authentication username")
@@ -115,7 +131,10 @@ func init() {
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyECDSACurve, "ssh-ecdsa-curve", sourceGitArgs.keyECDSACurve.Description())
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials")
createSourceGitCmd.Flags().Var(&sourceGitArgs.gitImplementation, "git-implementation", sourceGitArgs.gitImplementation.Description())
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates, requires libgit2")
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)
}
@@ -138,16 +157,6 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
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)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
@@ -156,6 +165,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)
}
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 := os.MkdirTemp("", name)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
sourceLabels, err := parseLabels()
if err != nil {
return err
@@ -172,7 +199,8 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
Interval: metav1.Duration{
Duration: createArgs.interval,
},
Reference: &sourcev1.GitRepositoryRef{},
RecurseSubmodules: sourceGitArgs.recurseSubmodules,
Reference: &sourcev1.GitRepositoryRef{},
},
}
@@ -195,7 +223,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
}
if createArgs.export {
return exportGit(gitRepository)
return printExport(exportGit(&gitRepository))
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
@@ -216,13 +244,19 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
switch u.Scheme {
case "ssh":
secretOpts.SSHHostname = u.Host
secretOpts.PrivateKeyPath = sourceGitArgs.privateKeyFile
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(sourceGitArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(sourceGitArgs.keyRSABits)
secretOpts.ECDSACurve = sourceGitArgs.keyECDSACurve.Curve
secretOpts.Password = sourceGitArgs.password
case "https":
secretOpts.Username = sourceGitArgs.username
secretOpts.Password = sourceGitArgs.password
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)
if err != nil {

View File

@@ -19,7 +19,6 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
@@ -43,38 +42,37 @@ import (
var createSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Create or update a HelmRepository source",
Long: `
The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
Long: `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.`,
Example: ` # Create a source from a public Helm repository
Example: ` # Create a source for a public Helm repository
flux create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \
--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 \
--url=https://stefanprodan.github.io/podinfo \
--username=username \
--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 \
--url=https://stefanprodan.github.io/podinfo \
--cert-file=./cert.crt \
--key-file=./key.crt \
--ca-file=./ca.crt
`,
--ca-file=./ca.crt`,
RunE: createSourceHelmCmdRun,
}
type sourceHelmFlags struct {
url string
username string
password string
certFile string
keyFile string
caFile string
secretRef string
url string
username string
password string
certFile string
keyFile string
caFile string
secretRef string
passCredentials bool
}
var sourceHelmArgs sourceHelmFlags
@@ -87,6 +85,7 @@ func init() {
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
createSourceHelmCmd.Flags().BoolVarP(&sourceHelmArgs.passCredentials, "pass-credentials", "", false, "pass credentials to all domains")
createSourceCmd.AddCommand(createSourceHelmCmd)
}
@@ -106,7 +105,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return err
}
tmpDir, err := ioutil.TempDir("", name)
tmpDir, err := os.MkdirTemp("", name)
if err != nil {
return err
}
@@ -134,10 +133,11 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
Name: sourceHelmArgs.secretRef,
}
helmRepository.Spec.PassCredentials = sourceHelmArgs.passCredentials
}
if createArgs.export {
return exportHelmRepository(*helmRepository)
return printExport(exportHelmRepository(helmRepository))
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
@@ -177,6 +177,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
Name: secretName,
}
helmRepository.Spec.PassCredentials = sourceHelmArgs.passCredentials
logger.Successf("authentication configured")
}
}

View File

@@ -37,8 +37,7 @@ import (
var createTenantCmd = &cobra.Command{
Use: "tenant",
Short: "Create or update a tenant",
Long: `
The create tenant command generates namespaces, service accounts and role bindings to limit the
Long: `The create tenant command generates namespaces, service accounts and role bindings to limit the
reconcilers scope to the tenant namespaces.`,
Example: ` # Create a tenant with access to a namespace
flux create tenant dev-team \
@@ -49,8 +48,7 @@ reconcilers scope to the tenant namespaces.`,
flux create tenant dev-team \
--with-namespace=frontend \
--with-namespace=backend \
--export > dev-team.yaml
`,
--export > dev-team.yaml`,
RunE: createTenantCmdRun,
}
@@ -150,7 +148,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
}
if createArgs.export {
for i, _ := range tenantArgs.namespaces {
for i := range tenantArgs.namespaces {
if err := exportTenant(namespaces[i], accounts[i], roleBindings[i]); err != nil {
return err
}
@@ -166,7 +164,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
return err
}
for i, _ := range tenantArgs.namespaces {
for i := range tenantArgs.namespaces {
logger.Actionf("applying namespace %s", namespaces[i].Name)
if err := upsertNamespace(ctx, kubeClient, namespaces[i]); err != nil {
return err

View File

@@ -17,14 +17,8 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -33,56 +27,13 @@ var deleteAlertCmd = &cobra.Command{
Short: "Delete a Alert resource",
Long: "The delete alert command removes the given Alert from the cluster.",
Example: ` # Delete an Alert and the Kubernetes resources created by it
flux delete alert main
`,
RunE: deleteAlertCmdRun,
flux delete alert main`,
RunE: deleteCommand{
apiType: alertType,
object: universalAdapter{&notificationv1.Alert{}},
}.run,
}
func init() {
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
}

View File

@@ -17,14 +17,8 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -33,56 +27,13 @@ var deleteAlertProviderCmd = &cobra.Command{
Short: "Delete a Provider resource",
Long: "The delete alert-provider command removes the given Provider from the cluster.",
Example: ` # Delete a Provider and the Kubernetes resources created by it
flux delete alert-provider slack
`,
RunE: deleteAlertProviderCmdRun,
flux delete alert-provider slack`,
RunE: deleteCommand{
apiType: alertProviderType,
object: universalAdapter{&notificationv1.Provider{}},
}.run,
}
func init() {
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
}

View File

@@ -17,8 +17,9 @@ limitations under the License.
package main
import (
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"github.com/spf13/cobra"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
)
var deleteHelmReleaseCmd = &cobra.Command{
@@ -27,8 +28,7 @@ var deleteHelmReleaseCmd = &cobra.Command{
Short: "Delete a HelmRelease resource",
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
Example: ` # Delete a Helm release and the Kubernetes resources created by it
flux delete hr podinfo
`,
flux delete hr podinfo`,
RunE: deleteCommand{
apiType: helmReleaseType,
object: universalAdapter{&helmv2.HelmRelease{}},

View File

@@ -19,7 +19,7 @@ package main
import (
"github.com/spf13/cobra"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var deleteImagePolicyCmd = &cobra.Command{
@@ -27,8 +27,7 @@ var deleteImagePolicyCmd = &cobra.Command{
Short: "Delete an ImagePolicy object",
Long: "The delete image policy command deletes the given ImagePolicy from the cluster.",
Example: ` # Delete an image policy
flux delete image policy alpine3.x
`,
flux delete image policy alpine3.x`,
RunE: deleteCommand{
apiType: imagePolicyType,
object: universalAdapter{&imagev1.ImagePolicy{}},

View File

@@ -19,7 +19,7 @@ package main
import (
"github.com/spf13/cobra"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var deleteImageRepositoryCmd = &cobra.Command{
@@ -27,8 +27,7 @@ var deleteImageRepositoryCmd = &cobra.Command{
Short: "Delete an ImageRepository object",
Long: "The delete image repository command deletes the given ImageRepository from the cluster.",
Example: ` # Delete an image repository
flux delete image repository alpine
`,
flux delete image repository alpine`,
RunE: deleteCommand{
apiType: imageRepositoryType,
object: universalAdapter{&imagev1.ImageRepository{}},

View File

@@ -19,7 +19,7 @@ package main
import (
"github.com/spf13/cobra"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
)
var deleteImageUpdateCmd = &cobra.Command{
@@ -27,8 +27,7 @@ var deleteImageUpdateCmd = &cobra.Command{
Short: "Delete an ImageUpdateAutomation object",
Long: "The delete image update command deletes the given ImageUpdateAutomation from the cluster.",
Example: ` # Delete an image update automation
flux delete image update latest-images
`,
flux delete image update latest-images`,
RunE: deleteCommand{
apiType: imageUpdateAutomationType,
object: universalAdapter{&autov1.ImageUpdateAutomation{}},

View File

@@ -17,8 +17,9 @@ limitations under the License.
package main
import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/spf13/cobra"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
)
var deleteKsCmd = &cobra.Command{
@@ -27,8 +28,7 @@ var deleteKsCmd = &cobra.Command{
Short: "Delete a Kustomization resource",
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
Example: ` # Delete a kustomization and the Kubernetes resources created by it
flux delete kustomization podinfo
`,
flux delete kustomization podinfo`,
RunE: deleteCommand{
apiType: kustomizationType,
object: universalAdapter{&kustomizev1.Kustomization{}},

View File

@@ -17,14 +17,8 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -33,56 +27,13 @@ var deleteReceiverCmd = &cobra.Command{
Short: "Delete a Receiver resource",
Long: "The delete receiver command removes the given Receiver from the cluster.",
Example: ` # Delete an Receiver and the Kubernetes resources created by it
flux delete receiver main
`,
RunE: deleteReceiverCmdRun,
flux delete receiver main`,
RunE: deleteCommand{
apiType: receiverType,
object: universalAdapter{&notificationv1.Receiver{}},
}.run,
}
func init() {
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
}

View File

@@ -17,8 +17,9 @@ limitations under the License.
package main
import (
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var deleteSourceBucketCmd = &cobra.Command{
@@ -26,8 +27,7 @@ var deleteSourceBucketCmd = &cobra.Command{
Short: "Delete a Bucket source",
Long: "The delete source bucket command deletes the given Bucket from the cluster.",
Example: ` # Delete a Bucket source
flux delete source bucket podinfo
`,
flux delete source bucket podinfo`,
RunE: deleteCommand{
apiType: bucketType,
object: universalAdapter{&sourcev1.Bucket{}},

View File

@@ -17,8 +17,9 @@ limitations under the License.
package main
import (
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var deleteSourceGitCmd = &cobra.Command{
@@ -26,8 +27,7 @@ var deleteSourceGitCmd = &cobra.Command{
Short: "Delete a GitRepository source",
Long: "The delete source git command deletes the given GitRepository from the cluster.",
Example: ` # Delete a Git repository
flux delete source git podinfo
`,
flux delete source git podinfo`,
RunE: deleteCommand{
apiType: gitRepositoryType,
object: universalAdapter{&sourcev1.GitRepository{}},

View File

@@ -17,14 +17,9 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var deleteSourceHelmCmd = &cobra.Command{
@@ -32,8 +27,7 @@ var deleteSourceHelmCmd = &cobra.Command{
Short: "Delete a HelmRepository source",
Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
Example: ` # Delete a Helm repository
flux delete source helm podinfo
`,
flux delete source helm podinfo`,
RunE: deleteCommand{
apiType: helmRepositoryType,
object: universalAdapter{&sourcev1.HelmRepository{}},
@@ -43,48 +37,3 @@ var deleteSourceHelmCmd = &cobra.Command{
func init() {
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
View 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) + "/"
}

View File

@@ -20,7 +20,6 @@ import (
"bytes"
"context"
"fmt"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"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 {
logger.Failuref("no objects found in %s namespace", rootArgs.namespace)
return nil
return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
}
for i := 0; i < export.list.len(); i++ {

View File

@@ -17,16 +17,9 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
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"
)
@@ -38,62 +31,18 @@ var exportAlertCmd = &cobra.Command{
flux export alert --all > alerts.yaml
# Export a Alert
flux export alert main > main.yaml
`,
RunE: exportAlertCmdRun,
flux export alert main > main.yaml`,
RunE: exportCommand{
object: alertAdapter{&notificationv1.Alert{}},
list: alertListAdapter{&notificationv1.AlertList{}},
}.run,
}
func init() {
exportCmd.AddCommand(exportAlertCmd)
}
func exportAlertCmdRun(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 {
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 {
func exportAlert(alert *notificationv1.Alert) interface{} {
gvk := notificationv1.GroupVersion.WithKind("Alert")
export := notificationv1.Alert{
TypeMeta: metav1.TypeMeta{
@@ -109,12 +58,13 @@ func exportAlert(alert notificationv1.Alert) error {
Spec: alert.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
return export
}
func (ex alertAdapter) export() interface{} {
return exportAlert(ex.Alert)
}
func (ex alertListAdapter) exportItem(i int) interface{} {
return exportAlert(&ex.AlertList.Items[i])
}

View File

@@ -17,16 +17,9 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
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"
)
@@ -38,62 +31,18 @@ var exportAlertProviderCmd = &cobra.Command{
flux export alert-provider --all > alert-providers.yaml
# Export a Provider
flux export alert-provider slack > slack.yaml
`,
RunE: exportAlertProviderCmdRun,
flux export alert-provider slack > slack.yaml`,
RunE: exportCommand{
object: alertProviderAdapter{&notificationv1.Provider{}},
list: alertProviderListAdapter{&notificationv1.ProviderList{}},
}.run,
}
func init() {
exportCmd.AddCommand(exportAlertProviderCmd)
}
func exportAlertProviderCmdRun(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 {
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 {
func exportAlertProvider(alertProvider *notificationv1.Provider) interface{} {
gvk := notificationv1.GroupVersion.WithKind("Provider")
export := notificationv1.Provider{
TypeMeta: metav1.TypeMeta{
@@ -108,13 +57,13 @@ func exportAlertProvider(alertProvider notificationv1.Provider) error {
},
Spec: alertProvider.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
return export
}
func (ex alertProviderAdapter) export() interface{} {
return exportAlertProvider(ex.Provider)
}
func (ex alertProviderListAdapter) exportItem(i int) interface{} {
return exportAlertProvider(&ex.ProviderList.Items[i])
}

View File

@@ -17,16 +17,9 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
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"
)
@@ -39,62 +32,18 @@ var exportHelmReleaseCmd = &cobra.Command{
flux export helmrelease --all > kustomizations.yaml
# Export a HelmRelease
flux export hr my-app > app-release.yaml
`,
RunE: exportHelmReleaseCmdRun,
flux export hr my-app > app-release.yaml`,
RunE: exportCommand{
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
list: helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
}.run,
}
func init() {
exportCmd.AddCommand(exportHelmReleaseCmd)
}
func exportHelmReleaseCmdRun(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 {
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 {
func exportHelmRelease(helmRelease *helmv2.HelmRelease) interface{} {
gvk := helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)
export := helmv2.HelmRelease{
TypeMeta: metav1.TypeMeta{
@@ -109,13 +58,13 @@ func exportHelmRelease(helmRelease helmv2.HelmRelease) error {
},
Spec: helmRelease.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
return export
}
func (ex helmReleaseAdapter) export() interface{} {
return exportHelmRelease(ex.HelmRelease)
}
func (ex helmReleaseListAdapter) exportItem(i int) interface{} {
return exportHelmRelease(&ex.HelmReleaseList.Items[i])
}

View File

@@ -20,7 +20,7 @@ import (
"github.com/spf13/cobra"
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/v1beta1"
)
var exportImagePolicyCmd = &cobra.Command{
@@ -31,8 +31,7 @@ var exportImagePolicyCmd = &cobra.Command{
flux export image policy --all > image-policies.yaml
# Export a specific policy
flux export image policy alpine1x > alpine1x.yaml
`,
flux export image policy alpine1x > alpine1x.yaml`,
RunE: exportCommand{
object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},

View File

@@ -20,7 +20,7 @@ import (
"github.com/spf13/cobra"
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/v1beta1"
)
var exportImageRepositoryCmd = &cobra.Command{
@@ -31,8 +31,7 @@ var exportImageRepositoryCmd = &cobra.Command{
flux export image repository --all > image-repositories.yaml
# Export a specific ImageRepository resource
flux export image repository alpine > alpine.yaml
`,
flux export image repository alpine > alpine.yaml`,
RunE: exportCommand{
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},

View File

@@ -20,7 +20,7 @@ import (
"github.com/spf13/cobra"
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/v1beta1"
)
var exportImageUpdateCmd = &cobra.Command{
@@ -31,8 +31,7 @@ var exportImageUpdateCmd = &cobra.Command{
flux export image update --all > updates.yaml
# Export a specific automation
flux export image update latest-images > latest.yaml
`,
flux export image update latest-images > latest.yaml`,
RunE: exportCommand{
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},

View File

@@ -17,16 +17,9 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
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"
)
@@ -39,62 +32,18 @@ var exportKsCmd = &cobra.Command{
flux export kustomization --all > kustomizations.yaml
# Export a Kustomization
flux export kustomization my-app > kustomization.yaml
`,
RunE: exportKsCmdRun,
flux export kustomization my-app > kustomization.yaml`,
RunE: exportCommand{
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
list: kustomizationListAdapter{&kustomizev1.KustomizationList{}},
}.run,
}
func init() {
exportCmd.AddCommand(exportKsCmd)
}
func exportKsCmdRun(cmd *cobra.Command, args []string) error {
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 {
func exportKs(kustomization *kustomizev1.Kustomization) interface{} {
gvk := kustomizev1.GroupVersion.WithKind("Kustomization")
export := kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
@@ -110,12 +59,13 @@ func exportKs(kustomization kustomizev1.Kustomization) error {
Spec: kustomization.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
return export
}
func (ex kustomizationAdapter) export() interface{} {
return exportKs(ex.Kustomization)
}
func (ex kustomizationListAdapter) exportItem(i int) interface{} {
return exportKs(&ex.KustomizationList.Items[i])
}

View File

@@ -17,16 +17,9 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
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"
)
@@ -38,62 +31,18 @@ var exportReceiverCmd = &cobra.Command{
flux export receiver --all > receivers.yaml
# Export a Receiver
flux export receiver main > main.yaml
`,
RunE: exportReceiverCmdRun,
flux export receiver main > main.yaml`,
RunE: exportCommand{
list: receiverListAdapter{&notificationv1.ReceiverList{}},
object: receiverAdapter{&notificationv1.Receiver{}},
}.run,
}
func init() {
exportCmd.AddCommand(exportReceiverCmd)
}
func exportReceiverCmdRun(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 {
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 {
func exportReceiver(receiver *notificationv1.Receiver) interface{} {
gvk := notificationv1.GroupVersion.WithKind("Receiver")
export := notificationv1.Receiver{
TypeMeta: metav1.TypeMeta{
@@ -109,12 +58,13 @@ func exportReceiver(receiver notificationv1.Receiver) error {
Spec: receiver.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
return export
}
func (ex receiverAdapter) export() interface{} {
return exportReceiver(ex.Receiver)
}
func (ex receiverListAdapter) exportItem(i int) interface{} {
return exportReceiver(&ex.ReceiverList.Items[i])
}

134
cmd/flux/export_secret.go Normal file
View 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)
}

View File

@@ -17,17 +17,10 @@ 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"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -39,72 +32,18 @@ var exportSourceBucketCmd = &cobra.Command{
flux export source bucket --all > sources.yaml
# Export a Bucket source including the static credentials
flux export source bucket my-bucket --with-credentials > source.yaml
`,
RunE: exportSourceBucketCmdRun,
flux export source bucket my-bucket --with-credentials > source.yaml`,
RunE: exportWithSecretCommand{
list: bucketListAdapter{&sourcev1.BucketList{}},
object: bucketAdapter{&sourcev1.Bucket{}},
}.run,
}
func init() {
exportSourceCmd.AddCommand(exportSourceBucketCmd)
}
func exportSourceBucketCmdRun(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 {
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 {
func exportBucket(source *sourcev1.Bucket) interface{} {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)
export := sourcev1.Bucket{
TypeMeta: metav1.TypeMeta{
@@ -119,49 +58,34 @@ func exportBucket(source sourcev1.Bucket) error {
},
Spec: source.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
return export
}
func exportBucketCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.Bucket) error {
func getBucketSecret(source *sourcev1.Bucket) *types.NamespacedName {
if source.Spec.SecretRef != nil {
namespacedName := types.NamespacedName{
Namespace: source.Namespace,
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{
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 &namespacedName
}
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])
}

View File

@@ -17,17 +17,10 @@ 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"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -39,72 +32,18 @@ var exportSourceGitCmd = &cobra.Command{
flux export source git --all > sources.yaml
# Export a GitRepository source including the SSH key pair or basic auth credentials
flux export source git my-private-repo --with-credentials > source.yaml
`,
RunE: exportSourceGitCmdRun,
flux export source git my-private-repo --with-credentials > source.yaml`,
RunE: exportWithSecretCommand{
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
list: gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
}.run,
}
func init() {
exportSourceCmd.AddCommand(exportSourceGitCmd)
}
func exportSourceGitCmdRun(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 {
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 {
func exportGit(source *sourcev1.GitRepository) interface{} {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)
export := sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
@@ -120,48 +59,33 @@ func exportGit(source sourcev1.GitRepository) error {
Spec: source.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
return export
}
func exportGitCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.GitRepository) error {
func getGitSecret(source *sourcev1.GitRepository) *types.NamespacedName {
if source.Spec.SecretRef != nil {
namespacedName := types.NamespacedName{
Namespace: source.Namespace,
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{
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 &namespacedName
}
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])
}

View File

@@ -17,17 +17,10 @@ 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"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -39,72 +32,18 @@ var exportSourceHelmCmd = &cobra.Command{
flux export source helm --all > sources.yaml
# Export a HelmRepository source including the basic auth credentials
flux export source helm my-private-repo --with-credentials > source.yaml
`,
RunE: exportSourceHelmCmdRun,
flux export source helm my-private-repo --with-credentials > source.yaml`,
RunE: exportWithSecretCommand{
list: helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
}.run,
}
func init() {
exportSourceCmd.AddCommand(exportSourceHelmCmd)
}
func exportSourceHelmCmdRun(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 {
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 {
func exportHelmRepository(source *sourcev1.HelmRepository) interface{} {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)
export := sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{
@@ -119,49 +58,32 @@ func exportHelmRepository(source sourcev1.HelmRepository) error {
},
Spec: source.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
return export
}
func exportHelmCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.HelmRepository) error {
func getHelmSecret(source *sourcev1.HelmRepository) *types.NamespacedName {
if source.Spec.SecretRef != nil {
namespacedName := types.NamespacedName{
Namespace: source.Namespace,
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{
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 &namespacedName
}
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])
}

View File

@@ -25,6 +25,9 @@ import (
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
watchtools "k8s.io/client-go/tools/watch"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/pkg/apis/meta"
@@ -32,6 +35,26 @@ import (
"github.com/fluxcd/flux2/internal/utils"
)
type deriveType func(runtime.Object) (summarisable, error)
type typeMap map[string]deriveType
func (m typeMap) registerCommand(t string, f deriveType) error {
if _, ok := m[t]; ok {
return fmt.Errorf("duplicate type function %s", t)
}
m[t] = f
return nil
}
func (m typeMap) execute(t string, obj runtime.Object) (summarisable, error) {
f, ok := m[t]
if !ok {
return nil, fmt.Errorf("unsupported type %s", t)
}
return f(obj)
}
var getCmd = &cobra.Command{
Use: "get",
Short: "Get the resources and their status",
@@ -39,7 +62,10 @@ var getCmd = &cobra.Command{
}
type GetFlags struct {
allNamespaces bool
allNamespaces bool
noHeader bool
statusSelector string
watch bool
}
var getArgs GetFlags
@@ -47,6 +73,10 @@ var getArgs GetFlags
func init() {
getCmd.PersistentFlags().BoolVarP(&getArgs.allNamespaces, "all-namespaces", "A", false,
"list the requested object(s) across all namespaces")
getCmd.PersistentFlags().BoolVarP(&getArgs.noHeader, "no-header", "", false, "skip the header when printing the results")
getCmd.PersistentFlags().BoolVarP(&getArgs.watch, "watch", "w", false, "After listing/getting the requested object, watch for changes.")
getCmd.PersistentFlags().StringVar(&getArgs.statusSelector, "status-selector", "",
"specify the status condition name and the desired state to filter the get result, e.g. ready=false")
rootCmd.AddCommand(getCmd)
}
@@ -54,6 +84,7 @@ type summarisable interface {
listAdapter
summariseItem(i int, includeNamespace bool, includeKind bool) []string
headers(includeNamespace bool) []string
statusSelectorMatches(i int, conditionType, conditionStatus string) bool
}
// --- these help with implementations of summarisable
@@ -65,6 +96,20 @@ func statusAndMessage(conditions []metav1.Condition) (string, string) {
return string(metav1.ConditionFalse), "waiting to be reconciled"
}
func statusMatches(conditionType, conditionStatus string, conditions []metav1.Condition) bool {
// we don't use apimeta.FindStatusCondition because we'd like to use EqualFold to compare two strings
var c *metav1.Condition
for i := range conditions {
if strings.EqualFold(conditions[i].Type, conditionType) {
c = &conditions[i]
}
}
if c != nil {
return strings.EqualFold(string(c.Status), conditionStatus)
}
return false
}
func nameColumns(item named, includeNamespace bool, includeKind bool) []string {
name := item.GetName()
if includeKind {
@@ -82,7 +127,8 @@ var namespaceHeader = []string{"Namespace"}
type getCommand struct {
apiType
list summarisable
list summarisable
funcMap typeMap
}
func (get getCommand) run(cmd *cobra.Command, args []string) error {
@@ -103,13 +149,17 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
listOpts = append(listOpts, client.MatchingFields{"metadata.name": args[0]})
}
getAll := cmd.Use == "all"
if getArgs.watch {
return get.watch(ctx, kubeClient, cmd, args, listOpts)
}
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
if err != nil {
return err
}
getAll := cmd.Use == "all"
if get.list.len() == 0 {
if !getAll {
logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace)
@@ -117,16 +167,97 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
return nil
}
header := get.list.headers(getArgs.allNamespaces)
var rows [][]string
for i := 0; i < get.list.len(); i++ {
row := get.list.summariseItem(i, getArgs.allNamespaces, getAll)
rows = append(rows, row)
var header []string
if !getArgs.noHeader {
header = get.list.headers(getArgs.allNamespaces)
}
rows, err := getRowsToPrint(getAll, get.list)
if err != nil {
return err
}
utils.PrintTable(os.Stdout, header, rows)
if getAll {
fmt.Println()
}
return nil
}
func getRowsToPrint(getAll bool, list summarisable) ([][]string, error) {
noFilter := true
var conditionType, conditionStatus string
if getArgs.statusSelector != "" {
parts := strings.SplitN(getArgs.statusSelector, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("expected status selector in type=status format, but found: %s", getArgs.statusSelector)
}
conditionType = parts[0]
conditionStatus = parts[1]
noFilter = false
}
var rows [][]string
for i := 0; i < list.len(); i++ {
if noFilter || list.statusSelectorMatches(i, conditionType, conditionStatus) {
row := list.summariseItem(i, getArgs.allNamespaces, getAll)
rows = append(rows, row)
}
}
return rows, nil
}
//
// watch starts a client-side watch of one or more resources.
func (get *getCommand) watch(ctx context.Context, kubeClient client.WithWatch, cmd *cobra.Command, args []string, listOpts []client.ListOption) error {
w, err := kubeClient.Watch(ctx, get.list.asClientList(), listOpts...)
if err != nil {
return err
}
_, err = watchUntil(ctx, w, get)
if err != nil {
return err
}
return nil
}
func watchUntil(ctx context.Context, w watch.Interface, get *getCommand) (bool, error) {
firstIteration := true
_, error := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
objToPrint := e.Object
sink, err := get.funcMap.execute(get.apiType.kind, objToPrint)
if err != nil {
return false, err
}
var header []string
if !getArgs.noHeader {
header = sink.headers(getArgs.allNamespaces)
}
rows, err := getRowsToPrint(false, sink)
if err != nil {
return false, err
}
if firstIteration {
utils.PrintTable(os.Stdout, header, rows)
firstIteration = false
} else {
utils.PrintTable(os.Stdout, []string{}, rows)
}
return false, nil
})
return false, error
}
func validateWatchOption(cmd *cobra.Command, toMatch string) error {
w, _ := cmd.Flags().GetBool("watch")
if cmd.Use == toMatch && w {
return fmt.Errorf("expected a single resource type, but found %s", cmd.Use)
}
return nil
}

View File

@@ -17,19 +17,14 @@ limitations under the License.
package main
import (
"context"
"os"
"fmt"
"strconv"
"strings"
"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"
"k8s.io/apimachinery/pkg/runtime"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
var getAlertCmd = &cobra.Command{
@@ -38,66 +33,61 @@ var getAlertCmd = &cobra.Command{
Short: "Get Alert statuses",
Long: "The get alert command prints the statuses of the resources.",
Example: ` # List all Alerts and their status
flux get alerts
`,
RunE: getAlertCmdRun,
flux get alerts`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: alertType,
list: &alertListAdapter{&notificationv1.AlertList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*notificationv1.Alert)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v alert", obj)
}
sink := alertListAdapter{
&notificationv1.AlertList{
Items: []notificationv1.Alert{
*o,
},
},
}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
getCmd.AddCommand(getAlertCmd)
}
func getAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !getArgs.allNamespaces {
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
}
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
func (s alertListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), status, msg, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
}
func (s alertListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Suspended"}
if includeNamespace {
return append(namespaceHeader, headers...)
}
return headers
}
func (s alertListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,17 +17,12 @@ limitations under the License.
package main
import (
"context"
"os"
"fmt"
"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"
"k8s.io/apimachinery/pkg/runtime"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
var getAlertProviderCmd = &cobra.Command{
@@ -36,64 +31,61 @@ var getAlertProviderCmd = &cobra.Command{
Short: "Get Provider statuses",
Long: "The get alert-provider command prints the statuses of the resources.",
Example: ` # List all Providers and their status
flux get alert-providers
`,
RunE: getAlertProviderCmdRun,
flux get alert-providers`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: alertProviderType,
list: alertProviderListAdapter{&notificationv1.ProviderList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*notificationv1.Provider)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v alert-provider", obj)
}
sink := alertProviderListAdapter{
&notificationv1.ProviderList{
Items: []notificationv1.Provider{
*o,
},
},
}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
getCmd.AddCommand(getAlertProviderCmd)
}
func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !getArgs.allNamespaces {
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
}
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
func (s alertProviderListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), status, msg)
}
func (s alertProviderListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message"}
if includeNamespace {
return append(namespaceHeader, headers...)
}
return headers
}
func (s alertProviderListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

96
cmd/flux/get_all.go Normal file
View File

@@ -0,0 +1,96 @@
/*
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 := validateWatchOption(cmd, "all")
if err != nil {
return err
}
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{&notificationv1.ReceiverList{}},
},
{
apiType: alertProviderType,
list: alertProviderListAdapter{&notificationv1.ProviderList{}},
},
{
apiType: alertType,
list: &alertListAdapter{&notificationv1.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)
}

View File

@@ -17,11 +17,13 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
)
var getHelmReleaseCmd = &cobra.Command{
@@ -30,12 +32,37 @@ var getHelmReleaseCmd = &cobra.Command{
Short: "Get HelmRelease statuses",
Long: "The get helmreleases command prints the statuses of the resources.",
Example: ` # List all Helm releases and their status
flux get helmreleases
`,
RunE: getCommand{
apiType: helmReleaseType,
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
}.run,
flux get helmreleases`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: helmReleaseType,
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*helmv2.HelmRelease)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v helmrelease", obj)
}
sink := helmReleaseListAdapter{&helmv2.HelmReleaseList{
Items: []helmv2.HelmRelease{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -57,3 +84,8 @@ func (a helmReleaseListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (a helmReleaseListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := a.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -25,6 +25,9 @@ var getImageCmd = &cobra.Command{
Aliases: []string{"image"},
Short: "Get image automation object status",
Long: "The get image sub-commands print the status of image automation objects.",
RunE: func(cmd *cobra.Command, args []string) error {
return validateWatchOption(cmd, "images")
},
}
func init() {

View File

@@ -17,9 +17,12 @@ limitations under the License.
package main
import (
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
"strings"
"github.com/spf13/cobra"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var getImageAllCmd = &cobra.Command{
@@ -30,31 +33,34 @@ var getImageAllCmd = &cobra.Command{
flux get images all --namespace=flux-system
# 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 {
c := getCommand{
apiType: imageRepositoryType,
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
err := validateWatchOption(cmd, "all")
if err != nil {
return err
}
c = getCommand{
apiType: imagePolicyType,
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
var allImageCmd = []getCommand{
{
apiType: imageRepositoryType,
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
},
{
apiType: imagePolicyType,
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
},
{
apiType: imageUpdateAutomationType,
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
},
}
c = getCommand{
apiType: imageUpdateAutomationType,
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
for _, c := range allImageCmd {
if err := c.run(cmd, args); err != nil {
if !strings.Contains(err.Error(), "no matches for kind") {
logger.Failuref(err.Error())
}
}
}
return nil

View File

@@ -17,9 +17,12 @@ limitations under the License.
package main
import (
"github.com/spf13/cobra"
"fmt"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var getImagePolicyCmd = &cobra.Command{
@@ -30,12 +33,37 @@ var getImagePolicyCmd = &cobra.Command{
flux get image policy
# List image policies from all namespaces
flux get image policy --all-namespaces
`,
RunE: getCommand{
apiType: imagePolicyType,
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}.run,
flux get image policy --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: imagePolicyType,
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*imagev1.ImagePolicy)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v policy", obj)
}
sink := imagePolicyListAdapter{&imagev1.ImagePolicyList{
Items: []imagev1.ImagePolicy{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -55,3 +83,8 @@ func (s imagePolicyListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (s imagePolicyListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,13 +17,15 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var getImageRepositoryCmd = &cobra.Command{
@@ -34,12 +36,37 @@ var getImageRepositoryCmd = &cobra.Command{
flux get image repository
# List image repositories from all namespaces
flux get image repository --all-namespaces
`,
RunE: getCommand{
apiType: imageRepositoryType,
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
}.run,
flux get image repository --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: imageRepositoryType,
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*imagev1.ImageRepository)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v repository", obj)
}
sink := imageRepositoryListAdapter{&imagev1.ImageRepositoryList{
Items: []imagev1.ImageRepository{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -64,3 +91,8 @@ func (s imageRepositoryListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (s imageRepositoryListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,13 +17,15 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
)
var getImageUpdateCmd = &cobra.Command{
@@ -34,12 +36,37 @@ var getImageUpdateCmd = &cobra.Command{
flux get image update
# List image update automations from all namespaces
flux get image update --all-namespaces
`,
RunE: getCommand{
apiType: imageUpdateAutomationType,
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
}.run,
flux get image update --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: imageUpdateAutomationType,
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*autov1.ImageUpdateAutomation)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v update", obj)
}
sink := imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{
Items: []autov1.ImageUpdateAutomation{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -63,3 +90,8 @@ func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []strin
}
return headers
}
func (s imageUpdateAutomationListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,11 +17,14 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
)
var getKsCmd = &cobra.Command{
@@ -30,12 +33,40 @@ var getKsCmd = &cobra.Command{
Short: "Get Kustomization statuses",
Long: "The get kustomizations command prints the statuses of the resources.",
Example: ` # List all kustomizations and their status
flux get kustomizations
`,
RunE: getCommand{
apiType: kustomizationType,
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
}.run,
flux get kustomizations`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: kustomizationType,
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*kustomizev1.Kustomization)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v kustomization", obj)
}
sink := kustomizationListAdapter{
&kustomizev1.KustomizationList{
Items: []kustomizev1.Kustomization{
*o,
},
},
}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -57,3 +88,8 @@ func (a kustomizationListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (a kustomizationListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := a.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,19 +17,14 @@ limitations under the License.
package main
import (
"context"
"os"
"fmt"
"strconv"
"strings"
"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"
"k8s.io/apimachinery/pkg/runtime"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
var getReceiverCmd = &cobra.Command{
@@ -38,63 +33,58 @@ var getReceiverCmd = &cobra.Command{
Short: "Get Receiver statuses",
Long: "The get receiver command prints the statuses of the resources.",
Example: ` # List all Receiver and their status
flux get receivers
`,
RunE: getReceiverCmdRun,
flux get receivers`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: receiverType,
list: receiverListAdapter{&notificationv1.ReceiverList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*notificationv1.Receiver)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v receiver", obj)
}
sink := receiverListAdapter{&notificationv1.ReceiverList{
Items: []notificationv1.Receiver{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
getCmd.AddCommand(getReceiverCmd)
}
func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !getArgs.allNamespaces {
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
}
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
func (s receiverListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), status, msg, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
}
func (s receiverListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Suspended"}
if includeNamespace {
return append(namespaceHeader, headers...)
}
return headers
}
func (s receiverListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -25,6 +25,10 @@ var getSourceCmd = &cobra.Command{
Aliases: []string{"source"},
Short: "Get source statuses",
Long: "The get source sub-commands print the statuses of the sources.",
RunE: func(cmd *cobra.Command, args []string) error {
return validateWatchOption(cmd, "sources")
},
}
func init() {

View File

@@ -17,8 +17,11 @@ limitations under the License.
package main
import (
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"strings"
"github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var getSourceAllCmd = &cobra.Command{
@@ -29,39 +32,38 @@ var getSourceAllCmd = &cobra.Command{
flux get sources all --namespace=flux-system
# 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 {
c := getCommand{
apiType: bucketType,
list: &bucketListAdapter{&sourcev1.BucketList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
err := validateWatchOption(cmd, "all")
if err != nil {
return err
}
c = getCommand{
apiType: gitRepositoryType,
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
var allSourceCmd = []getCommand{
{
apiType: bucketType,
list: &bucketListAdapter{&sourcev1.BucketList{}},
},
{
apiType: gitRepositoryType,
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
},
{
apiType: helmRepositoryType,
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
},
{
apiType: helmChartType,
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
},
}
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())
for _, c := range allSourceCmd {
if err := c.run(cmd, args); err != nil {
if !strings.Contains(err.Error(), "no matches for kind") {
logger.Failuref(err.Error())
}
}
}
return nil

View File

@@ -17,11 +17,14 @@ limitations under the License.
package main
import (
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var getSourceBucketCmd = &cobra.Command{
@@ -32,12 +35,37 @@ var getSourceBucketCmd = &cobra.Command{
flux get sources bucket
# List buckets from all namespaces
flux get sources helm --all-namespaces
`,
RunE: getCommand{
apiType: bucketType,
list: &bucketListAdapter{&sourcev1.BucketList{}},
}.run,
flux get sources helm --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: bucketType,
list: &bucketListAdapter{&sourcev1.BucketList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*sourcev1.Bucket)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v bucket", obj)
}
sink := &bucketListAdapter{&sourcev1.BucketList{
Items: []sourcev1.Bucket{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -62,3 +90,8 @@ func (a bucketListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (a bucketListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := a.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,11 +17,14 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var getSourceHelmChartCmd = &cobra.Command{
@@ -32,12 +35,37 @@ var getSourceHelmChartCmd = &cobra.Command{
flux get sources chart
# List Helm charts from all namespaces
flux get sources chart --all-namespaces
`,
RunE: getCommand{
apiType: helmChartType,
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
}.run,
flux get sources chart --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: helmChartType,
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*sourcev1.HelmChart)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v chart", obj)
}
sink := &helmChartListAdapter{&sourcev1.HelmChartList{
Items: []sourcev1.HelmChart{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -62,3 +90,8 @@ func (a helmChartListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (a helmChartListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := a.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,11 +17,14 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var getSourceGitCmd = &cobra.Command{
@@ -32,12 +35,37 @@ var getSourceGitCmd = &cobra.Command{
flux get sources git
# List Git repositories from all namespaces
flux get sources git --all-namespaces
`,
RunE: getCommand{
apiType: gitRepositoryType,
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
}.run,
flux get sources git --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: gitRepositoryType,
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*sourcev1.GitRepository)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v git", obj)
}
sink := &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{
Items: []sourcev1.GitRepository{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -62,3 +90,8 @@ func (a gitRepositoryListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (a gitRepositoryListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := a.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,11 +17,14 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var getSourceHelmCmd = &cobra.Command{
@@ -32,12 +35,37 @@ var getSourceHelmCmd = &cobra.Command{
flux get sources helm
# List Helm repositories from all namespaces
flux get sources helm --all-namespaces
`,
RunE: getCommand{
apiType: helmRepositoryType,
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
}.run,
flux get sources helm --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
get := getCommand{
apiType: helmRepositoryType,
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
funcMap: make(typeMap),
}
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
o, ok := obj.(*sourcev1.HelmRepository)
if !ok {
return nil, fmt.Errorf("Impossible to cast type %#v helm", obj)
}
sink := &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{
Items: []sourcev1.HelmRepository{
*o,
}}}
return sink, nil
})
if err != nil {
return err
}
if err := get.run(cmd, args); err != nil {
return err
}
return nil
},
}
func init() {
@@ -62,3 +90,8 @@ func (a helmRepositoryListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (a helmRepositoryListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := a.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -17,8 +17,9 @@ limitations under the License.
package main
import (
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
)
// helmv2.HelmRelease
@@ -36,6 +37,10 @@ func (h helmReleaseAdapter) asClientObject() client.Object {
return h.HelmRelease
}
func (h helmReleaseAdapter) deepCopyClientObject() client.Object {
return h.HelmRelease.DeepCopy()
}
// helmv2.HelmReleaseList
type helmReleaseListAdapter struct {

View File

@@ -19,8 +19,8 @@ package main
import (
"sigs.k8s.io/controller-runtime/pkg/client"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
// These are general-purpose adapters for attaching methods to, for
@@ -42,6 +42,10 @@ func (a imageRepositoryAdapter) asClientObject() client.Object {
return a.ImageRepository
}
func (a imageRepositoryAdapter) deepCopyClientObject() client.Object {
return a.ImageRepository.DeepCopy()
}
// imagev1.ImageRepositoryList
type imageRepositoryListAdapter struct {
@@ -100,6 +104,10 @@ func (a imageUpdateAutomationAdapter) asClientObject() client.Object {
return a.ImageUpdateAutomation
}
func (a imageUpdateAutomationAdapter) deepCopyClientObject() client.Object {
return a.ImageUpdateAutomation.DeepCopy()
}
// autov1.ImageUpdateAutomationList
type imageUpdateAutomationListAdapter struct {

View File

@@ -19,7 +19,6 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -51,8 +50,7 @@ If a previous version is installed, then an in-place upgrade will be performed.`
flux install --dry-run --verbose
# Write install manifests to file
flux install --export > flux-system.yaml
`,
flux install --export > flux-system.yaml`,
RunE: installCmdRun,
}
@@ -133,7 +131,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
logger.Generatef("generating manifests")
}
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
tmpDir, err := os.MkdirTemp("", rootArgs.namespace)
if err != nil {
return err
}
@@ -177,15 +175,15 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("install failed: %w", err)
}
if rootArgs.verbose {
fmt.Print(manifest.Content)
} else if installArgs.export {
if installArgs.export {
fmt.Println("---")
fmt.Println("# Flux version:", installArgs.version)
fmt.Println("# Components:", strings.Join(components, ","))
fmt.Print(manifest.Content)
fmt.Println("---")
return nil
} else if rootArgs.verbose {
fmt.Print(manifest.Content)
}
logger.Successf("manifests build completed")

View File

@@ -17,8 +17,9 @@ limitations under the License.
package main
import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
)
// kustomizev1.Kustomization
@@ -36,6 +37,10 @@ func (a kustomizationAdapter) asClientObject() client.Object {
return a.Kustomization
}
func (a kustomizationAdapter) deepCopyClientObject() client.Object {
return a.Kustomization.DeepCopy()
}
// kustomizev1.KustomizationList
type kustomizationListAdapter struct {

View File

@@ -41,6 +41,10 @@ func (l stderrLogger) Successf(format string, a ...interface{}) {
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{}) {
fmt.Fprintln(l.stderr, ``, fmt.Sprintf(format, a...))
}

View File

@@ -23,9 +23,9 @@ import (
"time"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
)
@@ -37,14 +37,15 @@ var rootCmd = &cobra.Command{
SilenceUsage: true,
SilenceErrors: true,
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
flux check --pre
# Install the latest version of Flux
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 \
--url=https://github.com/stefanprodan/podinfo \
--branch=master \
@@ -89,8 +90,7 @@ var rootCmd = &cobra.Command{
flux delete source git webapp-latest
# Uninstall Flux and delete CRDs
flux uninstall
`,
flux uninstall`,
}
var logger = stderrLogger{stderr: os.Stderr}
@@ -111,7 +111,11 @@ func init() {
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().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.DisableAutoGenTag = true
}
func NewRootFlags() rootFlags {
@@ -123,41 +127,36 @@ func NewRootFlags() rootFlags {
return rf
}
type rootContext struct {
kubeManager utils.KubeManager
}
var rootCtx = NewRootContext()
func NewRootContext() rootContext {
var rc rootContext
rc.kubeManager = utils.DefaultKubeManager()
return rc
}
func main() {
log.SetFlags(0)
generateDocs()
kubeconfigFlag()
configureKubeconfig()
if err := rootCmd.Execute(); err != nil {
logger.Failuref("%v", err)
os.Exit(1)
}
}
func kubeconfigFlag() {
if home := homeDir(); home != "" {
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", filepath.Join(home, ".kube", "config"),
"path to the kubeconfig file")
} else {
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "",
"absolute path to the kubeconfig file")
}
if len(os.Getenv("KUBECONFIG")) > 0 {
func configureKubeconfig() {
switch {
case len(rootArgs.kubeconfig) > 0:
case len(os.Getenv("KUBECONFIG")) > 0:
rootArgs.kubeconfig = os.Getenv("KUBECONFIG")
}
}
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)
default:
if home := homeDir(); len(home) > 0 {
rootArgs.kubeconfig = filepath.Join(home, ".kube", "config")
}
os.Exit(0)
}
}

158
cmd/flux/main_test.go Normal file
View File

@@ -0,0 +1,158 @@
package main
import (
"bufio"
"bytes"
"context"
"io"
"os"
"testing"
"github.com/fluxcd/flux2/internal/utils"
"github.com/google/go-cmp/cmp"
"github.com/mattn/go-shellwords"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/controller-runtime/pkg/client"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func TestMain(m *testing.M) {
// Ensure tests print consistent timestamps regardless of timezone
os.Setenv("TZ", "UTC")
os.Exit(m.Run())
}
func readYamlObjects(objectFile string) ([]client.Object, error) {
obj, err := os.ReadFile(objectFile)
if err != nil {
return nil, err
}
objects := []client.Object{}
reader := k8syaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(obj)))
for {
doc, err := reader.Read()
if err != nil {
if err == io.EOF {
break
}
}
unstructuredObj := &unstructured.Unstructured{}
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(doc), len(doc))
err = decoder.Decode(unstructuredObj)
if err != nil {
return nil, err
}
objects = append(objects, unstructuredObj)
}
return objects, nil
}
// A KubeManager that can create objects that are subject to a test.
type fakeKubeManager struct {
fakeClient client.WithWatch
}
func (m *fakeKubeManager) NewClient(kubeconfig string, kubecontext string) (client.WithWatch, error) {
return m.fakeClient, nil
}
func (m *fakeKubeManager) CreateObjects(clientObjects []client.Object) error {
for _, obj := range clientObjects {
err := m.fakeClient.Create(context.Background(), obj)
if err != nil {
return err
}
}
return nil
}
func NewFakeKubeManager() *fakeKubeManager {
c := fakeclient.NewClientBuilder().WithScheme(utils.NewScheme()).Build()
return &fakeKubeManager{
fakeClient: c,
}
}
// Run the command and return the captured output.
func executeCommand(cmd string) (string, error) {
args, err := shellwords.Parse(cmd)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
rootCmd.SetOut(buf)
rootCmd.SetErr(buf)
rootCmd.SetArgs(args)
_, err = rootCmd.ExecuteC()
result := buf.String()
return result, err
}
// Structure used for each test to load objects into kubernetes, run
// commands and assert on the expected output.
type cmdTestCase struct {
// The command line arguments to test.
args string
// When true, the test expects the command to fail.
wantError bool
// String literal that contains the expected test output.
goldenValue string
// Filename that contains the expected test output.
goldenFile string
// Filename that contains yaml objects to load into Kubernetes
objectFile string
}
func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
km := NewFakeKubeManager()
rootCtx.kubeManager = km
if cmd.objectFile != "" {
clientObjects, err := readYamlObjects(cmd.objectFile)
if err != nil {
t.Fatalf("Error loading yaml: '%v'", err)
}
err = km.CreateObjects(clientObjects)
if err != nil {
t.Fatalf("Error creating test objects: '%v'", err)
}
}
actual, err := executeCommand(cmd.args)
if (err != nil) != cmd.wantError {
t.Fatalf("Expected error='%v', Got: %v", cmd.wantError, err)
}
if err != nil {
actual = err.Error()
}
var expected string
if cmd.goldenValue != "" {
expected = cmd.goldenValue
}
if cmd.goldenFile != "" {
expectedOutput, err := os.ReadFile(cmd.goldenFile)
if err != nil {
t.Fatalf("Error reading golden file: '%s'", err)
}
expected = string(expectedOutput)
}
diff := cmp.Diff(expected, actual)
if diff != "" {
t.Errorf("Mismatch from '%s' (-want +got):\n%s", cmd.goldenFile, diff)
}
}
func TestVersion(t *testing.T) {
cmd := cmdTestCase{
args: "--version",
goldenValue: "flux version 0.0.0-dev.0\n",
}
cmd.runTestCmd(t)
}

View File

@@ -39,6 +39,13 @@ type adapter interface {
asClientObject() client.Object
}
// copyable is an interface for a wrapper or alias from which we can
// get a deep copied client.Object, required when you e.g. want to
// calculate a patch.
type copyable interface {
deepCopyClientObject() client.Object
}
// listAdapater is the analogue to adapter, but for lists; the
// controller runtime distinguishes between methods dealing with
// objects and lists.

56
cmd/flux/receiver.go Normal file
View File

@@ -0,0 +1,56 @@
/*
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
}
func (a receiverAdapter) deepCopyClientObject() client.Object {
return a.Receiver.DeepCopy()
}
// 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)
}

View File

@@ -21,7 +21,6 @@ import (
"fmt"
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -30,6 +29,9 @@ import (
"k8s.io/client-go/util/retry"
"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"
)
@@ -50,6 +52,7 @@ type reconcileCommand struct {
type reconcilable interface {
adapter // to be able to load from the cluster
copyable // to be able to calculate patches
suspendable // to tell if it's suspended
// these are implemented by anything embedding metav1.ObjectMeta
@@ -97,12 +100,23 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
}
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()
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) {
@@ -129,6 +143,7 @@ func requestReconciliation(ctx context.Context, kubeClient client.Client,
if err := kubeClient.Get(ctx, namespacedName, obj.asClientObject()); err != nil {
return err
}
patch := client.MergeFrom(obj.deepCopyClientObject())
if ann := obj.GetAnnotations(); ann == nil {
obj.SetAnnotations(map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
@@ -137,6 +152,26 @@ func requestReconciliation(ctx context.Context, kubeClient client.Client,
ann[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
obj.SetAnnotations(ann)
}
return kubeClient.Update(ctx, obj.asClientObject())
return kubeClient.Patch(ctx, obj.asClientObject(), patch)
})
}
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
}
}

View File

@@ -17,16 +17,7 @@ limitations under the License.
package main
import (
"context"
"fmt"
"time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -36,63 +27,17 @@ var reconcileAlertCmd = &cobra.Command{
Short: "Reconcile an Alert",
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
flux reconcile alert main
`,
RunE: reconcileAlertCmdRun,
flux reconcile alert main`,
RunE: reconcileCommand{
apiType: alertType,
object: alertAdapter{&notificationv1.Alert{}},
}.run,
}
func init() {
reconcileCmd.AddCommand(reconcileAlertCmd)
}
func reconcileAlertCmdRun(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 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
func (obj alertAdapter) lastHandledReconcileRequest() string {
return ""
}

View File

@@ -21,14 +21,14 @@ import (
"fmt"
"time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/internal/utils"
)
var reconcileAlertProviderCmd = &cobra.Command{
@@ -36,8 +36,7 @@ var reconcileAlertProviderCmd = &cobra.Command{
Short: "Reconcile a Provider",
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
flux reconcile alert-provider slack
`,
flux reconcile alert-provider slack`,
RunE: reconcileAlertProviderCmdRun,
}

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