1
0
mirror of synced 2026-03-01 19:26:55 +00:00

Compare commits

..

119 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
147 changed files with 3695 additions and 649 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
@@ -61,6 +64,13 @@ jobs:
--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

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
@@ -193,7 +199,7 @@ jobs:
/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
@@ -205,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

@@ -22,13 +22,19 @@ 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
gofish install flux
```
With Bash for macOS and Linux:
```sh
curl -s https://fluxcd.io/install.sh | sudo bash
@@ -46,10 +52,10 @@ 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 container image with `kubectl` and `flux` is available on Docker Hub and GitHub:
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>`

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

View File

@@ -37,6 +37,10 @@ func (a alertAdapter) asClientObject() client.Object {
return a.Alert
}
func (a alertAdapter) deepCopyClientObject() client.Object {
return a.Alert.DeepCopy()
}
// notificationv1.Alert
type alertListAdapter struct {

View File

@@ -37,6 +37,10 @@ func (a alertProviderAdapter) asClientObject() client.Object {
return a.Provider
}
func (a alertProviderAdapter) deepCopyClientObject() client.Object {
return a.Provider.DeepCopy()
}
// notificationv1.Provider
type alertProviderListAdapter struct {

View File

@@ -19,7 +19,7 @@ package main
import (
"crypto/elliptic"
"fmt"
"io/ioutil"
"os"
"github.com/spf13/cobra"
@@ -145,7 +145,7 @@ func buildEmbeddedManifestBase() (string, error) {
if !isEmbeddedVersion(bootstrapArgs.version) {
return "", nil
}
tmpBaseDir, err := ioutil.TempDir("", "flux-manifests-")
tmpBaseDir, err := os.MkdirTemp("", "flux-manifests-")
if err != nil {
return "", err
}

View File

@@ -19,7 +19,6 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
"strings"
@@ -69,6 +68,7 @@ type gitFlags struct {
path flags.SafeRelativePath
username string
password string
silent bool
}
var gitArgs gitFlags
@@ -79,6 +79,7 @@ func init() {
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)
}
@@ -116,7 +117,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
defer os.RemoveAll(manifestsBase)
// Lazy go-git repository
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
if err != nil {
return fmt.Errorf("failed to create temporary working dir: %w", err)
}
@@ -173,7 +174,6 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
// Configure repository URL to match auth config for sync.
repositoryURL.User = url.User(gitArgs.username)
repositoryURL.Scheme = "ssh"
repositoryURL.Host = repositoryURL.Hostname()
if bootstrapArgs.sshHostname != "" {
repositoryURL.Host = bootstrapArgs.sshHostname
}
@@ -248,13 +248,16 @@ func promptPublicKey(ctx context.Context, secret corev1.Secret, _ sourcesecret.O
}
logger.Successf("public key: %s", strings.TrimSpace(ppk))
prompt := promptui.Prompt{
Label: "Please give the key access to your repository",
IsConfirm: true,
}
_, err := prompt.Run()
if err != nil {
return fmt.Errorf("aborting")
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,7 +19,6 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"os"
"time"
@@ -146,7 +145,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
}
// Lazy go-git repository
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
if err != nil {
return fmt.Errorf("failed to create temporary working dir: %w", err)
}

View File

@@ -19,7 +19,6 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"os"
"regexp"
"strings"
@@ -159,7 +158,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
}
// Lazy go-git repository
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
if err != nil {
return fmt.Errorf("failed to create temporary working dir: %w", err)
}

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,7 +87,8 @@ 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
@@ -113,6 +114,7 @@ type helmReleaseFlags struct {
chart string
chartVersion string
targetNamespace string
createNamespace bool
valuesFiles []string
valuesFrom flags.HelmReleaseValuesFrom
saName string
@@ -128,6 +130,7 @@ func init() {
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
createHelmReleaseCmd.Flags().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().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())
@@ -167,6 +170,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
Duration: createArgs.interval,
},
TargetNamespace: helmReleaseArgs.targetNamespace,
Chart: helmv2.HelmChartTemplate{
Spec: helmv2.HelmChartTemplateSpec{
Chart: helmReleaseArgs.chart,
@@ -178,6 +182,9 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
},
},
},
Install: &helmv2.Install{
CreateNamespace: helmReleaseArgs.createNamespace,
},
Suspend: false,
},
}
@@ -187,14 +194,14 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}
if helmReleaseArgs.crds != "" {
helmRelease.Spec.Install = &helmv2.Install{CRDs: helmv2.Create}
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 := ioutil.ReadFile(v)
data, err := os.ReadFile(v)
if err != nil {
return fmt.Errorf("reading values from %s failed: %w", v, err)
}

View File

@@ -28,7 +28,7 @@ import (
"github.com/fluxcd/pkg/apis/meta"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var createImagePolicyCmd = &cobra.Command{

View File

@@ -26,7 +26,7 @@ import (
"github.com/fluxcd/pkg/apis/meta"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var createImageRepositoryCmd = &cobra.Command{

View File

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

View File

@@ -63,19 +63,15 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
--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`,
RunE: createSecretGitCmdRun,
@@ -187,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

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

@@ -109,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"
@@ -112,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
}

View File

@@ -20,7 +20,6 @@ import (
"context"
"crypto/elliptic"
"fmt"
"io/ioutil"
"net/url"
"os"
@@ -178,7 +177,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("recurse submodules requires --git-implementation=%s", sourcev1.GoGitImplementation)
}
tmpDir, err := ioutil.TempDir("", name)
tmpDir, err := os.MkdirTemp("", name)
if err != nil {
return err
}

View File

@@ -19,7 +19,6 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
@@ -66,13 +65,14 @@ For private Helm repositories, the basic authentication credentials are stored i
}
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
@@ -85,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)
}
@@ -104,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
}
@@ -132,6 +133,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
Name: sourceHelmArgs.secretRef,
}
helmRepository.Spec.PassCredentials = sourceHelmArgs.passCredentials
}
if createArgs.export {
@@ -175,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

@@ -148,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
}
@@ -164,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

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

View File

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

View File

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

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/v1alpha2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var exportImagePolicyCmd = &cobra.Command{

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/v1alpha2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var exportImageRepositoryCmd = &cobra.Command{

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/v1alpha2"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
)
var exportImageUpdateCmd = &cobra.Command{

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,10 +17,12 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -32,10 +34,39 @@ var getAlertCmd = &cobra.Command{
Long: "The get alert command prints the statuses of the resources.",
Example: ` # List all Alerts and their status
flux get alerts`,
RunE: getCommand{
apiType: alertType,
list: &alertListAdapter{&notificationv1.AlertList{}},
}.run,
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() {
@@ -55,3 +86,8 @@ func (s alertListAdapter) headers(includeNamespace bool) []string {
}
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,7 +17,10 @@ limitations under the License.
package main
import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -29,10 +32,39 @@ var getAlertProviderCmd = &cobra.Command{
Long: "The get alert-provider command prints the statuses of the resources.",
Example: ` # List all Providers and their status
flux get alert-providers`,
RunE: getCommand{
apiType: alertProviderType,
list: alertProviderListAdapter{&notificationv1.ProviderList{}},
}.run,
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() {
@@ -52,3 +84,8 @@ func (s alertProviderListAdapter) headers(includeNamespace bool) []string {
}
return headers
}
func (s alertProviderListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
item := s.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
}

View File

@@ -36,7 +36,12 @@ var getAllCmd = &cobra.Command{
# List all resources in all namespaces
flux get all --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
err := getSourceAllCmd.RunE(cmd, args)
err := validateWatchOption(cmd, "all")
if err != nil {
return err
}
err = getSourceAllCmd.RunE(cmd, args)
if err != nil {
logError(err)
}

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{
@@ -31,10 +33,36 @@ var getHelmReleaseCmd = &cobra.Command{
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,
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() {
@@ -56,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

@@ -21,8 +21,8 @@ import (
"github.com/spf13/cobra"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var getImageAllCmd = &cobra.Command{
@@ -35,6 +35,11 @@ var getImageAllCmd = &cobra.Command{
# List all image objects in all namespaces
flux get images all --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
err := validateWatchOption(cmd, "all")
if err != nil {
return err
}
var allImageCmd = []getCommand{
{
apiType: imageRepositoryType,

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/v1alpha2"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var getImagePolicyCmd = &cobra.Command{
@@ -31,10 +34,36 @@ var getImagePolicyCmd = &cobra.Command{
# List image policies from all namespaces
flux get image policy --all-namespaces`,
RunE: getCommand{
apiType: imagePolicyType,
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}.run,
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() {
@@ -54,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/v1alpha2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
)
var getImageRepositoryCmd = &cobra.Command{
@@ -35,10 +37,36 @@ var getImageRepositoryCmd = &cobra.Command{
# List image repositories from all namespaces
flux get image repository --all-namespaces`,
RunE: getCommand{
apiType: imageRepositoryType,
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
}.run,
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() {
@@ -63,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/v1alpha2"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
)
var getImageUpdateCmd = &cobra.Command{
@@ -35,10 +37,36 @@ var getImageUpdateCmd = &cobra.Command{
# List image update automations from all namespaces
flux get image update --all-namespaces`,
RunE: getCommand{
apiType: imageUpdateAutomationType,
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
}.run,
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() {
@@ -62,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,10 +17,12 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
)
@@ -32,10 +34,39 @@ var getKsCmd = &cobra.Command{
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,
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,10 +17,12 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -32,10 +34,36 @@ var getReceiverCmd = &cobra.Command{
Long: "The get receiver command prints the statuses of the resources.",
Example: ` # List all Receiver and their status
flux get receivers`,
RunE: getCommand{
apiType: receiverType,
list: receiverListAdapter{&notificationv1.ReceiverList{}},
}.run,
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() {
@@ -55,3 +83,8 @@ func (s receiverListAdapter) headers(includeNamespace bool) []string {
}
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

@@ -34,6 +34,11 @@ var getSourceAllCmd = &cobra.Command{
# List all sources in all namespaces
flux get sources all --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
err := validateWatchOption(cmd, "all")
if err != nil {
return err
}
var allSourceCmd = []getCommand{
{
apiType: bucketType,

View File

@@ -17,10 +17,12 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -34,10 +36,36 @@ var getSourceBucketCmd = &cobra.Command{
# List buckets from all namespaces
flux get sources helm --all-namespaces`,
RunE: getCommand{
apiType: bucketType,
list: &bucketListAdapter{&sourcev1.BucketList{}},
}.run,
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,10 +17,12 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -34,10 +36,36 @@ var getSourceHelmChartCmd = &cobra.Command{
# List Helm charts from all namespaces
flux get sources chart --all-namespaces`,
RunE: getCommand{
apiType: helmChartType,
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
}.run,
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,10 +17,12 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -34,10 +36,36 @@ var getSourceGitCmd = &cobra.Command{
# List Git repositories from all namespaces
flux get sources git --all-namespaces`,
RunE: getCommand{
apiType: gitRepositoryType,
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
}.run,
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,10 +17,12 @@ limitations under the License.
package main
import (
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -34,10 +36,36 @@ var getSourceHelmCmd = &cobra.Command{
# List Helm repositories from all namespaces
flux get sources helm --all-namespaces`,
RunE: getCommand{
apiType: helmRepositoryType,
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
}.run,
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

@@ -37,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/v1alpha2"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
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"
@@ -132,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
}

View File

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

@@ -25,6 +25,7 @@ import (
"github.com/spf13/cobra"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
)
@@ -126,6 +127,18 @@ 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)
configureKubeconfig()

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.

View File

@@ -37,6 +37,10 @@ func (a receiverAdapter) asClientObject() client.Object {
return a.Receiver
}
func (a receiverAdapter) deepCopyClientObject() client.Object {
return a.Receiver.DeepCopy()
}
// notificationv1.Receiver
type receiverListAdapter struct {

View File

@@ -52,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
@@ -142,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),
@@ -150,7 +152,7 @@ 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)
})
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -41,6 +41,10 @@ func (a bucketAdapter) asClientObject() client.Object {
return a.Bucket
}
func (a bucketAdapter) deepCopyClientObject() client.Object {
return a.Bucket.DeepCopy()
}
// sourcev1.BucketList
type bucketListAdapter struct {
@@ -70,6 +74,10 @@ func (a helmChartAdapter) asClientObject() client.Object {
return a.HelmChart
}
func (a helmChartAdapter) deepCopyClientObject() client.Object {
return a.HelmChart.DeepCopy()
}
// sourcev1.HelmChartList
type helmChartListAdapter struct {
@@ -99,6 +107,10 @@ func (a gitRepositoryAdapter) asClientObject() client.Object {
return a.GitRepository
}
func (a gitRepositoryAdapter) deepCopyClientObject() client.Object {
return a.GitRepository.DeepCopy()
}
// sourcev1.GitRepositoryList
type gitRepositoryListAdapter struct {
@@ -128,6 +140,10 @@ func (a helmRepositoryAdapter) asClientObject() client.Object {
return a.HelmRepository
}
func (a helmRepositoryAdapter) deepCopyClientObject() client.Object {
return a.HelmRepository.DeepCopy()
}
// sourcev1.HelmRepositoryList
type helmRepositoryListAdapter struct {

View File

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

View File

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

25
cmd/flux/testdata/trace/deployment.txt vendored Normal file
View File

@@ -0,0 +1,25 @@
Object: deployment/podinfo
Namespace: podinfo
Status: Managed by Flux
---
HelmRelease: podinfo
Namespace: podinfo
Revision: 6.0.0
Status: Last reconciled at 2021-07-16 15:42:20 +0000 UTC
Message: Release reconciliation succeeded
---
HelmChart: podinfo-podinfo
Namespace: flux-system
Chart: podinfo
Version: 6.0.0
Revision: 6.0.0
Status: Last reconciled at 2021-07-16 15:32:09 +0000 UTC
Message: Fetched revision: 6.0.0
---
HelmRepository: podinfo
Namespace: flux-system
URL: https://stefanprodan.github.io/podinfo
Revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
Status: Last reconciled at 2021-07-11 00:25:46 +0000 UTC
Message: Fetched revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56

129
cmd/flux/testdata/trace/deployment.yaml vendored Normal file
View File

@@ -0,0 +1,129 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: podinfo
helm.toolkit.fluxcd.io/name: podinfo
helm.toolkit.fluxcd.io/namespace: podinfo
name: podinfo
namespace: podinfo
spec:
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: podinfo
spec:
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: infrastructure
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: podinfo
namespace: podinfo
spec:
chart:
spec:
chart: podinfo
sourceRef:
kind: HelmRepository
name: podinfo
namespace: flux-system
status:
conditions:
- lastTransitionTime: "2021-07-16T15:42:20Z"
message: Release reconciliation succeeded
reason: ReconciliationSucceeded
status: "True"
type: Ready
helmChart: flux-system/podinfo-podinfo
lastAppliedRevision: 6.0.0
lastAttemptedRevision: 6.0.0
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
---
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmChart
metadata:
name: podinfo-podinfo
namespace: flux-system
spec:
chart: podinfo
sourceRef:
kind: HelmRepository
name: podinfo
version: 6.0.0
status:
artifact:
checksum: cf13ba96773d9a879cd052c86e73199b3f96c854
lastUpdateTime: "2021-08-01T04:42:55Z"
revision: 6.0.0
conditions:
- lastTransitionTime: "2021-07-16T15:32:09Z"
message: 'Fetched revision: 6.0.0'
reason: ChartPullSucceeded
status: "True"
type: Ready
---
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmRepository
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: infrastructure
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: podinfo
namespace: flux-system
spec:
interval: 5m
timeout: 1m0s
url: https://stefanprodan.github.io/podinfo
status:
artifact:
checksum: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
lastUpdateTime: "2021-07-11T00:25:46Z"
revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
conditions:
- lastTransitionTime: "2021-07-11T00:25:46Z"
message: 'Fetched revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56'
reason: IndexationSucceed
status: "True"
type: Ready
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
name: infrastructure
namespace: flux-system
spec:
path: ./infrastructure/
sourceRef:
kind: GitRepository
name: flux-system
validation: client
status:
conditions:
- lastTransitionTime: "2021-08-01T04:52:56Z"
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
reason: ReconciliationSucceeded
status: "True"
type: Ready
---
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: flux-system
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: flux-system
namespace: flux-system
spec:
gitImplementation: go-git
ref:
branch: main
secretRef:
name: flux-system

View File

@@ -0,0 +1,18 @@
Object: HelmRelease/podinfo
Namespace: podinfo
Status: Managed by Flux
---
Kustomization: infrastructure
Namespace: flux-system
Path: ./infrastructure
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
Status: Last reconciled at 2021-08-01 04:52:56 +0000 UTC
Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f
---
GitRepository: flux-system
Namespace: flux-system
URL: ssh://git@github.com/example/repo
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
Status: Last reconciled at 2021-07-20 00:48:16 +0000 UTC
Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f

View File

@@ -0,0 +1,73 @@
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: infrastructure
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: podinfo
namespace: podinfo
spec:
chart:
spec:
chart: podinfo
sourceRef:
kind: HelmRepository
name: podinfo
namespace: flux-system
status:
conditions:
- lastTransitionTime: "2021-07-16T15:42:20Z"
message: Release reconciliation succeeded
reason: ReconciliationSucceeded
status: "True"
type: Ready
helmChart: flux-system/podinfo-podinfo
lastAppliedRevision: 6.0.0
lastAttemptedRevision: 6.0.0
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
name: infrastructure
namespace: flux-system
spec:
path: ./infrastructure
sourceRef:
kind: GitRepository
name: flux-system
validation: client
status:
conditions:
- lastTransitionTime: "2021-08-01T04:52:56Z"
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
reason: ReconciliationSucceeded
status: "True"
type: Ready
lastAppliedRevision: main/696f056df216eea4f9401adbee0ff744d4df390f
---
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: flux-system
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: flux-system
namespace: flux-system
spec:
gitImplementation: go-git
secretRef:
name: flux-system
url: ssh://git@github.com/example/repo
status:
artifact:
lastUpdateTime: "2021-08-01T04:28:42Z"
revision: main/696f056df216eea4f9401adbee0ff744d4df390f
conditions:
- lastTransitionTime: "2021-07-20T00:48:16Z"
message: 'Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
reason: GitOperationSucceed
status: "True"
type: Ready

19
cmd/flux/testdata/trace/helmrelease.txt vendored Normal file
View File

@@ -0,0 +1,19 @@
Object: HelmRelease/podinfo
Namespace: podinfo
Status: Managed by Flux
---
Kustomization: infrastructure
Namespace: flux-system
Path: ./infrastructure
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
Status: Last reconciled at 2021-08-01 04:52:56 +0000 UTC
Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f
---
GitRepository: flux-system
Namespace: flux-system
URL: ssh://git@github.com/example/repo
Branch: main
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
Status: Last reconciled at 2021-07-20 00:48:16 +0000 UTC
Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f

View File

@@ -0,0 +1,75 @@
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: infrastructure
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: podinfo
namespace: podinfo
spec:
chart:
spec:
chart: podinfo
sourceRef:
kind: HelmRepository
name: podinfo
namespace: flux-system
status:
conditions:
- lastTransitionTime: "2021-07-16T15:42:20Z"
message: Release reconciliation succeeded
reason: ReconciliationSucceeded
status: "True"
type: Ready
helmChart: flux-system/podinfo-podinfo
lastAppliedRevision: 6.0.0
lastAttemptedRevision: 6.0.0
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
name: infrastructure
namespace: flux-system
spec:
path: ./infrastructure
sourceRef:
kind: GitRepository
name: flux-system
validation: client
status:
conditions:
- lastTransitionTime: "2021-08-01T04:52:56Z"
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
reason: ReconciliationSucceeded
status: "True"
type: Ready
lastAppliedRevision: main/696f056df216eea4f9401adbee0ff744d4df390f
---
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: flux-system
kustomize.toolkit.fluxcd.io/namespace: flux-system
name: flux-system
namespace: flux-system
spec:
gitImplementation: go-git
ref:
branch: main
secretRef:
name: flux-system
url: ssh://git@github.com/example/repo
status:
artifact:
lastUpdateTime: "2021-08-01T04:28:42Z"
revision: main/696f056df216eea4f9401adbee0ff744d4df390f
conditions:
- lastTransitionTime: "2021-07-20T00:48:16Z"
message: 'Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
reason: GitOperationSucceed
status: "True"
type: Ready

492
cmd/flux/trace.go Normal file
View File

@@ -0,0 +1,492 @@
/*
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 (
"bufio"
"bytes"
"context"
"fmt"
"text/template"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
fluxmeta "github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var traceCmd = &cobra.Command{
Use: "trace [name]",
Short: "trace an in-cluster object throughout the GitOps delivery pipeline",
Long: `The trace command shows how an object is managed by Flux,
from which source and revision it comes, and what's the latest reconciliation status.'`,
Example: ` # Trace a Kubernetes Deployment
flux trace my-app --kind=deployment --api-version=apps/v1 --namespace=apps
# Trace a Kubernetes Pod
flux trace redis-master-0 --kind=pod --api-version=v1 -n redis
# Trace a Kubernetes global object
flux trace redis --kind=namespace --api-version=v1
# Trace a Kubernetes custom resource
flux trace redis --kind=helmrelease --api-version=helm.toolkit.fluxcd.io/v2beta1 -n redis`,
RunE: traceCmdRun,
}
type traceFlags struct {
apiVersion string
kind string
}
var traceArgs = traceFlags{}
func init() {
traceCmd.Flags().StringVar(&traceArgs.kind, "kind", "",
"the Kubernetes object kind, e.g. Deployment'")
traceCmd.Flags().StringVar(&traceArgs.apiVersion, "api-version", "",
"the Kubernetes object API version, e.g. 'apps/v1'")
rootCmd.AddCommand(traceCmd)
}
func traceCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("object name is required")
}
name := args[0]
if traceArgs.kind == "" {
return fmt.Errorf("object kind is required (--kind)")
}
if traceArgs.apiVersion == "" {
return fmt.Errorf("object apiVersion is required (--api-version)")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := rootCtx.kubeManager.NewClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
gv, err := schema.ParseGroupVersion(traceArgs.apiVersion)
if err != nil {
return fmt.Errorf("invaild apiVersion: %w", err)
}
obj := &unstructured.Unstructured{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: gv.Group,
Version: gv.Version,
Kind: traceArgs.kind,
})
objName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
err = kubeClient.Get(ctx, objName, obj)
if err != nil {
return fmt.Errorf("failed to find object: %w", err)
}
if ks, ok := isOwnerManagedByFlux(ctx, kubeClient, obj, kustomizev1.GroupVersion.Group); ok {
report, err := traceKustomization(ctx, kubeClient, ks, obj)
if err != nil {
return err
}
rootCmd.Print(report)
return nil
}
if hr, ok := isOwnerManagedByFlux(ctx, kubeClient, obj, helmv2.GroupVersion.Group); ok {
report, err := traceHelm(ctx, kubeClient, hr, obj)
if err != nil {
return err
}
rootCmd.Print(report)
return nil
}
return fmt.Errorf("object not managed by Flux")
}
func traceKustomization(ctx context.Context, kubeClient client.Client, ksName types.NamespacedName, obj *unstructured.Unstructured) (string, error) {
ks := &kustomizev1.Kustomization{}
ksReady := &metav1.Condition{}
err := kubeClient.Get(ctx, ksName, ks)
if err != nil {
return "", fmt.Errorf("failed to find kustomization: %w", err)
}
ksReady = meta.FindStatusCondition(ks.Status.Conditions, fluxmeta.ReadyCondition)
var ksRepository *sourcev1.GitRepository
var ksRepositoryReady *metav1.Condition
if ks.Spec.SourceRef.Kind == sourcev1.GitRepositoryKind {
ksRepository = &sourcev1.GitRepository{}
sourceNamespace := ks.Namespace
if ks.Spec.SourceRef.Namespace != "" {
sourceNamespace = ks.Spec.SourceRef.Namespace
}
err = kubeClient.Get(ctx, types.NamespacedName{
Namespace: sourceNamespace,
Name: ks.Spec.SourceRef.Name,
}, ksRepository)
if err != nil {
return "", fmt.Errorf("failed to find GitRepository: %w", err)
}
ksRepositoryReady = meta.FindStatusCondition(ksRepository.Status.Conditions, fluxmeta.ReadyCondition)
}
var traceTmpl = `
Object: {{.ObjectName}}
{{- if .ObjectNamespace }}
Namespace: {{.ObjectNamespace}}
{{- end }}
Status: Managed by Flux
{{- if .Kustomization }}
---
Kustomization: {{.Kustomization.Name}}
Namespace: {{.Kustomization.Namespace}}
{{- if .Kustomization.Spec.TargetNamespace }}
Target: {{.Kustomization.Spec.TargetNamespace}}
{{- end }}
Path: {{.Kustomization.Spec.Path}}
Revision: {{.Kustomization.Status.LastAppliedRevision}}
{{- if .KustomizationReady }}
Status: Last reconciled at {{.KustomizationReady.LastTransitionTime}}
Message: {{.KustomizationReady.Message}}
{{- else }}
Status: Unknown
{{- end }}
{{- end }}
{{- if .GitRepository }}
---
GitRepository: {{.GitRepository.Name}}
Namespace: {{.GitRepository.Namespace}}
URL: {{.GitRepository.Spec.URL}}
{{- if .GitRepository.Spec.Reference }}
{{- if .GitRepository.Spec.Reference.Tag }}
Tag: {{.GitRepository.Spec.Reference.Tag}}
{{- else if .GitRepository.Spec.Reference.SemVer }}
Tag: {{.GitRepository.Spec.Reference.SemVer}}
{{- else if .GitRepository.Spec.Reference.Branch }}
Branch: {{.GitRepository.Spec.Reference.Branch}}
{{- end }}
{{- end }}
{{- if .GitRepository.Status.Artifact }}
Revision: {{.GitRepository.Status.Artifact.Revision}}
{{- end }}
{{- if .GitRepositoryReady }}
{{- if eq .GitRepositoryReady.Status "False" }}
Status: Last reconciliation failed at {{.GitRepositoryReady.LastTransitionTime}}
{{- else }}
Status: Last reconciled at {{.GitRepositoryReady.LastTransitionTime}}
{{- end }}
Message: {{.GitRepositoryReady.Message}}
{{- else }}
Status: Unknown
{{- end }}
{{- end }}
`
traceResult := struct {
ObjectName string
ObjectNamespace string
Kustomization *kustomizev1.Kustomization
KustomizationReady *metav1.Condition
GitRepository *sourcev1.GitRepository
GitRepositoryReady *metav1.Condition
}{
ObjectName: obj.GetKind() + "/" + obj.GetName(),
ObjectNamespace: obj.GetNamespace(),
Kustomization: ks,
KustomizationReady: ksReady,
GitRepository: ksRepository,
GitRepositoryReady: ksRepositoryReady,
}
t, err := template.New("tmpl").Parse(traceTmpl)
if err != nil {
return "", err
}
var data bytes.Buffer
writer := bufio.NewWriter(&data)
if err := t.Execute(writer, traceResult); err != nil {
return "", err
}
if err := writer.Flush(); err != nil {
return "", err
}
return data.String(), nil
}
func traceHelm(ctx context.Context, kubeClient client.Client, hrName types.NamespacedName, obj *unstructured.Unstructured) (string, error) {
hr := &helmv2.HelmRelease{}
hrReady := &metav1.Condition{}
err := kubeClient.Get(ctx, hrName, hr)
if err != nil {
return "", fmt.Errorf("failed to find HelmRelease: %w", err)
}
hrReady = meta.FindStatusCondition(hr.Status.Conditions, fluxmeta.ReadyCondition)
var hrChart *sourcev1.HelmChart
var hrChartReady *metav1.Condition
if chart := hr.Status.HelmChart; chart != "" {
hrChart = &sourcev1.HelmChart{}
err = kubeClient.Get(ctx, utils.ParseNamespacedName(chart), hrChart)
if err != nil {
return "", fmt.Errorf("failed to find HelmChart: %w", err)
}
hrChartReady = meta.FindStatusCondition(hrChart.Status.Conditions, fluxmeta.ReadyCondition)
}
var hrGitRepository *sourcev1.GitRepository
var hrGitRepositoryReady *metav1.Condition
if hr.Spec.Chart.Spec.SourceRef.Kind == sourcev1.GitRepositoryKind {
hrGitRepository = &sourcev1.GitRepository{}
sourceNamespace := hr.Namespace
if hr.Spec.Chart.Spec.SourceRef.Namespace != "" {
sourceNamespace = hr.Spec.Chart.Spec.SourceRef.Namespace
}
err = kubeClient.Get(ctx, types.NamespacedName{
Namespace: sourceNamespace,
Name: hr.Spec.Chart.Spec.SourceRef.Name,
}, hrGitRepository)
if err != nil {
return "", fmt.Errorf("failed to find GitRepository: %w", err)
}
hrGitRepositoryReady = meta.FindStatusCondition(hrGitRepository.Status.Conditions, fluxmeta.ReadyCondition)
}
var hrHelmRepository *sourcev1.HelmRepository
var hrHelmRepositoryReady *metav1.Condition
if hr.Spec.Chart.Spec.SourceRef.Kind == sourcev1.HelmRepositoryKind {
hrHelmRepository = &sourcev1.HelmRepository{}
sourceNamespace := hr.Namespace
if hr.Spec.Chart.Spec.SourceRef.Namespace != "" {
sourceNamespace = hr.Spec.Chart.Spec.SourceRef.Namespace
}
err = kubeClient.Get(ctx, types.NamespacedName{
Namespace: sourceNamespace,
Name: hr.Spec.Chart.Spec.SourceRef.Name,
}, hrHelmRepository)
if err != nil {
return "", fmt.Errorf("failed to find HelmRepository: %w", err)
}
hrHelmRepositoryReady = meta.FindStatusCondition(hrHelmRepository.Status.Conditions, fluxmeta.ReadyCondition)
}
var traceTmpl = `
Object: {{.ObjectName}}
{{- if .ObjectNamespace }}
Namespace: {{.ObjectNamespace}}
{{- end }}
Status: Managed by Flux
{{- if .HelmRelease }}
---
HelmRelease: {{.HelmRelease.Name}}
Namespace: {{.HelmRelease.Namespace}}
{{- if .HelmRelease.Spec.TargetNamespace }}
Target: {{.HelmRelease.Spec.TargetNamespace}}
{{- end }}
Revision: {{.HelmRelease.Status.LastAppliedRevision}}
{{- if .HelmReleaseReady }}
Status: Last reconciled at {{.HelmReleaseReady.LastTransitionTime}}
Message: {{.HelmReleaseReady.Message}}
{{- else }}
Status: Unknown
{{- end }}
{{- end }}
{{- if .HelmChart }}
---
HelmChart: {{.HelmChart.Name}}
Namespace: {{.HelmChart.Namespace}}
Chart: {{.HelmChart.Spec.Chart}}
Version: {{.HelmChart.Spec.Version}}
{{- if .HelmChart.Status.Artifact }}
Revision: {{.HelmChart.Status.Artifact.Revision}}
{{- end }}
{{- if .HelmChartReady }}
Status: Last reconciled at {{.HelmChartReady.LastTransitionTime}}
Message: {{.HelmChartReady.Message}}
{{- else }}
Status: Unknown
{{- end }}
{{- end }}
{{- if .HelmRepository }}
---
HelmRepository: {{.HelmRepository.Name}}
Namespace: {{.HelmRepository.Namespace}}
URL: {{.HelmRepository.Spec.URL}}
{{- if .HelmRepository.Status.Artifact }}
Revision: {{.HelmRepository.Status.Artifact.Revision}}
{{- end }}
{{- if .HelmRepositoryReady }}
Status: Last reconciled at {{.HelmRepositoryReady.LastTransitionTime}}
Message: {{.HelmRepositoryReady.Message}}
{{- else }}
Status: Unknown
{{- end }}
{{- end }}
{{- if .GitRepository }}
---
GitRepository: {{.GitRepository.Name}}
Namespace: {{.GitRepository.Namespace}}
URL: {{.GitRepository.Spec.URL}}
{{- if .GitRepository.Spec.Reference }}
{{- if .GitRepository.Spec.Reference.Tag }}
Tag: {{.GitRepository.Spec.Reference.Tag}}
{{- else if .GitRepository.Spec.Reference.SemVer }}
Tag: {{.GitRepository.Spec.Reference.SemVer}}
{{- else if .GitRepository.Spec.Reference.Branch }}
Branch: {{.GitRepository.Spec.Reference.Branch}}
{{- end }}
{{- end }}
{{- if .GitRepository.Status.Artifact }}
Revision: {{.GitRepository.Status.Artifact.Revision}}
{{- end }}
{{- if .GitRepositoryReady }}
{{- if eq .GitRepositoryReady.Status "False" }}
Status: Last reconciliation failed at {{.GitRepositoryReady.LastTransitionTime}}
{{- else }}
Status: Last reconciled at {{.GitRepositoryReady.LastTransitionTime}}
{{- end }}
Message: {{.GitRepositoryReady.Message}}
{{- else }}
Status: Unknown
{{- end }}
{{- end }}
`
traceResult := struct {
ObjectName string
ObjectNamespace string
HelmRelease *helmv2.HelmRelease
HelmReleaseReady *metav1.Condition
HelmChart *sourcev1.HelmChart
HelmChartReady *metav1.Condition
GitRepository *sourcev1.GitRepository
GitRepositoryReady *metav1.Condition
HelmRepository *sourcev1.HelmRepository
HelmRepositoryReady *metav1.Condition
}{
ObjectName: obj.GetKind() + "/" + obj.GetName(),
ObjectNamespace: obj.GetNamespace(),
HelmRelease: hr,
HelmReleaseReady: hrReady,
HelmChart: hrChart,
HelmChartReady: hrChartReady,
GitRepository: hrGitRepository,
GitRepositoryReady: hrGitRepositoryReady,
HelmRepository: hrHelmRepository,
HelmRepositoryReady: hrHelmRepositoryReady,
}
t, err := template.New("tmpl").Parse(traceTmpl)
if err != nil {
return "", err
}
var data bytes.Buffer
writer := bufio.NewWriter(&data)
if err := t.Execute(writer, traceResult); err != nil {
return "", err
}
if err := writer.Flush(); err != nil {
return "", err
}
return data.String(), nil
}
func isManagedByFlux(obj *unstructured.Unstructured, group string) (types.NamespacedName, bool) {
nameKey := fmt.Sprintf("%s/name", group)
namespaceKey := fmt.Sprintf("%s/namespace", group)
namespacedName := types.NamespacedName{}
for k, v := range obj.GetLabels() {
if k == nameKey {
namespacedName.Name = v
}
if k == namespaceKey {
namespacedName.Namespace = v
}
}
if namespacedName.Name == "" {
return namespacedName, false
}
return namespacedName, true
}
func isOwnerManagedByFlux(ctx context.Context, kubeClient client.Client, obj *unstructured.Unstructured, group string) (types.NamespacedName, bool) {
if n, ok := isManagedByFlux(obj, group); ok {
return n, true
}
namespacedName := types.NamespacedName{}
for _, reference := range obj.GetOwnerReferences() {
owner := &unstructured.Unstructured{}
gv, err := schema.ParseGroupVersion(reference.APIVersion)
if err != nil {
return namespacedName, false
}
owner.SetGroupVersionKind(schema.GroupVersionKind{
Group: gv.Group,
Version: gv.Version,
Kind: reference.Kind,
})
ownerName := types.NamespacedName{
Namespace: obj.GetNamespace(),
Name: reference.Name,
}
err = kubeClient.Get(ctx, ownerName, owner)
if err != nil {
return namespacedName, false
}
if n, ok := isManagedByFlux(owner, group); ok {
return n, true
}
if len(owner.GetOwnerReferences()) > 0 {
return isOwnerManagedByFlux(ctx, kubeClient, owner, group)
}
}
return namespacedName, false
}

44
cmd/flux/trace_test.go Normal file
View File

@@ -0,0 +1,44 @@
package main
import (
"testing"
)
func TestTraceNoArgs(t *testing.T) {
cmd := cmdTestCase{
args: "trace",
wantError: true,
goldenValue: "object name is required",
}
cmd.runTestCmd(t)
}
func TestTraceDeployment(t *testing.T) {
cmd := cmdTestCase{
args: "trace podinfo -n podinfo --kind deployment --api-version=apps/v1",
wantError: false,
goldenFile: "testdata/trace/deployment.txt",
objectFile: "testdata/trace/deployment.yaml",
}
cmd.runTestCmd(t)
}
func TestTraceHelmRelease(t *testing.T) {
cmd := cmdTestCase{
args: "trace podinfo -n podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1",
wantError: false,
goldenFile: "testdata/trace/helmrelease.txt",
objectFile: "testdata/trace/helmrelease.yaml",
}
cmd.runTestCmd(t)
}
func TestTraceHelmReleaseMissingGitRef(t *testing.T) {
cmd := cmdTestCase{
args: "trace podinfo -n podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1",
wantError: false,
goldenFile: "testdata/trace/helmrelease-missing-git-ref.txt",
objectFile: "testdata/trace/helmrelease-missing-git-ref.yaml",
}
cmd.runTestCmd(t)
}

39
go.mod
View File

@@ -6,31 +6,32 @@ require (
github.com/Masterminds/semver/v3 v3.1.0
github.com/cyphar/filepath-securejoin v0.2.2
github.com/fluxcd/go-git-providers v0.1.1
github.com/fluxcd/helm-controller/api v0.10.0
github.com/fluxcd/image-automation-controller/api v0.9.1
github.com/fluxcd/image-reflector-controller/api v0.9.1
github.com/fluxcd/kustomize-controller/api v0.12.0
github.com/fluxcd/notification-controller/api v0.13.0
github.com/fluxcd/pkg/apis/meta v0.9.0
github.com/fluxcd/pkg/runtime v0.11.0
github.com/fluxcd/helm-controller/api v0.11.2
github.com/fluxcd/image-automation-controller/api v0.14.1
github.com/fluxcd/image-reflector-controller/api v0.11.1
github.com/fluxcd/kustomize-controller/api v0.13.3
github.com/fluxcd/notification-controller/api v0.15.1
github.com/fluxcd/pkg/apis/meta v0.10.0
github.com/fluxcd/pkg/runtime v0.12.0
github.com/fluxcd/pkg/ssh v0.0.5
github.com/fluxcd/pkg/untar v0.0.5
github.com/fluxcd/pkg/version v0.0.1
github.com/fluxcd/source-controller/api v0.12.1
github.com/go-git/go-git/v5 v5.1.0
github.com/fluxcd/source-controller/api v0.15.4
github.com/go-git/go-git/v5 v5.4.2
github.com/google/go-cmp v0.5.5
github.com/google/go-containerregistry v0.2.0
github.com/manifoldco/promptui v0.7.0
github.com/mattn/go-shellwords v1.0.12
github.com/olekukonko/tablewriter v0.0.4
github.com/spf13/cobra v1.1.1
github.com/spf13/cobra v1.1.3
github.com/spf13/pflag v1.0.5
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
k8s.io/api v0.20.4
k8s.io/apiextensions-apiserver v0.20.4
k8s.io/apimachinery v0.20.4
k8s.io/cli-runtime v0.20.2 // indirect
k8s.io/client-go v0.20.4
sigs.k8s.io/cli-utils v0.22.2
sigs.k8s.io/controller-runtime v0.8.3
sigs.k8s.io/kustomize/api v0.7.4
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
k8s.io/api v0.21.3
k8s.io/apiextensions-apiserver v0.21.3
k8s.io/apimachinery v0.21.3
k8s.io/client-go v0.21.3
sigs.k8s.io/cli-utils v0.25.1-0.20210608181808-f3974341173a
sigs.k8s.io/controller-runtime v0.9.5
sigs.k8s.io/kustomize/api v0.8.10
sigs.k8s.io/yaml v1.2.0
)

619
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,6 @@ package bootstrap
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -160,7 +159,7 @@ func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifest
// Apply components using any existing customisations
kfile := filepath.Join(filepath.Dir(componentsYAML), konfig.DefaultKustomizationFileName())
if _, err := os.Stat(kfile); err == nil {
tmpDir, err := ioutil.TempDir("", "gotk-crds")
tmpDir, err := os.MkdirTemp("", "gotk-crds")
defer os.RemoveAll(tmpDir)
// Extract the CRDs from the components manifest

View File

@@ -0,0 +1,69 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: flux-system
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
name: alerts.notification.toolkit.fluxcd.io
spec:
group: notification.toolkit.fluxcd.io
names:
kind: Alert
listKind: AlertList
plural: alerts
singular: alert
scope: Namespaced
versions:
- name: v1beta1
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
name: buckets.source.toolkit.fluxcd.io
spec:
group: source.toolkit.fluxcd.io
names:
kind: Bucket
listKind: BucketList
plural: buckets
singular: bucket
scope: Namespaced
versions:
- name: v1beta1
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: kustomize-controller
namespace: flux-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: notification-controller
namespace: flux-system

View File

@@ -0,0 +1,17 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: flux-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: kustomize-controller
namespace: flux-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: notification-controller
namespace: flux-system

View File

@@ -17,18 +17,15 @@ limitations under the License.
package utils
import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"text/template"
"github.com/olekukonko/tablewriter"
appsv1 "k8s.io/api/apps/v1"
@@ -38,6 +35,7 @@ import (
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
apiruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
sigyaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
@@ -45,8 +43,8 @@ import (
"sigs.k8s.io/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
imageautov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
imagereflectv1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
imageautov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
imagereflectv1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/runtime/dependency"
@@ -109,36 +107,6 @@ func ExecKubectlCommand(ctx context.Context, mode ExecMode, kubeConfigPath strin
return "", nil
}
func ExecTemplate(obj interface{}, tmpl, filename string) error {
t, err := template.New("tmpl").Parse(tmpl)
if err != nil {
return err
}
var data bytes.Buffer
writer := bufio.NewWriter(&data)
if err := t.Execute(writer, obj); err != nil {
return err
}
if err := writer.Flush(); err != nil {
return err
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
_, err = io.WriteString(file, data.String())
if err != nil {
return err
}
return file.Sync()
}
func KubeConfig(kubeConfigPath string, kubeContext string) (*rest.Config, error) {
configFiles := SplitKubeConfigPath(kubeConfigPath)
configOverrides := clientcmd.ConfigOverrides{}
@@ -156,15 +124,46 @@ func KubeConfig(kubeConfigPath string, kubeContext string) (*rest.Config, error)
return nil, fmt.Errorf("kubernetes configuration load failed: %w", err)
}
// avoid throttling request when some Flux CRDs are not registered
cfg.QPS = 50
cfg.Burst = 100
return cfg, nil
}
func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error) {
// KubeManger creates a Kubernetes client.Client. This interface exists to
// facilitate unit testing and provide a fake client.
type KubeManager interface {
NewClient(string, string) (client.WithWatch, error)
}
type defaultKubeManager struct{}
func DefaultKubeManager() KubeManager {
var manager defaultKubeManager
return manager
}
func (m defaultKubeManager) NewClient(kubeConfigPath string, kubeContext string) (client.WithWatch, error) {
cfg, err := KubeConfig(kubeConfigPath, kubeContext)
if err != nil {
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
}
scheme := NewScheme()
kubeClient, err := client.NewWithWatch(cfg, client.Options{
Scheme: scheme,
})
if err != nil {
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
}
return kubeClient, nil
}
// Create the Scheme, methods for serializing and deserializing API objects
// which can be shared by tests.
func NewScheme() *apiruntime.Scheme {
scheme := apiruntime.NewScheme()
_ = apiextensionsv1.AddToScheme(scheme)
_ = corev1.AddToScheme(scheme)
@@ -177,15 +176,13 @@ func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error
_ = notificationv1.AddToScheme(scheme)
_ = imagereflectv1.AddToScheme(scheme)
_ = imageautov1.AddToScheme(scheme)
return scheme
}
kubeClient, err := client.New(cfg, client.Options{
Scheme: scheme,
})
if err != nil {
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
}
return kubeClient, nil
func KubeClient(kubeConfigPath string, kubeContext string) (client.WithWatch, error) {
m := DefaultKubeManager()
kubeClient, err := m.NewClient(kubeConfigPath, kubeContext)
return kubeClient, err
}
// SplitKubeConfigPath splits the given KUBECONFIG path based on the runtime OS
@@ -221,6 +218,21 @@ func ContainsEqualFoldItemString(s []string, e string) (string, bool) {
return "", false
}
// ParseNamespacedName extracts the NamespacedName of a resource
// based on the '<namespace>/<name>' format
func ParseNamespacedName(input string) types.NamespacedName {
parts := strings.Split(input, "/")
if len(parts) == 2 {
return types.NamespacedName{
Namespace: parts[0],
Name: parts[1],
}
}
return types.NamespacedName{
Name: input,
}
}
// ParseObjectKindName extracts the kind and name of a resource
// based on the '<kind>/<name>' format
func ParseObjectKindName(input string) (kind, name string) {
@@ -235,11 +247,7 @@ func ParseObjectKindName(input string) (kind, name string) {
// ParseObjectKindNameNamespace extracts the kind, name and namespace of a resource
// based on the '<kind>/<name>.<namespace>' format
func ParseObjectKindNameNamespace(input string) (kind, name, namespace string) {
name = input
parts := strings.Split(input, "/")
if len(parts) == 2 {
kind, name = parts[0], parts[1]
}
kind, name = ParseObjectKindName(input)
if nn := strings.Split(name, "."); len(nn) > 1 {
name = strings.Join(nn[:len(nn)-1], ".")
@@ -320,7 +328,7 @@ func CompatibleVersion(binary, target string) bool {
}
func ExtractCRDs(inManifestPath, outManifestPath string) error {
manifests, err := ioutil.ReadFile(inManifestPath)
manifests, err := os.ReadFile(inManifestPath)
if err != nil {
return err
}
@@ -354,5 +362,5 @@ func ExtractCRDs(inManifestPath, outManifestPath string) error {
return fmt.Errorf("no CRDs found in %s", inManifestPath)
}
return ioutil.WriteFile(outManifestPath, []byte(crds), os.ModePerm)
return os.WriteFile(outManifestPath, []byte(crds), os.ModePerm)
}

View File

@@ -16,7 +16,14 @@ limitations under the License.
package utils
import "testing"
import (
"os"
"path/filepath"
"reflect"
"testing"
"github.com/fluxcd/pkg/runtime/dependency"
)
func TestCompatibleVersion(t *testing.T) {
tests := []struct {
@@ -40,3 +47,103 @@ func TestCompatibleVersion(t *testing.T) {
})
}
}
func TestParseObjectKindNameNamespace(t *testing.T) {
tests := []struct {
name string
input string
wantKind string
wantName string
wantNamespace string
}{
{"with kind name namespace", "Kustomization/foo.flux-system", "Kustomization", "foo", "flux-system"},
{"without namespace", "Kustomization/foo", "Kustomization", "foo", ""},
{"name with dots", "Kustomization/foo.bar.flux-system", "Kustomization", "foo.bar", "flux-system"},
{"multiple slashes", "foo/bar/baz", "", "foo/bar/baz", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotKind, gotName, gotNamespace := ParseObjectKindNameNamespace(tt.input)
if gotKind != tt.wantKind {
t.Errorf("kind = %s, want %s", gotKind, tt.wantKind)
}
if gotName != tt.wantName {
t.Errorf("name = %s, want %s", gotName, tt.wantName)
}
if gotNamespace != tt.wantNamespace {
t.Errorf("namespace = %s, want %s", gotNamespace, tt.wantNamespace)
}
})
}
}
func TestMakeDependsOn(t *testing.T) {
input := []string{
"someNSA/someNameA",
"someNSB/someNameB",
"someNameC",
"someNSD/",
"",
}
want := []dependency.CrossNamespaceDependencyReference{
{Namespace: "someNSA", Name: "someNameA"},
{Namespace: "someNSB", Name: "someNameB"},
{Namespace: "", Name: "someNameC"},
{Namespace: "someNSD", Name: ""},
{Namespace: "", Name: ""},
}
got := MakeDependsOn(input)
if !reflect.DeepEqual(got, want) {
t.Errorf("MakeDependsOn() = %v, want %v", got, want)
}
}
func TestValidateComponents(t *testing.T) {
tests := []struct {
name string
input []string
expectErr bool
}{
{"default and extra components", []string{"source-controller", "image-reflector-controller"}, false},
{"unknown components", []string{"some-comp-1", "some-comp-2"}, true},
{"mix of default and unknown", []string{"source-controller", "some-comp-1"}, true},
{"empty", []string{}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := ValidateComponents(tt.input); (err != nil) != tt.expectErr {
t.Errorf("ValidateComponents() error = %v, expectErr %v", err, tt.expectErr)
}
})
}
}
func TestExtractCRDs(t *testing.T) {
tests := []struct {
name string
inManifestFile string
expectErr bool
}{
{"with crds", "components-with-crds.yaml", false},
{"without crds", "components-without-crds.yaml", true},
{"non-existent file", "non-existent-file.yaml", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create temporary directory to write the result in.
dir, err := os.MkdirTemp("", "flux-TestExtractCRDs")
if err != nil {
t.Fatalf("failed to create temporary directory: %v", err)
}
defer os.RemoveAll(dir)
outManifestPath := filepath.Join(dir, "crds.yaml")
inManifestPath := filepath.Join("testdata", tt.inManifestFile)
if err = ExtractCRDs(inManifestPath, outManifestPath); (err != nil) != tt.expectErr {
t.Errorf("ExtractCRDs() error = %v, expectErr %v", err, tt.expectErr)
}
})
}
}

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/helm-controller/releases/download/v0.10.0/helm-controller.crds.yaml
- https://github.com/fluxcd/helm-controller/releases/download/v0.10.0/helm-controller.deployment.yaml
- https://github.com/fluxcd/helm-controller/releases/download/v0.11.2/helm-controller.crds.yaml
- https://github.com/fluxcd/helm-controller/releases/download/v0.11.2/helm-controller.deployment.yaml
- account.yaml
patchesJson6902:
- target:

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.9.1/image-automation-controller.crds.yaml
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.9.1/image-automation-controller.deployment.yaml
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.14.1/image-automation-controller.crds.yaml
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.14.1/image-automation-controller.deployment.yaml
- account.yaml
patchesJson6902:
- target:

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.9.1/image-reflector-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.9.1/image-reflector-controller.deployment.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.11.1/image-reflector-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.11.1/image-reflector-controller.deployment.yaml
- account.yaml
patchesJson6902:
- target:

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.12.0/kustomize-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.12.0/kustomize-controller.deployment.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.13.3/kustomize-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.13.3/kustomize-controller.deployment.yaml
- account.yaml
patchesJson6902:
- target:

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/notification-controller/releases/download/v0.13.0/notification-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v0.13.0/notification-controller.deployment.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v0.15.1/notification-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v0.15.1/notification-controller.deployment.yaml
- account.yaml
patchesJson6902:
- target:

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/source-controller/releases/download/v0.12.1/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v0.12.1/source-controller.deployment.yaml
- https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.deployment.yaml
- account.yaml
patchesJson6902:
- target:

View File

@@ -1,9 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/source-controller/releases/download/v0.12.1/source-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.12.0/kustomize-controller.crds.yaml
- https://github.com/fluxcd/helm-controller/releases/download/v0.10.0/helm-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v0.13.0/notification-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.9.1/image-reflector-controller.crds.yaml
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.9.1/image-automation-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.13.3/kustomize-controller.crds.yaml
- https://github.com/fluxcd/helm-controller/releases/download/v0.11.2/helm-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v0.15.1/notification-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.11.1/image-reflector-controller.crds.yaml
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.14.1/image-automation-controller.crds.yaml

View File

@@ -0,0 +1,14 @@
bases := $(shell dirname $(shell find | grep kustomization.yaml | sort))
all: $(bases)
permutations := $(bases) $(addsuffix /,$(bases))
.PHONY: $(permutations)
$(permutations):
@echo $@
@warnings=$$(kustomize build $@ -o /dev/null 2>&1); \
if [ "$$warnings" ]; then \
echo "$$warnings"; \
false; \
fi

View File

@@ -0,0 +1,32 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: credentials-sync-eventhub
namespace: flux-system
spec:
template:
spec:
initContainers:
- image: bitnami/kubectl
securityContext:
privileged: false
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
name: copy-kubectl
# it's okay to do this because kubectl is a statically linked binary
command:
- sh
- -ceu
- cp $(which kubectl) /kbin/
resources: {}
volumeMounts:
- name: kbin
mountPath: /kbin
containers:
- name: sync
volumeMounts:
- name: kbin
mountPath: /kbin
volumes:
- name: kbin
emptyDir: {}

View File

@@ -0,0 +1,23 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app: credentials-sync-eventhub
resources:
- sync.yaml
patchesStrategicMerge:
- kubectl-patch.yaml
vars:
- name: KUBE_SECRET
objref:
kind: ConfigMap
name: credentials-sync-eventhub
apiVersion: v1
fieldref:
fieldpath: data.KUBE_SECRET
configurations:
- kustomizeconfig.yaml

View File

@@ -0,0 +1,3 @@
varReference:
- path: rules/resourceNames
kind: Role

View File

@@ -0,0 +1,133 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: credentials-sync-eventhub
data:
# Patch this ConfigMap with additional values needed for your cloud
KUBE_SECRET: webhook-url # does not yet exist -- will be created in the same Namespace
ADDRESS: "fluxv2" # the Azure Event Hub name
SYNC_PERIOD: "3600" # tokens expire; refresh faster than that
---
# This Deployment frequently fetches registry tokens and applies them as an imagePullSecret.
# It's done as a 1-replica Deployment rather than a CronJob, because CronJob scheduling can
# block cluster bootstraps and cold-reboots from obtaining registry tokens for a considerable time.
# This deployment will immediately fetch a token, which reduces latency for working image updates.
apiVersion: apps/v1
kind: Deployment
metadata:
name: credentials-sync-eventhub
namespace: flux-system
spec:
replicas: 1
strategy:
type: Recreate
template:
spec:
serviceAccountName: credentials-sync-eventhub
securityContext:
runAsNonRoot: true
runAsUser: 1001
containers:
- image: busybox # override this with a cloud-specific image
name: sync
envFrom:
- configMapRef:
name: credentials-sync-eventhub
env:
- name: RECONCILE_SH # override this env var with a shell function in a kustomize patch
value: |-
reconcile() {
echo reconciling...
}
command:
- bash
- -ceu
- |-
# template reconcile() into the script
# env var is expanded by k8s before the pod starts
$(RECONCILE_SH)
apply-secret() {
/kbin/kubectl create secret generic "${1}" \
--from-literal=token="${2}" \
--from-literal=address="${3}" \
--dry-run=client -o=yaml \
| grep -v "creationTimestamp:" \
| /kbin/kubectl apply -f -
}
pause_loop() {
sleep "${SYNC_PERIOD:-3600}" || true
}
graceful_exit() {
echo "Trapped signal -- $(date)"
job_ids="$(
jobs \
| grep "pause_loop" \
| cut -d] -f1 \
| tr [ %
)"
# shellcheck disable=SC2086
if [ "${job_ids}" ]; then
kill ${job_ids}
fi
wait
echo "Graceful exit -- $(date)"
}
trap graceful_exit INT TERM
echo "Loop started (period: ${SYNC_PERIOD} s) -- $(date)"
while true; do
reconcile & wait $!
pause_loop & wait $!
done
resources: {}
volumeMounts:
- mountPath: /.azure
name: cache-volume
volumes:
- emptyDir: {}
name: cache-volume
# RBAC necessary for our Deployment to apply our secret that will store the JWT token
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: credentials-sync-eventhub
namespace: flux-system
rules:
- apiGroups: [""]
resources:
- secrets
verbs:
- get
- create
- update
- patch
# Lock this down to the specific Secret name (Optional)
#resourceNames:
# - $(KUBE_SECRET) # templated from kustomize vars referencing ConfigMap, also see kustomizeconfig.yaml
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: credentials-sync-eventhub
namespace: flux-system
subjects:
- kind: ServiceAccount
name: credentials-sync-eventhub
roleRef:
kind: Role
name: credentials-sync-eventhub
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: credentials-sync-eventhub
namespace: flux-system

View File

@@ -1,7 +1,7 @@
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: credentials-sync
name: credentials-sync-eventhub
namespace: flux-system
spec:
jobTemplate:

View File

@@ -0,0 +1,23 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app: credentials-sync-eventhub
resources:
- sync.yaml
patchesStrategicMerge:
- kubectl-patch.yaml
vars:
- name: KUBE_SECRET
objref:
kind: ConfigMap
name: credentials-sync-eventhub
apiVersion: v1
fieldref:
fieldpath: data.KUBE_SECRET
configurations:
- kustomizeconfig.yaml

View File

@@ -0,0 +1,3 @@
varReference:
- path: rules/resourceNames
kind: Role

View File

@@ -0,0 +1,109 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: credentials-sync-eventhub
data:
# Patch this ConfigMap with additional values needed for your cloud
KUBE_SECRET: webhook-url # does not yet exist -- will be created in the same Namespace
ADDRESS: "fluxv2" # the Azure Event Hub name
---
# This CronJob frequently fetches registry tokens and applies them as an imagePullSecret.
# note: CronJob scheduling can block cluster bootstraps and cold-reboots from obtaining registry tokens for a considerable time.
# To run the job immediately, do `kubectl create job --from=cronjob/credentials-sync-eventhub -n flux-system credentials-sync-eventhub-init`
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: credentials-sync-eventhub
namespace: flux-system
spec:
suspend: false
schedule: 0 */6 * * *
failedJobsHistoryLimit: 1
successfulJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
serviceAccountName: credentials-sync-eventhub
securityContext:
runAsNonRoot: true
runAsUser: 1001
restartPolicy: Never
containers:
- image: busybox # override this with a cloud-specific image
name: sync
envFrom:
- configMapRef:
name: credentials-sync-eventhub
env:
- name: RECONCILE_SH # override this env var with a shell function in a kustomize patch
value: |-
reconcile() {
echo reconciling...
}
command:
- bash
- -ceu
- |-
# template reconcile() into the script
# env var is expanded by k8s before the pod starts
$(RECONCILE_SH)
apply-secret() {
/kbin/kubectl create secret generic "${1}" \
--from-literal=token="${2}" \
--from-literal=address="${3}" \
--dry-run=client -o=yaml \
| grep -v "creationTimestamp:" \
| /kbin/kubectl apply -f -
}
reconcile
resources: {}
volumeMounts:
- mountPath: /.azure
name: cache-volume
volumes:
- emptyDir: {}
name: cache-volume
# RBAC necessary for our Deployment to apply our secret that will store the JWT token
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: credentials-sync-eventhub
namespace: flux-system
rules:
- apiGroups: [""]
resources:
- secrets
verbs:
- get
- create
- update
- patch
# Lock this down to the specific Secret name (Optional)
resourceNames:
- $(KUBE_SECRET) # templated from kustomize vars referencing ConfigMap, also see kustomizeconfig.yaml
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: credentials-sync-eventhub
namespace: flux-system
subjects:
- kind: ServiceAccount
name: credentials-sync-eventhub
roleRef:
kind: Role
name: credentials-sync-eventhub
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: credentials-sync-eventhub
namespace: flux-system

View File

@@ -0,0 +1,16 @@
# This is a stub resource patched by config-patches.yaml, so that all config is visible in one file
---
apiVersion: aadpodidentity.k8s.io/v1
kind: AzureIdentity
metadata:
name: lab # if this is changed, also change in config-patches.yaml
namespace: flux-system
---
apiVersion: aadpodidentity.k8s.io/v1
kind: AzureIdentityBinding
metadata:
name: lab
namespace: flux-system
spec:
azureIdentity: $(AZ_IDENTITY_NAME) # match the AzureIdentity name
selector: $(AZ_IDENTITY_NAME) # match the AzureIdentity name

View File

@@ -0,0 +1,41 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: credentials-sync-eventhub
data:
KUBE_SECRET: webhook-url # does not yet exist -- will be created in the same Namespace
ADDRESS: "fluxv2" # the Azure Event Hub name
# Create an identity in Azure and assign it a role to write to Azure Event Hub (note: the identity's resourceGroup should match the Azure Event Hub):
# az identity create -n eventhub-write
# az role assignment create --role eventhub --assignee-object-id "$(az identity show -n eventhub-write -o tsv --query principalId)"
# Fetch the clientID and resourceID to configure the AzureIdentity spec below:
# az identity show -n eventhub-write -otsv --query clientId
# az identity show -n eventhub-write -otsv --query resourceId
---
apiVersion: aadpodidentity.k8s.io/v1
kind: AzureIdentity
metadata:
name: lab
namespace: flux-system
spec:
clientID: 82d01fb0-7799-4d9d-92c7-21e7632c0000
resourceID: /subscriptions/82d01fb0-7799-4d9d-92c7-21e7632c0000/resourceGroups/stealthybox/providers/Microsoft.ManagedIdentity/userAssignedIdentities/eventhub-write
type: 0
# Set the reconcile period + specify the pod-identity via the aadpodidbinding label
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: credentials-sync-eventhub
namespace: flux-system
spec:
schedule: 0 * * * * # JWT tokens expire every 24 hours; refresh faster than that
jobTemplate:
spec:
template:
metadata:
labels:
aadpodidbinding: $(AZ_IDENTITY_NAME) # match the AzureIdentity name

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