1
0
mirror of synced 2026-03-02 11:36:56 +00:00

Compare commits

..

126 Commits

Author SHA1 Message Date
Hidde Beydals
64e7a857b8 Merge pull request #1164 from fluxcd/update-components
Update toolkit components
2021-03-26 16:55:26 +01:00
fluxcdbot
7da24932ab Update toolkit components
- helm-controller to v0.9.0
  https://github.com/fluxcd/helm-controller/blob/v0.9.0/CHANGELOG.md
- kustomize-controller to v0.10.0
  https://github.com/fluxcd/kustomize-controller/blob/v0.10.0/CHANGELOG.md
- source-controller to v0.10.0
  https://github.com/fluxcd/source-controller/blob/v0.10.0/CHANGELOG.md
- notification-controller to v0.11.0
  https://github.com/fluxcd/notification-controller/blob/v0.11.0/CHANGELOG.md

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

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

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

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

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-26 09:49:30 +01:00
Hidde Beydals
63a210a0b2 Merge pull request #1149 from SomtochiAma/refactor-cmd 2021-03-25 22:39:46 +01:00
Somtochi Onyekwere
465eaa24d3 Refactor all remaining create, delete, export, get command to use adapter
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-03-25 21:54:58 +01:00
Hidde Beydals
c23e8c7ee1 Merge pull request #1156 from Legion2/patch-1
Fix CRD deletion instruction in Helm Operator migration docs
2021-03-25 18:41:35 +01:00
Leon Kiefer
974f01cb46 fix crd deletion command in v2 migration docs
Signed-off-by: Leon Kiefer <leon.k97@gmx.de>
2021-03-25 18:27:33 +01:00
Stefan Prodan
16fa9b2753 Merge pull request #1150 from fluxcd/dev-guide-update
Update dev guide to controller-runtime v0.8
2021-03-24 15:15:20 +02:00
Stefan Prodan
9deab1c415 Update dev guide to controller-runtime v0.8
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-24 14:59:17 +02:00
Hidde Beydals
7c01eeb115 Merge pull request #1141 from fluxcd/cmd-docs-frontmatter
Add frontmatter to command documentation
2021-03-24 13:53:06 +01:00
Hidde Beydals
998f0c7d53 Add frontmatter to command documentation
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-24 13:31:14 +01:00
Hidde Beydals
bd41406aaa Merge pull request #1134 from kingdonb/fixup-azure-doc 2021-03-22 10:23:15 +01:00
Kingdon Barrett
f17801753d Fixup a broken reference and a typo in Azure doc
Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-03-21 13:53:27 -04:00
Hidde Beydals
92891fd340 Merge pull request #1128 from jestallin/patch-1 2021-03-20 21:22:04 +01:00
Jim Stallings
aa122455f7 Remove branch switch for image update cmd in guide
Signed-off-by: James Stallings <jstallings@constantcontact.com>
2021-03-20 15:35:31 -04:00
Stefan Prodan
880e70c19c Merge pull request #1122 from fluxcd/aws-sops
Add AWS IAM role binding example to SOPS guide
2021-03-19 12:19:42 +02:00
Stefan Prodan
968f249562 Move GOTK diagram to docs/files
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-19 12:02:09 +02:00
Stefan Prodan
bb9f476be2 Fix typo in image automation guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-19 12:01:33 +02:00
Stefan Prodan
276f43fdeb Add AWS IAM role binding example to SOPS guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-19 12:01:01 +02:00
Stefan Prodan
150d1c2a5a Merge pull request #1064 from stealthybox/azure
Document Azure + Flux Installs
2021-03-19 10:47:37 +02:00
leigh capili
1bf3814701 Cleanup note sections
Signed-off-by: leigh capili <leigh@null.net>
2021-03-18 12:29:08 -06:00
leigh capili
166181c745 Add Azure Use-Case doc to new section
Signed-off-by: leigh capili <leigh@null.net>
2021-03-18 12:29:07 -06:00
leigh capili
bfff977d41 Improve Azure DevOps install notes
Signed-off-by: leigh capili <leigh@null.net>
2021-03-18 12:29:07 -06:00
leigh capili
787d755261 Document Flux + SOPS + Azure Key Vault (#851)
Signed-off-by: leigh capili <leigh@null.net>
2021-03-18 12:29:04 -06:00
Hidde Beydals
acf7173959 Merge pull request #1119 from anovateam/azure-acr-secret-reconcile-script 2021-03-18 18:35:21 +01:00
Marco Amador
e6132e36ba fix: revert azure patch and fix the order parameters in the function
Signed-off-by: Marco Amador <amador.marco@gmail.com>
2021-03-18 16:59:17 +00:00
Marco Amador
585b97c462 fix: parameter order
Signed-off-by: Marco Amador <amador.marco@gmail.com>
2021-03-18 15:01:58 +00:00
Stefan Prodan
3dfd0bc2e1 Merge pull request #1117 from fluxcd/update-image-auto-guide]
Add push branch and commit template to image automation guide
2021-03-18 15:52:01 +02:00
Stefan Prodan
10ff99542f Add image update automation diagram
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-18 14:20:01 +02:00
Stefan Prodan
2449030ab8 Add push branch and commit template to image automation guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-18 13:56:42 +02:00
Stefan Prodan
3e85901f40 Merge pull request #1116 from fluxcd/get-all-sources-images
Implement get all for sources and images
2021-03-18 13:30:52 +02:00
Stefan Prodan
73b1576f81 Implement get all for sources and images
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-18 13:00:49 +02:00
Stefan Prodan
cdb5b7c9a2 Merge pull request #1113 from fluxcd/create-image-update
Add repo path and push branch to image update cmd
2021-03-18 12:35:08 +02:00
Stefan Prodan
d9331b0c91 Add repo path and push branch to image update cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-18 11:10:21 +01:00
Stefan Prodan
b6a8163dd9 Add create image policy examples
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-18 11:10:21 +01:00
Stefan Prodan
185252ba48 Update flux logs examples
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-18 11:10:21 +01:00
Stefan Prodan
043d37921b Merge pull request #1091 from joebowbeer/patch-1
Fix deployment name in image update guide
2021-03-18 10:43:04 +02:00
Joe Bowbeer
02fb8d9958 Merge branch 'main' into patch-1 2021-03-17 14:29:42 -07:00
Stefan Prodan
cfa6c0a178 Merge pull request #1095 from fluxcd/update-components
Update toolkit components
2021-03-17 14:58:04 +02:00
fluxcdbot
e8b52bf2fc Update toolkit components
- helm-controller to v0.8.2
  https://github.com/fluxcd/helm-controller/blob/v0.8.2/CHANGELOG.md
- kustomize-controller to v0.9.3
  https://github.com/fluxcd/kustomize-controller/blob/v0.9.3/CHANGELOG.md
- source-controller to v0.9.1
  https://github.com/fluxcd/source-controller/blob/v0.9.1/CHANGELOG.md
- notification-controller to v0.10.0
  https://github.com/fluxcd/notification-controller/blob/v0.10.0/CHANGELOG.md
- image-reflector-controller to v0.7.1
  https://github.com/fluxcd/image-reflector-controller/blob/v0.7.1/CHANGELOG.md
- image-automation-controller to v0.7.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.7.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-03-17 12:45:22 +00:00
Joe Bowbeer
85fbb780bf Update image-update.md
The filename is podinfo-deployment.yaml

Signed-off-by: Joe Bowbeer <joe.bowbeer@gmail.com>
2021-03-16 18:58:38 -07:00
Hidde Beydals
bd9f9bf518 Merge pull request #1103 from fluxcd/build/fix-go-mod-update
Fix updating of `go.mod` entries for components
2021-03-16 16:31:17 +01:00
Hidde Beydals
077860fff1 Fix updating of go.mod entries for components
We noticed that some of our components had not received `go.mod` updates
while they did receive updates for the versions declared in the YAML
manifests.

Was able to trace this back to a behavior change in Go since `1.16.x`,
resulting in it no longer making automated changes to `go.mod` and
`go.sum`[1]. This is an issue for our updater script as it relies
on `go list -m all`, which now after the first `go mod edit` returns:

```console
$ go list -m all
go: github.com/fluxcd/notification-controller/api@v0.10.0: missing
go.sum entry; to add it:
        go mod download github.com/fluxcd/notification-controller/api
```

To work around the issue without having to repeatedly call `go mod
tidy`, I have opted to simply `grep` on the contents of `go.mod` as a
workaround.

[1]: https://blog.golang.org/go116-module-changes#TOC_3.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-16 16:11:29 +01:00
Stefan Prodan
d29a4ee4d2 Merge pull request #1075 from SomtochiAma/formatted-logs
Implement flux logs command
2021-03-16 16:16:36 +02:00
Somtochi Onyekwere
6d2e34e9b2 Add flux logs command
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-03-16 14:59:03 +01:00
Hidde Beydals
0b6969537b Merge pull request #1102 from fluxcd/fix-sshscan-port
Use Host from parsed URL instead of Hostname
2021-03-16 14:17:07 +01:00
Hidde Beydals
dc6b0d0f0d Use Host from parsed URL instead of Hostname
Regression bug introduced in #1001.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-16 14:01:46 +01:00
Hidde Beydals
b4dbb178fe Merge pull request #1098 from kingdonb/fixup-hint-1
Fix hint in Flux v1 Migration guide
2021-03-15 17:33:03 +01:00
Kingdon Barrett
4cf5290989 fixup hint in Flux v1 Migration guide
The wrong indenting means the hint body will not display as a hint

Signed-off-by: Kingdon Barrett <kingdon@weave.works>
2021-03-15 11:50:37 -04:00
Hidde Beydals
6ffd2222c2 Merge pull request #1094 from fluxcd/status-pkg
Move `StatusChecker` to separate and generic pkg
2021-03-15 11:57:52 +01:00
Hidde Beydals
e7725911a7 Move StatusChecker to separate and generic pkg
This commit moves the `StatusChecker` to a separate package, while
making it more generic so that it is able to assess the status of any
given set of `object.ObjMetadata` identifiers.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-15 11:41:02 +01:00
Hidde Beydals
0c1664cd01 Merge pull request #1093 from fluxcd/build-bootstrap-action-changes 2021-03-15 11:33:31 +01:00
Hidde Beydals
0239307d8e Change repository name used in bootstrap tests
This changes the name of the repository that is used for the GitHub
end-to-end tests to a name that is still traceable to the source
(repository) that created it, by using the format
`<ORIGIN_REPOSITORY_NAME>-<PSEUDO_RAND_SUFFIX>`.

The `PSEUDO_RAND_SUFFIX` is a SHA1 sum of the name of the branch and
commit SHA the tests run for, resulting in a 40 character suffix that
unlike the short commit SHA used before, should not result in collisions.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-15 11:20:26 +01:00
Hidde Beydals
9f10b6be1b Replace delete opt on GitHub bootstrap with curl
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-15 11:20:26 +01:00
Stefan Prodan
01f613b39e Merge pull request #1036 from scottrigby/migration-timetable
Migration and Support Timetable
2021-03-14 09:52:20 +02:00
Scott Rigby
b775d11a70 Flux Migration and Support Timetable
* Set explicit column widths for timetable

  No need for old Firefox workaround. It appears fine on FF in 2021

  See:
  - https://github.com/squidfunk/mkdocs-material/issues/922
  - https://github.com/squidfunk/mkdocs-material/issues/118

* Hide TOC right column on migration table page

* SDK->GOTK footnote

* Cross-link admonitions between Roadmap and Timetable

  To-do: change structure and file names under migration menu dir when we
  move to fluxcd/website

* Add custom heart admonition

* Link to documentated deprecation of apiextensions.k8s.io/v1beta1
  CustomResourceDefinition

* Fix caret (^^ underlines short status)

* Initial migration and Support Timetable

  Add mkdocs markdown_extensions and sort them alphabetically

Co-authored-by: Stefan Prodan <stefan.prodan@gmail.com>
Signed-off-by: Scott Rigby <scott@r6by.com>
2021-03-13 22:24:21 -05:00
Stefan Prodan
022576697f Merge pull request #1086 from squat/export_source_typo
cmd/flux/export_source*: fix typo in comment
2021-03-12 14:55:01 +02:00
Lucas Servén Marín
065d0b2c06 cmd/flux/export_source*: fix typo in comment
This commit fixes a small typo in the comments for the export source
commands.

Signed-off-by: Lucas Servén Marín <lserven@gmail.com>
2021-03-12 13:41:46 +01:00
Stefan Prodan
ed4718205a Merge pull request #1065 from fluxcd/build-prerequisites
Add Go 1.16 to prerequisites (contributing doc)
2021-03-12 12:45:24 +02:00
Stefan Prodan
a29d0c536d Add Go 1.16 to prerequisites (contributing doc)
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-12 12:28:43 +02:00
Hidde Beydals
eaeb8ca5c1 Merge pull request #1025 from gliptak/patch-1
Correct ineffassign
2021-03-10 18:12:20 +01:00
Gábor Lipták
2092c14aca Correct ineffassign
Signed-off-by: Gábor Lipták <gliptak@gmail.com>
2021-03-10 17:10:26 +00:00
Stefan Prodan
69f38b8c77 Merge pull request #1027 from stealthybox/sops-gpg-batch
Improve SOPS GPG guide key management
2021-03-10 10:04:40 +02:00
leigh capili
d2cdd02a57 Parameterize GPG key input and fingerprint
Signed-off-by: leigh capili <leigh@null.net>
2021-03-09 17:11:53 -07:00
leigh capili
095c8323a1 Capitalize SOPS, K8s, and Git
Signed-off-by: leigh capili <leigh@null.net>
2021-03-09 17:11:53 -07:00
leigh capili
accb4c915e Improve SOPS GPG guide key management
- Switch to batch GPG key creation
- Accurately name the cluster's decryption key
- Suggest password-manager backup
- Optionally cleanup secret key from generating machine
- Optionally commit the public key to the repo for team members
- Document SOPS limitations decryption required for editing / appending fields

Signed-off-by: leigh capili <leigh@null.net>
2021-03-09 17:11:52 -07:00
Hidde Beydals
242809f61d Merge pull request #1069 from fluxcd/doc-link-fix
docs: fix link to source-controller documentation
2021-03-09 12:15:06 +01:00
Hidde Beydals
c4907cf6c6 docs: fix link to source-controller documentation
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-09 10:16:15 +01:00
Hidde Beydals
a4b9191fa3 Merge pull request #1066 from fluxcd/manifest-bundle-tweaks 2021-03-09 09:14:07 +01:00
Hidde Beydals
5fd3d0bd41 Generate manifests in flux-{go,scm} AUR packages
This commit makes a couple of changes to the `flux-{go,scm}` packages
so that they properly build again:

- The manifests are generated before the compilation of the `flux`
  binary.
- The `makedepends` have been updated to require a version of Go
  `>=1.16` (which is a requirement for `embed`).
- The `makedepends` have been updated to require a `kustomize` version
  `>=3.0` (as we use `transformers`).

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-08 19:06:07 +01:00
Hidde Beydals
ba6da23323 Make manifests dir bundle.sh configurable
There was an assumption in this script that it is always executed in Git
repository/directory, this is however not always true, for example when
one downloads the `.tar.gz` that is made available for every release
by GitHub (and used in one of our AUR packages).

This commit changes this, and makes the first argument of `bundle.sh`
configurable, so a custom manifests directory can always be defined
_without_ relying on Git.

Omitting it, or passing an empty string, will still fall back to the
previous behavior of using `git rev-parse --show-toplevel`.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-08 18:45:05 +01:00
Hidde Beydals
0328bb14ce Merge pull request #1062 from fluxcd/improve-embedded-manifests-build
Improve build process embedded manifests
2021-03-08 16:25:48 +01:00
Hidde Beydals
2b7a0f3fd4 Improve build process embedded manifests
This commit changes the way the build of manifests is triggered by
making smarter use of the capabilities of Make. The result should be
that the manifests are only regenerated if:

1. There is no `cmd/flux/manifests/` directory.
2. There have been made changes to the YAML files in the `manifests/`
   directory that are newer than the files in `cmd/flux/manifests/`.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-08 16:06:44 +01:00
Hidde Beydals
527886bea0 Merge pull request #1060 from fluxcd/aur-pkg-autocomplete-install
Install Bash, Fish, ZSH auto complete in AUR pkgs
2021-03-08 13:35:01 +01:00
Hidde Beydals
98078a0c65 Install Bash, Fish, ZSH auto complete in AUR pkgs
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-08 13:16:50 +01:00
Stefan Prodan
ca660b7ba5 Merge pull request #1048 from fluxcd/restore-key-algorithm-default
Restore default key algorithm flag create source
2021-03-05 15:58:56 +02:00
Hidde Beydals
ed93e93b81 Restore default key algorithm flag create source
This was removed by accident in the PR that introduced the new
`manifestgen` packages, and now restored in full glory.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-05 14:40:02 +01:00
Stefan Prodan
80419f00db Merge pull request #1043 from fluxcd/update-components
Update toolkit components
2021-03-05 12:11:34 +02:00
fluxcdbot
01946facb3 Update toolkit components
- helm-controller to v0.8.1
  https://github.com/fluxcd/helm-controller/blob/v0.8.1/CHANGELOG.md
- kustomize-controller to v0.9.2
  https://github.com/fluxcd/kustomize-controller/blob/v0.9.2/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-03-05 09:47:51 +00:00
Stefan Prodan
08c1bd7653 Merge pull request #1034 from stealthybox/fix-migration-link
Fix anchor on kustomize migration link
2021-03-04 18:23:39 +02:00
leigh capili
ebf9188c6a Fix anchor on kustomize migration link
Signed-off-by: leigh capili <leigh@null.net>
2021-03-04 08:56:22 -07:00
Stefan Prodan
382c6d5885 Merge pull request #1033 from fluxcd/docs-migration-faq
Move the v1 vs v2 FAQ to the migration section
2021-03-04 11:24:46 +02:00
Stefan Prodan
384c60a988 Move the v1 vs v2 FAQ to the migration section
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-04 10:44:57 +02:00
Hidde Beydals
0078147587 Merge pull request #1022 from fluxcd/create-kustomization-path-fix
Use path with '/' slashes in created Kustomization
2021-03-02 11:35:35 +01:00
Hidde Beydals
d79bedf2bc Use path with '/' slashes in created Kustomization
This fixes a bug on Windows where the safe relative path would contain
'\' slashes, which are not compatible with the controller.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-03-02 11:13:19 +01:00
Hidde Beydals
14b31b321c Merge pull request #1008 from fluxcd/helm-faq 2021-03-01 21:20:56 +01:00
Stefan Prodan
309b9b52f8 Add Helm Controller standalone FAQ to migration guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-01 21:07:49 +01:00
Stefan Prodan
5d063e7390 faq: Can I use Flux HelmReleases without GitOps?
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-03-01 21:07:49 +01:00
Hidde Beydals
e7ba9b5624 Merge pull request #1018 from tvories/docs/fish_completions 2021-03-01 17:02:41 +01:00
tvories
81f6fa598f Updated godocs to match readme
Signed-off-by: tvories <taylor@tmtech.me>
2021-03-01 08:49:29 -07:00
tvories
d9eabcdbf7 Removed posix style loading for flux and added .fish filetype to completions file
Signed-off-by: tvories <taylor@tmtech.me>
2021-03-01 08:39:04 -07:00
Hidde Beydals
bb3562427b Merge pull request #1001 from fluxcd/manifestgen-deploysecret-kustomization
Add `sourcesecret` and `kustomization` manifestgen
2021-02-26 17:16:13 +01:00
Hidde Beydals
8a5bba80bf Add sourcesecret and kustomization manifestgen
This includes a change to the `sync` generator to make the deploy
secret name configurable.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-26 16:58:41 +01:00
Stefan Prodan
ff2833c4d1 Merge pull request #993 from fluxcd/air-gapped-install
Document air-gapped install procedure
2021-02-25 19:45:37 +02:00
Stefan Prodan
45ba845f23 Document air-gapped install procedure
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-25 19:23:57 +02:00
Hidde Beydals
771a14fcf6 Merge pull request #998 from fluxcd/update-components
Update image-automation-controller to v0.6.1
2021-02-25 17:55:36 +01:00
fluxcdbot
c8ff861d00 Update toolkit components
- image-automation-controller to v0.6.1
  https://github.com/fluxcd/image-automation-controller/blob/v0.6.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-02-25 16:43:31 +00:00
Hidde Beydals
0f05ce3605 Merge pull request #994 from fluxcd/update-components
Update toolkit components
2021-02-25 15:16:01 +01:00
fluxcdbot
38a3f3ba11 Update toolkit components
- kustomize-controller to v0.9.1
  https://github.com/fluxcd/kustomize-controller/blob/v0.9.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-02-25 14:01:37 +00:00
Hidde Beydals
659a19cd80 Merge pull request #992 from fluxcd/status-poller-fix
Update sigs.k8s.io/cli-utils to v0.22.2
2021-02-25 13:20:47 +01:00
Hidde Beydals
baaa466c0f Update sigs.k8s.io/cli-utils to v0.22.2
This is the first release that includes a patch of the
`CachingClusterReader` so that it continues on all list errors.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-25 12:56:16 +01:00
Stefan Prodan
168c65bb6e Merge pull request #987 from fluxcd/embed-install-manifests
Embed the Kubernetes manifests in flux binary
2021-02-25 13:27:12 +02:00
Stefan Prodan
6003d11156 Embed the install manifests in flux binary
- add make target for generating the install manifests using kustomize
- embed the generated manifests in flux binary
- the install and bootstrap commands default to using the embedded manifests
- download the install manifests from GitHub only if the install/bootstrap version arg is set

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-25 12:53:04 +02:00
Stefan Prodan
1f16b6d639 Merge pull request #988 from fluxcd/setup-go-update-ci
Properly setup Go version in update workflow
2021-02-25 12:16:15 +02:00
Hidde Beydals
54bb4b2efd Properly setup Go version in update workflow
To prevent false `go.mod` modifications.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-25 10:48:09 +01:00
Stefan Prodan
f54770c21a Merge pull request #984 from fluxcd/darwin-arm64-build
Publish flux binary for Apple Silicon
2021-02-25 09:21:27 +02:00
Stefan Prodan
1244a62deb Publish flux binary for Apple Silicon
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-24 23:05:46 +02:00
Stefan Prodan
2fe55bcdde Update Go to v1.16
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-24 23:05:42 +02:00
Hidde Beydals
9943690855 Merge pull request #983 from fluxcd/doc-controller-ver-fix 2021-02-24 19:14:39 +01:00
Hidde Beydals
89c46a6379 Fix controller_version helper func
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-24 18:34:52 +01:00
Hidde Beydals
a0d4530cc0 Merge pull request #982 from fluxcd/update-components-plus-ci 2021-02-24 18:31:50 +01:00
Hidde Beydals
6db84269af Update Toolkit component update script
To recognize and correctly replace the versions in the components'
Kustomization files.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-24 18:17:04 +01:00
Hidde Beydals
2cd3c32ca7 Update toolkit components
This includes a change to the components' Kustomization files to make
use of the YAML multi-doc manifests that are now attached to the GitHub
releases.

- helm-controller to v0.8.0
  https://github.com/fluxcd/helm-controller/blob/v0.8.0/CHANGELOG.md
- kustomize-controller to v0.9.0
  https://github.com/fluxcd/kustomize-controller/blob/v0.9.0/CHANGELOG.md
- source-controller to v0.9.0
  https://github.com/fluxcd/source-controller/blob/v0.9.0/CHANGELOG.md
- notification-controller to v0.9.0
  https://github.com/fluxcd/notification-controller/blob/v0.9.0/CHANGELOG.md
- image-reflector-controller to v0.7.0
  https://github.com/fluxcd/image-reflector-controller/blob/v0.7.0/CHANGELOG.md
- image-automation-controller to v0.6.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.6.0/CHANGELOG.md

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-24 18:16:44 +01:00
Stefan Prodan
9740fecc35 Merge pull request #974 from tarioch/patch-1
Add example podMonitor for prometheus
2021-02-24 18:34:36 +02:00
Patrick Ruckstuhl
433492791b Add example podMonitor for prometheus
Signed-off-by: Patrick Ruckstuhl <patrick@ch.tario.org>
2021-02-24 16:01:00 +00:00
Hidde Beydals
7d3c63ad74 Merge pull request #981 from fluxcd/docs/v1-migration-notes
Add notes about flux bootstrap and feature parity
2021-02-24 13:42:51 +01:00
Hidde Beydals
a6538b117e Add notes about flux bootstrap and feature parity
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-24 13:29:04 +01:00
Stefan Prodan
d54e7559a5 Merge pull request #966 from stealthybox/fixup-az-imgup-kustomize-build
Use git remote-base instead of zip-archive for cloud image-update example
2021-02-23 11:39:59 +02:00
Hidde Beydals
bb9eca7232 Merge pull request #967 from fluxcd/ci/tweak-if-cond
Fix detection of PRs from forks
2021-02-23 10:24:25 +01:00
leigh capili
b5027d8f3f Use git remote-base instead of zip-archive for cloud image-update example
Signed-off-by: leigh capili <leigh@null.net>
2021-02-23 10:24:24 +01:00
Hidde Beydals
00a134e23f Fix detection of PRs from forks
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-23 10:10:03 +01:00
283 changed files with 4384 additions and 3109 deletions

View File

@@ -8,18 +8,20 @@ pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
url="https://fluxcd.io/" url="https://fluxcd.io/"
arch=("x86_64" "armv6h" "armv7h" "aarch64") arch=("x86_64" "armv6h" "armv7h" "aarch64")
license=("APACHE") license=("APACHE")
optdepends=("kubectl") optdepends=('kubectl: for apply actions on the Kubernetes cluster',
'bash-completion: auto-completion for flux in Bash',
'zsh-completions: auto-completion for flux in ZSH')
source_x86_64=( source_x86_64=(
"$pkgname-$pkgver.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_amd64.tar.gz" "${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_amd64.tar.gz"
) )
source_armv6h=( source_armv6h=(
"$pkgname-$pkgver.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm.tar.gz" "${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm.tar.gz"
) )
source_armv7h=( source_armv7h=(
"$pkgname-$pkgver.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm.tar.gz" "${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm.tar.gz"
) )
source_aarch64=( source_aarch64=(
"$pkgname-$pkgver.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm64.tar.gz" "${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm64.tar.gz"
) )
sha256sums_x86_64=( sha256sums_x86_64=(
${SHA256SUM_AMD64} ${SHA256SUM_AMD64}
@@ -33,7 +35,12 @@ sha256sums_armv7h=(
sha256sums_aarch64=( sha256sums_aarch64=(
${SHA256SUM_ARM64} ${SHA256SUM_ARM64}
) )
_srcname=flux
package() { package() {
install -Dm755 flux "$pkgdir/usr/bin/flux" install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
"${pkgdir}/usr/bin/${_srcname}" completion bash | install -Dm644 /dev/stdin "${pkgdir}/usr/share/bash-completion/completions/${_srcname}"
"${pkgdir}/usr/bin/${_srcname}" completion fish | install -Dm644 /dev/stdin "${pkgdir}/usr/share/fish/vendor_completions.d/${_srcname}.fish"
"${pkgdir}/usr/bin/${_srcname}" completion zsh | install -Dm644 /dev/stdin "${pkgdir}/usr/share/zsh/site-functions/_${_srcname}"
} }

View File

@@ -12,32 +12,40 @@ provides=("flux-bin")
conflicts=("flux-bin") conflicts=("flux-bin")
replaces=("flux-cli") replaces=("flux-cli")
depends=("glibc") depends=("glibc")
makedepends=("go") makedepends=('go>=1.16', 'kustomize>=3.0')
optdepends=("kubectl") optdepends=('kubectl: for apply actions on the Kubernetes cluster',
'bash-completion: auto-completion for flux in Bash',
'zsh-completions: auto-completion for flux in ZSH')
source=( source=(
"$pkgname-$pkgver.tar.gz::https://github.com/fluxcd/flux2/archive/v$pkgver.tar.gz" "${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/archive/v${pkgver}.tar.gz"
) )
sha256sums=( sha256sums=(
${SHA256SUM} ${SHA256SUM}
) )
_srcname=flux
build() { build() {
cd "flux2-$pkgver" cd "flux2-${pkgver}"
export CGO_LDFLAGS="$LDFLAGS" export CGO_LDFLAGS="$LDFLAGS"
export CGO_CFLAGS="$CFLAGS" export CGO_CFLAGS="$CFLAGS"
export CGO_CXXFLAGS="$CXXFLAGS" export CGO_CXXFLAGS="$CXXFLAGS"
export CGO_CPPFLAGS="$CPPFLAGS" export CGO_CPPFLAGS="$CPPFLAGS"
export GOFLAGS="-buildmode=pie -trimpath -ldflags=-linkmode=external -mod=readonly -modcacherw" export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
go build -ldflags "-X main.VERSION=$pkgver" -o flux-bin ./cmd/flux ./manifests/scripts/bundle.sh "${PWD}/manifests" "${PWD}/cmd/flux/manifests"
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
} }
check() { check() {
cd "flux2-$pkgver" cd "flux2-${pkgver}"
make test make test
} }
package() { package() {
cd "flux2-$pkgver" cd "flux2-${pkgver}"
install -Dm755 flux-bin "$pkgdir/usr/bin/flux" install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
"${pkgdir}/usr/bin/${_srcname}" completion bash | install -Dm644 /dev/stdin "${pkgdir}/usr/share/bash-completion/completions/${_srcname}"
"${pkgdir}/usr/bin/${_srcname}" completion fish | install -Dm644 /dev/stdin "${pkgdir}/usr/share/fish/vendor_completions.d/${_srcname}.fish"
"${pkgdir}/usr/bin/${_srcname}" completion zsh | install -Dm644 /dev/stdin "${pkgdir}/usr/share/zsh/site-functions/_${_srcname}"
} }

View File

@@ -11,12 +11,15 @@ license=("APACHE")
provides=("flux-bin") provides=("flux-bin")
conflicts=("flux-bin") conflicts=("flux-bin")
depends=("glibc") depends=("glibc")
makedepends=("go") makedepends=('go>=1.16', 'kustomize>=3.0')
optdepends=("kubectl") optdepends=('kubectl: for apply actions on the Kubernetes cluster',
'bash-completion: auto-completion for flux in Bash',
'zsh-completions: auto-completion for flux in ZSH')
source=( source=(
"git+https://github.com/fluxcd/flux2.git" "git+https://github.com/fluxcd/flux2.git"
) )
md5sums=('SKIP') md5sums=('SKIP')
_srcname=flux
pkgver() { pkgver() {
cd "flux2" cd "flux2"
@@ -29,8 +32,9 @@ build() {
export CGO_CFLAGS="$CFLAGS" export CGO_CFLAGS="$CFLAGS"
export CGO_CXXFLAGS="$CXXFLAGS" export CGO_CXXFLAGS="$CXXFLAGS"
export CGO_CPPFLAGS="$CPPFLAGS" export CGO_CPPFLAGS="$CPPFLAGS"
export GOFLAGS="-buildmode=pie -trimpath -ldflags=-linkmode=external -mod=readonly -modcacherw" export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
go build -ldflags "-X main.VERSION=$pkgver" -o flux-bin ./cmd/flux make cmd/flux/manifests
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
} }
check() { check() {
@@ -40,6 +44,10 @@ check() {
package() { package() {
cd "flux2" cd "flux2"
install -Dm755 flux-bin "$pkgdir/usr/bin/flux" install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
"${pkgdir}/usr/bin/${_srcname}" completion bash | install -Dm644 /dev/stdin "${pkgdir}/usr/share/bash-completion/completions/${_srcname}"
"${pkgdir}/usr/bin/${_srcname}" completion fish | install -Dm644 /dev/stdin "${pkgdir}/usr/share/fish/vendor_completions.d/${_srcname}.fish"
"${pkgdir}/usr/bin/${_srcname}" completion zsh | install -Dm644 /dev/stdin "${pkgdir}/usr/share/zsh/site-functions/_${_srcname}"
} }

View File

@@ -9,7 +9,7 @@ on:
jobs: jobs:
github: github:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'fluxcd' }} if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -17,59 +17,69 @@ jobs:
uses: actions/cache@v1 uses: actions/cache@v1
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go- ${{ runner.os }}-go1.16-
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.15.x go-version: 1.16.x
- name: Setup Kubernetes - name: Setup Kubernetes
uses: engineerd/setup-kind@v0.5.0 uses: engineerd/setup-kind@v0.5.0
- name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@main
- name: Build
run: |
make cmd/flux/manifests
go build -o /tmp/flux ./cmd/flux
- name: Set outputs - name: Set outputs
id: vars id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" run: |
- name: Build REPOSITORY_NAME=${{ github.event.repository.name }}
run: sudo go build -o ./bin/flux ./cmd/flux BRANCH_NAME=${GITHUB_REF##*/}
COMMIT_SHA=$(git rev-parse HEAD)
PSEUDO_RAND_SUFFIX=$(echo "${BRANCH_NAME}-${COMMIT_SHA}" | shasum | awk '{print $1}')
TEST_REPO_NAME="${REPOSITORY_NAME}-${PSEUDO_RAND_SUFFIX}"
echo "::set-output name=test_repo_name::$TEST_REPO_NAME"
- name: bootstrap init - name: bootstrap init
run: | run: |
./bin/flux bootstrap github --manifests ./manifests/install/ \ /tmp/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \ --owner=fluxcd-testing \
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \ --repository=${{ steps.vars.outputs.test_repo_name }} \
--branch=main \ --branch=main \
--path=test-cluster --path=test-cluster
env: env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: bootstrap no-op - name: bootstrap no-op
run: | run: |
./bin/flux bootstrap github --manifests ./manifests/install/ \ /tmp/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \ --owner=fluxcd-testing \
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \ --repository=${{ steps.vars.outputs.test_repo_name }} \
--branch=main \ --branch=main \
--path=test-cluster --path=test-cluster
env: env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: uninstall - name: uninstall
run: | run: |
./bin/flux uninstall -s --keep-namespace /tmp/flux uninstall -s --keep-namespace
kubectl delete ns flux-system --timeout=10m --wait=true kubectl delete ns flux-system --timeout=10m --wait=true
- name: bootstrap reinstall - name: bootstrap reinstall
run: | run: |
./bin/flux bootstrap github --manifests ./manifests/install/ \ /tmp/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \ --owner=fluxcd-testing \
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \ --repository=${{ steps.vars.outputs.test_repo_name }} \
--branch=main \ --branch=main \
--path=test-cluster --path=test-cluster
env: env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: delete repository - name: delete repository
run: | run: |
./bin/flux bootstrap github --manifests ./manifests/install/ \ curl \
--owner=fluxcd-testing \ -X DELETE \
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \ -H "Accept: application/vnd.github.v3+json" \
--branch=main \ -H "Authorization: token ${GITHUB_TOKEN}" \
--path=test-cluster \ --fail --silent \
--delete https://api.github.com/repos/fluxcd-testing/${{ steps.vars.outputs.test_repo_name }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: Debug failure - name: Debug failure

View File

@@ -16,7 +16,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
controller_version() { controller_version() {
sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml
} }
{ {

View File

@@ -16,13 +16,13 @@ jobs:
uses: actions/cache@v1 uses: actions/cache@v1
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go- ${{ runner.os }}-go1.16-
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.15.x go-version: 1.16.x
- name: Setup Kubernetes - name: Setup Kubernetes
uses: engineerd/setup-kind@v0.5.0 uses: engineerd/setup-kind@v0.5.0
with: with:
@@ -33,6 +33,8 @@ jobs:
run: | run: |
kubectl apply -f https://docs.projectcalico.org/v3.16/manifests/calico.yaml kubectl apply -f https://docs.projectcalico.org/v3.16/manifests/calico.yaml
kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
- name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@main
- name: Run test - name: Run test
run: make test run: make test
- name: Check if working tree is dirty - name: Check if working tree is dirty
@@ -43,43 +45,44 @@ jobs:
exit 1 exit 1
fi fi
- name: Build - name: Build
run: sudo go build -o ./bin/flux ./cmd/flux run: |
go build -o /tmp/flux ./cmd/flux
- name: flux check --pre - name: flux check --pre
run: | run: |
./bin/flux check --pre /tmp/flux check --pre
- name: flux install --manifests - name: flux install --manifests
run: | run: |
./bin/flux install --manifests ./manifests/install/ /tmp/flux install --manifests ./manifests/install/
- name: flux create secret - name: flux create secret
run: | run: |
./bin/flux create secret git git-ssh-test \ /tmp/flux create secret git git-ssh-test \
--url ssh://git@github.com/stefanprodan/podinfo --url ssh://git@github.com/stefanprodan/podinfo
./bin/flux create secret git git-https-test \ /tmp/flux create secret git git-https-test \
--url https://github.com/stefanprodan/podinfo \ --url https://github.com/stefanprodan/podinfo \
--username=test --password=test --username=test --password=test
./bin/flux create secret helm helm-test \ /tmp/flux create secret helm helm-test \
--username=test --password=test --username=test --password=test
- name: flux create source git - name: flux create source git
run: | run: |
./bin/flux create source git podinfo \ /tmp/flux create source git podinfo \
--url https://github.com/stefanprodan/podinfo \ --url https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.3" --tag-semver=">=3.2.3"
- name: flux create source git export apply - name: flux create source git export apply
run: | run: |
./bin/flux create source git podinfo-export \ /tmp/flux create source git podinfo-export \
--url https://github.com/stefanprodan/podinfo \ --url https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.3" \ --tag-semver=">=3.2.3" \
--export | kubectl apply -f - --export | kubectl apply -f -
./bin/flux delete source git podinfo-export --silent /tmp/flux delete source git podinfo-export --silent
- name: flux get sources git - name: flux get sources git
run: | run: |
./bin/flux get sources git /tmp/flux get sources git
- name: flux get sources git --all-namespaces - name: flux get sources git --all-namespaces
run: | run: |
./bin/flux get sources git --all-namespaces /tmp/flux get sources git --all-namespaces
- name: flux create kustomization - name: flux create kustomization
run: | run: |
./bin/flux create kustomization podinfo \ /tmp/flux create kustomization podinfo \
--source=podinfo \ --source=podinfo \
--path="./deploy/overlays/dev" \ --path="./deploy/overlays/dev" \
--prune=true \ --prune=true \
@@ -90,112 +93,112 @@ jobs:
--health-check-timeout=3m --health-check-timeout=3m
- name: flux reconcile kustomization --with-source - name: flux reconcile kustomization --with-source
run: | run: |
./bin/flux reconcile kustomization podinfo --with-source /tmp/flux reconcile kustomization podinfo --with-source
- name: flux get kustomizations - name: flux get kustomizations
run: | run: |
./bin/flux get kustomizations /tmp/flux get kustomizations
- name: flux get kustomizations --all-namespaces - name: flux get kustomizations --all-namespaces
run: | run: |
./bin/flux get kustomizations --all-namespaces /tmp/flux get kustomizations --all-namespaces
- name: flux suspend kustomization - name: flux suspend kustomization
run: | run: |
./bin/flux suspend kustomization podinfo /tmp/flux suspend kustomization podinfo
- name: flux resume kustomization - name: flux resume kustomization
run: | run: |
./bin/flux resume kustomization podinfo /tmp/flux resume kustomization podinfo
- name: flux export - name: flux export
run: | run: |
./bin/flux export source git --all /tmp/flux export source git --all
./bin/flux export kustomization --all /tmp/flux export kustomization --all
- name: flux delete kustomization - name: flux delete kustomization
run: | run: |
./bin/flux delete kustomization podinfo --silent /tmp/flux delete kustomization podinfo --silent
- name: flux create source helm - name: flux create source helm
run: | run: |
./bin/flux create source helm podinfo \ /tmp/flux create source helm podinfo \
--url https://stefanprodan.github.io/podinfo --url https://stefanprodan.github.io/podinfo
- name: flux create helmrelease --source=HelmRepository/podinfo - name: flux create helmrelease --source=HelmRepository/podinfo
run: | run: |
./bin/flux create hr podinfo-helm \ /tmp/flux create hr podinfo-helm \
--target-namespace=default \ --target-namespace=default \
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo \ --chart=podinfo \
--chart-version=">4.0.0 <5.0.0" --chart-version=">4.0.0 <5.0.0"
- name: flux create helmrelease --source=GitRepository/podinfo - name: flux create helmrelease --source=GitRepository/podinfo
run: | run: |
./bin/flux create hr podinfo-git \ /tmp/flux create hr podinfo-git \
--target-namespace=default \ --target-namespace=default \
--source=GitRepository/podinfo \ --source=GitRepository/podinfo \
--chart=./charts/podinfo --chart=./charts/podinfo
- name: flux reconcile helmrelease --with-source - name: flux reconcile helmrelease --with-source
run: | run: |
./bin/flux reconcile helmrelease podinfo-git --with-source /tmp/flux reconcile helmrelease podinfo-git --with-source
- name: flux get helmreleases - name: flux get helmreleases
run: | run: |
./bin/flux get helmreleases /tmp/flux get helmreleases
- name: flux get helmreleases --all-namespaces - name: flux get helmreleases --all-namespaces
run: | run: |
./bin/flux get helmreleases --all-namespaces /tmp/flux get helmreleases --all-namespaces
- name: flux export helmrelease - name: flux export helmrelease
run: | run: |
./bin/flux export hr --all /tmp/flux export hr --all
- name: flux delete helmrelease podinfo-helm - name: flux delete helmrelease podinfo-helm
run: | run: |
./bin/flux delete hr podinfo-helm --silent /tmp/flux delete hr podinfo-helm --silent
- name: flux delete helmrelease podinfo-git - name: flux delete helmrelease podinfo-git
run: | run: |
./bin/flux delete hr podinfo-git --silent /tmp/flux delete hr podinfo-git --silent
- name: flux delete source helm - name: flux delete source helm
run: | run: |
./bin/flux delete source helm podinfo --silent /tmp/flux delete source helm podinfo --silent
- name: flux delete source git - name: flux delete source git
run: | run: |
./bin/flux delete source git podinfo --silent /tmp/flux delete source git podinfo --silent
- name: flux create tenant - name: flux create tenant
run: | run: |
./bin/flux create tenant dev-team --with-namespace=apps /tmp/flux create tenant dev-team --with-namespace=apps
./bin/flux -n apps create source helm podinfo \ /tmp/flux -n apps create source helm podinfo \
--url https://stefanprodan.github.io/podinfo --url https://stefanprodan.github.io/podinfo
./bin/flux -n apps create hr podinfo-helm \ /tmp/flux -n apps create hr podinfo-helm \
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo \ --chart=podinfo \
--chart-version="5.0.x" \ --chart-version="5.0.x" \
--service-account=dev-team --service-account=dev-team
- name: flux create image repository - name: flux create image repository
run: | run: |
./bin/flux create image repository podinfo \ /tmp/flux create image repository podinfo \
--image=ghcr.io/stefanprodan/podinfo \ --image=ghcr.io/stefanprodan/podinfo \
--interval=1m --interval=1m
- name: flux create image policy - name: flux create image policy
run: | run: |
./bin/flux create image policy podinfo \ /tmp/flux create image policy podinfo \
--image-ref=podinfo \ --image-ref=podinfo \
--interval=1m \ --interval=1m \
--select-semver=5.0.x --select-semver=5.0.x
- name: flux create image policy podinfo-select-alpha - name: flux create image policy podinfo-select-alpha
run: | run: |
./bin/flux create image policy podinfo-alpha \ /tmp/flux create image policy podinfo-alpha \
--image-ref=podinfo \ --image-ref=podinfo \
--interval=1m \ --interval=1m \
--select-alpha=desc --select-alpha=desc
- name: flux get image policy - name: flux get image policy
run: | run: |
./bin/flux get image policy podinfo | grep '5.0.3' /tmp/flux get image policy podinfo | grep '5.0.3'
- name: flux2-kustomize-helm-example - name: flux2-kustomize-helm-example
run: | run: |
./bin/flux create source git flux-system \ /tmp/flux create source git flux-system \
--url=https://github.com/fluxcd/flux2-kustomize-helm-example \ --url=https://github.com/fluxcd/flux2-kustomize-helm-example \
--branch=main --branch=main
./bin/flux create kustomization flux-system \ /tmp/flux create kustomization flux-system \
--source=flux-system \ --source=flux-system \
--path=./clusters/staging --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=2m
- name: flux check - name: flux check
run: | run: |
./bin/flux check /tmp/flux check
- name: flux uninstall - name: flux uninstall
run: | run: |
./bin/flux uninstall --silent /tmp/flux uninstall --silent
- name: Debug failure - name: Debug failure
if: failure() if: failure()
run: | run: |

View File

@@ -2,7 +2,7 @@ name: release
on: on:
push: push:
tags: [ '*' ] tags: [ 'v*' ]
jobs: jobs:
goreleaser: goreleaser:
@@ -15,7 +15,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.15.x go-version: 1.16.x
- name: Download release notes utility - name: Download release notes utility
env: env:
GH_REL_URL: https://github.com/buchanae/github-release-notes/releases/download/0.2.0/github-release-notes-linux-amd64-0.2.0.tar.gz GH_REL_URL: https://github.com/buchanae/github-release-notes/releases/download/0.2.0/github-release-notes-linux-amd64-0.2.0.tar.gz
@@ -28,38 +28,10 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@main uses: fluxcd/pkg//actions/kustomize@main
- name: Generate manifests tarball - name: Generate manifests
run: |
mkdir -p ./output
files=""
# build controllers
for controller in ./manifests/bases/*/; do
output_path="./output/$(basename $controller).yaml"
echo "building $controller to $output_path"
kustomize build $controller > $output_path
files+=" $(basename $output_path)"
done
# build rbac
rbac_path="./manifests/rbac"
rbac_output_path="./output/rbac.yaml"
echo "building $rbac_path to $rbac_output_path"
kustomize build $rbac_path > $rbac_output_path
files+=" $(basename $rbac_output_path)"
# build policies
policies_path="./manifests/policies"
policies_output_path="./output/policies.yaml"
echo "building $policies_path to $policies_output_path"
kustomize build $policies_path > $policies_output_path
files+=" $(basename $policies_output_path)"
# create tarball
cd ./output && tar -cvzf manifests.tar.gz $files
- name: Generate install manifest
run: | run: |
make cmd/flux/manifests
./manifests/scripts/bundle.sh "" ./output manifests.tar.gz
kustomize build ./manifests/install > ./output/install.yaml kustomize build ./manifests/install > ./output/install.yaml
- name: Run GoReleaser - name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1 uses: goreleaser/goreleaser-action@v1

View File

@@ -24,9 +24,14 @@ jobs:
snyk: snyk:
name: Snyk name: Snyk
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'fluxcd' }} if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@main
- name: Build manifests
run: |
make cmd/flux/manifests
- name: Run Snyk to check for vulnerabilities - name: Run Snyk to check for vulnerabilities
uses: snyk/actions/golang@master uses: snyk/actions/golang@master
continue-on-error: true continue-on-error: true

View File

@@ -13,7 +13,10 @@ jobs:
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.16.x
- name: Update component versions - name: Update component versions
id: update id: update
run: | run: |
@@ -21,13 +24,13 @@ jobs:
bump_version() { bump_version() {
local RELEASE_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name') local RELEASE_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
local CURRENT_VERSION=$(sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml) local CURRENT_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
if [[ "${RELEASE_VERSION}" != "${CURRENT_VERSION}" ]]; then if [[ "${RELEASE_VERSION}" != "${CURRENT_VERSION}" ]]; then
# bump kustomize # bump kustomize
sed -i "s/\($1\/archive\/\)v.*\(.zip\/\/$1-\).*\(\/config.*\)/\1${RELEASE_VERSION}\2${RELEASE_VERSION/v}\3/g" "manifests/bases/$1/kustomization.yaml" sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${RELEASE_VERSION}\2/g" "manifests/bases/$1/kustomization.yaml"
if [[ ! -z $(go list -m all | grep "github.com/fluxcd/$1/api" | awk '{print $2}') ]]; then if [[ ! -z $(grep "github.com/fluxcd/$1/api" go.mod | awk '{print $2}') ]]; then
# bump go mod # bump go mod
go mod edit -require="github.com/fluxcd/$1/api@${RELEASE_VERSION}" go mod edit -require="github.com/fluxcd/$1/api@${RELEASE_VERSION}"
fi fi

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@
# vendor/ # vendor/
bin/ bin/
output/ output/
cmd/flux/manifests/

View File

@@ -22,6 +22,7 @@ builds:
- darwin - darwin
goarch: goarch:
- amd64 - amd64
- arm64
- <<: *build_defaults - <<: *build_defaults
id: windows id: windows
goos: goos:

View File

@@ -48,11 +48,13 @@ you might want to take a look at the [introductory talk and demo](https://www.yo
This project is composed of: This project is composed of:
- [/f/flux2](https://github.com/fluxcd/flux2): The Flux CLI - [flux2](https://github.com/fluxcd/flux2): The Flux CLI
- [/f/source-manager](https://github.com/fluxcd/source-controller): Kubernetes operator for managing sources - [source-manager](https://github.com/fluxcd/source-controller): Kubernetes operator for managing sources (Git and Helm repositories, S3-compatible Buckets)
- [/f/kustomize-controller](https://github.com/fluxcd/kustomize-controller): Kubernetes operator for building GitOps pipelines with Kustomize - [kustomize-controller](https://github.com/fluxcd/kustomize-controller): Kubernetes operator for building GitOps pipelines with Kustomize
- [/f/helm-controller](https://github.com/fluxcd/helm-controller): Kubernetes operator for building GitOps pipelines with Helm - [helm-controller](https://github.com/fluxcd/helm-controller): Kubernetes operator for building GitOps pipelines with Helm
- [/f/notification-controller](https://github.com/fluxcd/notification-controller): Kubernetes operator for handling inbound and outbound events - [notification-controller](https://github.com/fluxcd/notification-controller): Kubernetes operator for handling inbound and outbound events
- [image-reflector-controller](https://github.com/fluxcd/image-reflector-controller): Kubernetes operator for scanning container registries
- [image-automation-controller](https://github.com/fluxcd/image-automation-controller): Kubernetes operator for patches container image tags in Git
### Understanding the code ### Understanding the code
@@ -63,6 +65,12 @@ for source changes.
### How to run the test suite ### How to run the test suite
Prerequisites:
* go >= 1.16
* kubectl >= 1.18
* kustomize >= 3.1
You can run the unit tests by simply doing You can run the unit tests by simply doing
```bash ```bash

View File

@@ -1,4 +1,7 @@
VERSION?=$(shell grep 'VERSION' cmd/flux/main.go | awk '{ print $$4 }' | tr -d '"') VERSION?=$(shell grep 'VERSION' cmd/flux/main.go | awk '{ print $$4 }' | tr -d '"')
EMBEDDED_MANIFESTS_TARGET=cmd/flux/manifests
rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2)) $(filter $(subst *,%,$(2)),$(d)))
all: test build all: test build
@@ -11,10 +14,13 @@ fmt:
vet: vet:
go vet ./... go vet ./...
test: tidy fmt vet docs test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet docs
go test ./... -coverprofile cover.out go test ./... -coverprofile cover.out
build: $(EMBEDDED_MANIFESTS_TARGET): $(call rwildcard,manifests/,*.yaml *.json)
./manifests/scripts/bundle.sh
build: $(EMBEDDED_MANIFESTS_TARGET)
CGO_ENABLED=0 go build -o ./bin/flux ./cmd/flux CGO_ENABLED=0 go build -o ./bin/flux ./cmd/flux
install: install:
@@ -22,7 +28,7 @@ install:
.PHONY: docs .PHONY: docs
docs: docs:
rm docs/cmd/* rm -rf docs/cmd/*
mkdir -p ./docs/cmd && go run ./cmd/flux/ docgen mkdir -p ./docs/cmd && go run ./cmd/flux/ docgen
install-dev: install-dev:

View File

@@ -74,7 +74,7 @@ runtime for Flux v2. The APIs comprise Kubernetes custom resources,
which can be created and updated by a cluster user, or by other which can be created and updated by a cluster user, or by other
automation tooling. automation tooling.
![overview](docs/diagrams/gitops-toolkit.png) ![overview](docs/_files/gitops-toolkit.png)
You can use the toolkit to extend Flux, or to build your own systems You can use the toolkit to extend Flux, or to build your own systems
for continuous delivery -- see [the developer for continuous delivery -- see [the developer

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

@@ -0,0 +1,51 @@
/*
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 (
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// notificationv1.Alert
var alertType = apiType{
kind: notificationv1.AlertKind,
humanKind: "alert",
}
type alertAdapter struct {
*notificationv1.Alert
}
func (a alertAdapter) asClientObject() client.Object {
return a.Alert
}
// notificationv1.Alert
type alertListAdapter struct {
*notificationv1.AlertList
}
func (a alertListAdapter) asClientList() client.ObjectList {
return a.AlertList
}
func (a alertListAdapter) len() int {
return len(a.AlertList.Items)
}

View File

@@ -0,0 +1,51 @@
/*
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 (
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// notificationv1.Provider
var alertProviderType = apiType{
kind: notificationv1.ProviderKind,
humanKind: "alert provider",
}
type alertProviderAdapter struct {
*notificationv1.Provider
}
func (a alertProviderAdapter) asClientObject() client.Object {
return a.Provider
}
// notificationv1.Provider
type alertProviderListAdapter struct {
*notificationv1.ProviderList
}
func (a alertProviderListAdapter) asClientList() client.ObjectList {
return a.ProviderList
}
func (a alertProviderListAdapter) len() int {
return len(a.ProviderList.Items)
}

View File

@@ -19,13 +19,11 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"net/url"
"path/filepath" "path/filepath"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
@@ -36,7 +34,9 @@ import (
"github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install" "github.com/fluxcd/flux2/pkg/manifestgen/install"
kus "github.com/fluxcd/flux2/pkg/manifestgen/kustomization"
"github.com/fluxcd/flux2/pkg/manifestgen/sync" "github.com/fluxcd/flux2/pkg/manifestgen/sync"
"github.com/fluxcd/flux2/pkg/status"
) )
var bootstrapCmd = &cobra.Command{ var bootstrapCmd = &cobra.Command{
@@ -70,8 +70,8 @@ const (
var bootstrapArgs = NewBootstrapFlags() var bootstrapArgs = NewBootstrapFlags()
func init() { func init() {
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", rootArgs.defaults.Version, bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "",
"toolkit version") "toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
@@ -126,23 +126,18 @@ func bootstrapValidate() error {
} }
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) { func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
if bootstrapArgs.version == install.MakeDefaultOptions().Version { if ver, err := getVersion(bootstrapArgs.version); err != nil {
version, err := install.GetLatestVersion() return "", err
if err != nil {
return "", err
}
bootstrapArgs.version = version
} else { } else {
if ok, err := install.ExistingVersion(bootstrapArgs.version); err != nil || !ok { bootstrapArgs.version = ver
if err == nil {
err = fmt.Errorf("targeted version '%s' does not exist", bootstrapArgs.version)
}
return "", err
}
} }
if !utils.CompatibleVersion(VERSION, bootstrapArgs.version) { manifestsBase := ""
return "", fmt.Errorf("targeted version '%s' is not compatible with your current version of flux (%s)", bootstrapArgs.version, VERSION) if isEmbeddedVersion(bootstrapArgs.version) {
if err := writeEmbeddedManifests(tmpDir); err != nil {
return "", err
}
manifestsBase = tmpDir
} }
opts := install.Options{ opts := install.Options{
@@ -167,7 +162,7 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
opts.BaseURL = rootArgs.defaults.BaseURL opts.BaseURL = rootArgs.defaults.BaseURL
} }
output, err := install.Generate(opts) output, err := install.Generate(opts, manifestsBase)
if err != nil { if err != nil {
return "", fmt.Errorf("generating install manifests failed: %w", err) return "", fmt.Errorf("generating install manifests failed: %w", err)
} }
@@ -182,19 +177,24 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error { func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
kubectlArgs := []string{"apply", "-f", manifestPath} kubectlArgs := []string{"apply", "-f", manifestPath}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil { if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed") return fmt.Errorf("install failed: %w", err)
} }
kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
statusChecker, err := NewStatusChecker(time.Second, rootArgs.timeout) if err != nil {
return fmt.Errorf("install failed: %w", err)
}
statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
componentRefs, err := buildComponentObjectRefs(components...)
if err != nil { if err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }
logger.Waitingf("verifying installation") logger.Waitingf("verifying installation")
if err := statusChecker.Assess(components...); err != nil { if err := statusChecker.Assess(componentRefs...); err != nil {
return fmt.Errorf("install failed") return fmt.Errorf("install failed")
} }
return nil return nil
} }
@@ -205,6 +205,7 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
URL: url, URL: url,
Branch: branch, Branch: branch,
Interval: interval, Interval: interval,
Secret: namespace,
TargetPath: targetPath, TargetPath: targetPath,
ManifestFile: sync.MakeDefaultOptions().ManifestFile, ManifestFile: sync.MakeDefaultOptions().ManifestFile,
} }
@@ -219,9 +220,19 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
return "", err return "", err
} }
outputDir := filepath.Dir(output) outputDir := filepath.Dir(output)
if err := utils.GenerateKustomizationYaml(outputDir); err != nil {
kusOpts := kus.MakeDefaultOptions()
kusOpts.BaseDir = tmpDir
kusOpts.TargetPath = filepath.Dir(manifest.Path)
kustomization, err := kus.Generate(kusOpts)
if err != nil {
return "", err return "", err
} }
if _, err = kustomization.WriteFile(tmpDir); err != nil {
return "", err
}
return outputDir, nil return outputDir, nil
} }
@@ -274,35 +285,6 @@ func shouldCreateDeployKey(ctx context.Context, kubeClient client.Client, namesp
return false return false
} }
func generateDeployKey(ctx context.Context, kubeClient client.Client, url *url.URL, namespace string) (string, error) {
pair, err := generateKeyPair(ctx, sourceGitArgs.keyAlgorithm, sourceGitArgs.keyRSABits, sourceGitArgs.keyECDSACurve)
if err != nil {
return "", err
}
hostKey, err := scanHostKey(ctx, url)
if err != nil {
return "", err
}
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
Namespace: namespace,
},
StringData: map[string]string{
"identity": string(pair.PrivateKey),
"identity.pub": string(pair.PublicKey),
"known_hosts": string(hostKey),
},
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return "", err
}
return string(pair.PublicKey), nil
}
func checkIfBootstrapPathDiffers(ctx context.Context, kubeClient client.Client, namespace string, path string) (string, bool) { func checkIfBootstrapPathDiffers(ctx context.Context, kubeClient client.Client, namespace string, path string) (string, bool) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Name: namespace, Name: namespace,

View File

@@ -26,14 +26,14 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/fluxcd/pkg/git"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml"
"github.com/fluxcd/pkg/git"
"github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
) )
var bootstrapGitHubCmd = &cobra.Command{ var bootstrapGitHubCmd = &cobra.Command{
@@ -47,27 +47,26 @@ the bootstrap command will perform an upgrade if needed.`,
Example: ` # Create a GitHub personal access token and export it as an env var Example: ` # Create a GitHub personal access token and export it as an env var
export GITHUB_TOKEN=<my-token> export GITHUB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitHub organization # Run bootstrap for a private repository owned by a GitHub organization
flux bootstrap github --owner=<organization> --repository=<repo name> flux bootstrap github --owner=<organization> --repository=<repository name>
# Run bootstrap for a private repo and assign organization teams to it # Run bootstrap for a private repository and assign organization teams to it
flux bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug> flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug> --team=<team2 slug>
# Run bootstrap for a repository path # Run bootstrap for a repository path
flux bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster flux bootstrap github --owner=<organization> --repository=<repository name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account # Run bootstrap for a public repository on a personal account
flux bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true flux bootstrap github --owner=<user> --repository=<repository name> --private=false --personal=true
# Run bootstrap for a private repo hosted on GitHub Enterprise using SSH auth # Run bootstrap for a private repository hosted on GitHub Enterprise using SSH auth
flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> --ssh-hostname=<domain> flux bootstrap github --owner=<organization> --repository=<repository name> --hostname=<domain> --ssh-hostname=<domain>
# Run bootstrap for a private repo hosted on GitHub Enterprise using HTTPS auth # Run bootstrap for a private repository hosted on GitHub Enterprise using HTTPS auth
flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> --token-auth flux bootstrap github --owner=<organization> --repository=<repository name> --hostname=<domain> --token-auth
# Run bootstrap for a an existing repository with a branch named main # Run bootstrap for an existing repository with a branch named main
flux bootstrap github --owner=<organization> --repository=<repo name> --branch=main flux bootstrap github --owner=<organization> --repository=<repository name> --branch=main`,
`,
RunE: bootstrapGitHubCmdRun, RunE: bootstrapGitHubCmdRun,
} }
@@ -80,7 +79,6 @@ type githubFlags struct {
hostname string hostname string
path flags.SafeRelativePath path flags.SafeRelativePath
teams []string teams []string
delete bool
sshHostname string sshHostname string
} }
@@ -101,9 +99,6 @@ func init() {
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one") bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitHubCmd.Flags().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path") bootstrapGitHubCmd.Flags().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.delete, "delete", false, "delete repository (used for testing only)")
bootstrapGitHubCmd.Flags().MarkHidden("delete")
bootstrapCmd.AddCommand(bootstrapGitHubCmd) bootstrapCmd.AddCommand(bootstrapGitHubCmd)
} }
@@ -163,14 +158,6 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
if githubArgs.delete {
if err := provider.DeleteRepository(ctx, repository); err != nil {
return err
}
logger.Successf("repository deleted")
return nil
}
// create GitHub repository if doesn't exists // create GitHub repository if doesn't exists
logger.Actionf("connecting to %s", githubArgs.hostname) logger.Actionf("connecting to %s", githubArgs.hostname)
changed, err := provider.CreateRepository(ctx, repository) changed, err := provider.CreateRepository(ctx, repository)
@@ -232,7 +219,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("components are up to date") logger.Successf("components are up to date")
} }
// determine if repo synchronization is working // determine if repository synchronization is working
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace) isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace)
if isInstall { if isInstall {
@@ -244,44 +231,48 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("install completed") logger.Successf("install completed")
} }
repoURL := repository.GetURL() repoURL := repository.GetSSH()
secretOpts := sourcesecret.Options{
Name: rootArgs.namespace,
Namespace: rootArgs.namespace,
}
if bootstrapArgs.tokenAuth { if bootstrapArgs.tokenAuth {
// setup HTTPS token auth // Setup HTTPS token auth
secret := corev1.Secret{ repoURL = repository.GetURL()
ObjectMeta: metav1.ObjectMeta{ secretOpts.Username = "git"
Name: rootArgs.namespace, secretOpts.Password = ghToken
Namespace: rootArgs.namespace, } else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
}, // Setup SSH auth
StringData: map[string]string{ u, err := url.Parse(repoURL)
"username": "git", if err != nil {
"password": ghToken, return fmt.Errorf("git URL parse failed: %w", err)
},
} }
if err := upsertSecret(ctx, kubeClient, secret); err != nil { secretOpts.SSHHostname = u.Host
secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm
secretOpts.RSAKeyBits = 2048
}
secret, err := sourcesecret.Generate(secretOpts)
if err != nil {
return err
}
var s corev1.Secret
if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if len(s.StringData) > 0 {
logger.Actionf("configuring deploy key")
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err return err
} }
} else {
// setup SSH deploy key
repoURL = repository.GetSSH()
if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
logger.Actionf("configuring deploy key")
u, err := url.Parse(repository.GetSSH())
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
key, err := generateDeployKey(ctx, kubeClient, u, rootArgs.namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
}
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
keyName := "flux" keyName := "flux"
if githubArgs.path != "" { if githubArgs.path != "" {
keyName = fmt.Sprintf("flux-%s", githubArgs.path) keyName = fmt.Sprintf("flux-%s", githubArgs.path)
} }
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil { if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil {
return err return err
} else if changed { } else if changed {
logger.Successf("deploy key configured") logger.Successf("deploy key configured")
@@ -289,7 +280,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
// configure repo synchronization // configure repository synchronization
logger.Actionf("generating sync manifests") logger.Actionf("generating sync manifests")
syncManifests, err := generateSyncManifests( syncManifests, err := generateSyncManifests(
repoURL, repoURL,

View File

@@ -29,12 +29,13 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml"
"github.com/fluxcd/pkg/git" "github.com/fluxcd/pkg/git"
"github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
) )
var bootstrapGitLabCmd = &cobra.Command{ var bootstrapGitLabCmd = &cobra.Command{
@@ -48,24 +49,23 @@ the bootstrap command will perform an upgrade if needed.`,
Example: ` # Create a GitLab API token and export it as an env var Example: ` # Create a GitLab API token and export it as an env var
export GITLAB_TOKEN=<my-token> export GITLAB_TOKEN=<my-token>
# Run bootstrap for a private repo using HTTPS token authentication # Run bootstrap for a private repository using HTTPS token authentication
flux bootstrap gitlab --owner=<group> --repository=<repo name> --token-auth flux bootstrap gitlab --owner=<group> --repository=<repository name> --token-auth
# Run bootstrap for a private repo using SSH authentication # Run bootstrap for a private repository using SSH authentication
flux bootstrap gitlab --owner=<group> --repository=<repo name> flux bootstrap gitlab --owner=<group> --repository=<repository name>
# Run bootstrap for a repository path # Run bootstrap for a repository path
flux bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster flux bootstrap gitlab --owner=<group> --repository=<repository name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account # Run bootstrap for a public repository on a personal account
flux bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal --token-auth flux bootstrap gitlab --owner=<user> --repository=<repository name> --private=false --personal --token-auth
# Run bootstrap for a private repo hosted on a GitLab server # Run bootstrap for a private repository hosted on a GitLab server
flux bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain> --token-auth flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
# Run bootstrap for a an existing repository with a branch named main # Run bootstrap for a an existing repository with a branch named main
flux bootstrap gitlab --owner=<organization> --repository=<repo name> --branch=main --token-auth flux bootstrap gitlab --owner=<organization> --repository=<repository name> --branch=main --token-auth`,
`,
RunE: bootstrapGitLabCmdRun, RunE: bootstrapGitLabCmdRun,
} }
@@ -206,7 +206,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("components are up to date") logger.Successf("components are up to date")
} }
// determine if repo synchronization is working // determine if repository synchronization is working
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace) isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace)
if isInstall { if isInstall {
@@ -218,44 +218,48 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("install completed") logger.Successf("install completed")
} }
repoURL := repository.GetURL() repoURL := repository.GetSSH()
secretOpts := sourcesecret.Options{
Name: rootArgs.namespace,
Namespace: rootArgs.namespace,
}
if bootstrapArgs.tokenAuth { if bootstrapArgs.tokenAuth {
// setup HTTPS token auth // Setup HTTPS token auth
secret := corev1.Secret{ repoURL = repository.GetURL()
ObjectMeta: metav1.ObjectMeta{ secretOpts.Username = "git"
Name: rootArgs.namespace, secretOpts.Password = glToken
Namespace: rootArgs.namespace, } else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
}, // Setup SSH auth
StringData: map[string]string{ u, err := url.Parse(repoURL)
"username": "git", if err != nil {
"password": glToken, return fmt.Errorf("git URL parse failed: %w", err)
},
} }
if err := upsertSecret(ctx, kubeClient, secret); err != nil { secretOpts.SSHHostname = u.Host
secretOpts.PrivateKeyAlgorithm = sourcesecret.RSAPrivateKeyAlgorithm
secretOpts.RSAKeyBits = 2048
}
secret, err := sourcesecret.Generate(secretOpts)
if err != nil {
return err
}
var s corev1.Secret
if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if len(s.StringData) > 0 {
logger.Actionf("configuring deploy key")
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err return err
} }
} else {
// setup SSH deploy key
repoURL = repository.GetSSH()
if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
logger.Actionf("configuring deploy key")
u, err := url.Parse(repoURL)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
key, err := generateDeployKey(ctx, kubeClient, u, rootArgs.namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
}
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
keyName := "flux" keyName := "flux"
if gitlabArgs.path != "" { if gitlabArgs.path != "" {
keyName = fmt.Sprintf("flux-%s", gitlabArgs.path) keyName = fmt.Sprintf("flux-%s", gitlabArgs.path)
} }
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil { if changed, err := provider.AddDeployKey(ctx, repository, ppk, keyName); err != nil {
return err return err
} else if changed { } else if changed {
logger.Successf("deploy key configured") logger.Successf("deploy key configured")
@@ -263,7 +267,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
// configure repo synchronization // configure repository synchronization
logger.Actionf("generating sync manifests") logger.Actionf("generating sync manifests")
syncManifests, err := generateSyncManifests( syncManifests, err := generateSyncManifests(
repoURL, repoURL,

View File

@@ -34,6 +34,7 @@ import (
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install" "github.com/fluxcd/flux2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/pkg/status"
) )
var checkCmd = &cobra.Command{ var checkCmd = &cobra.Command{
@@ -45,8 +46,7 @@ the local environment is configured correctly and if the installed components ar
flux check --pre flux check --pre
# Run installation checks # Run installation checks
flux check flux check`,
`,
RunE: runCheckCmd, RunE: runCheckCmd,
} }
@@ -205,12 +205,17 @@ func componentsCheck() bool {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return false return false
} }
statusChecker, err := NewStatusChecker(time.Second, rootArgs.timeout) statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger)
if err != nil {
return false
}
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return false return false
} }
@@ -220,10 +225,10 @@ func componentsCheck() bool {
var list v1.DeploymentList var list v1.DeploymentList
if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace), selector); err == nil { if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace), selector); err == nil {
for _, d := range list.Items { for _, d := range list.Items {
if err := statusChecker.Assess(d.Name); err != nil { if ref, err := buildComponentObjectRefs(d.Name); err == nil {
ok = false if err := statusChecker.Assess(ref...); err != nil {
} else { ok = false
logger.Successf("%s: healthy", d.Name) }
} }
for _, c := range d.Spec.Template.Spec.Containers { for _, c := range d.Spec.Template.Spec.Containers {
logger.Actionf(c.Image) logger.Actionf(c.Image)

View File

@@ -32,8 +32,7 @@ var completionBashCmd = &cobra.Command{
To configure your bash shell to load completions for each session add to your bashrc To configure your bash shell to load completions for each session add to your bashrc
# ~/.bashrc or ~/.profile # ~/.bashrc or ~/.profile
command -v flux >/dev/null && . <(flux completion bash) command -v flux >/dev/null && . <(flux completion bash)`,
`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenBashCompletion(os.Stdout) rootCmd.GenBashCompletion(os.Stdout)
}, },

View File

@@ -25,16 +25,11 @@ import (
var completionFishCmd = &cobra.Command{ var completionFishCmd = &cobra.Command{
Use: "fish", Use: "fish",
Short: "Generates fish completion scripts", Short: "Generates fish completion scripts",
Example: `To load completion run Example: `To configure your fish shell to load completions for each session write this script to your completions dir:
. <(flux completion fish) flux completion fish > ~/.config/fish/completions/flux.fish
To configure your fish shell to load completions for each session write this script to your completions dir: See http://fishshell.com/docs/current/index.html#completion-own for more details`,
flux completion fish > ~/.config/fish/completions/flux
See http://fishshell.com/docs/current/index.html#completion-own for more details
`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenFishCompletion(os.Stdout, true) rootCmd.GenFishCompletion(os.Stdout, true)
}, },

View File

@@ -39,8 +39,7 @@ flux completion >> flux-completion.ps1
Linux: Linux:
cd "${XDG_CONFIG_HOME:-"$HOME/.config/"}/powershell/modules" cd "${XDG_CONFIG_HOME:-"$HOME/.config/"}/powershell/modules"
flux completion >> flux-completions.ps1 flux completion >> flux-completions.ps1`,
`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenPowerShellCompletion(os.Stdout) rootCmd.GenPowerShellCompletion(os.Stdout)
}, },

View File

@@ -40,8 +40,7 @@ echo "${fpath// /\n}" | grep -i completion
flux completion zsh > _flux flux completion zsh > _flux
mv _flux ~/.oh-my-zsh/completions # oh-my-zsh mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto`,
`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenZshCompletion(os.Stdout) rootCmd.GenZshCompletion(os.Stdout)
}, },

View File

@@ -43,8 +43,7 @@ var createAlertCmd = &cobra.Command{
--event-severity info \ --event-severity info \
--event-source Kustomization/flux-system \ --event-source Kustomization/flux-system \
--provider-ref slack \ --provider-ref slack \
flux-system flux-system`,
`,
RunE: createAlertCmdRun, RunE: createAlertCmdRun,
} }
@@ -116,7 +115,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
} }
if createArgs.export { if createArgs.export {
return exportAlert(alert) return printExport(exportAlert(&alert))
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

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

View File

@@ -96,8 +96,7 @@ var createHelmReleaseCmd = &cobra.Command{
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo \ --chart=podinfo \
--values=./values.yaml \ --values=./values.yaml \
--export > podinfo-release.yaml --export > podinfo-release.yaml`,
`,
RunE: createHelmReleaseCmdRun, RunE: createHelmReleaseCmdRun,
} }
@@ -219,7 +218,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
} }
if createArgs.export { if createArgs.export {
return exportHelmRelease(helmRelease) return printExport(exportHelmRelease(&helmRelease))
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

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

View File

@@ -32,7 +32,7 @@ import (
) )
var createImagePolicyCmd = &cobra.Command{ var createImagePolicyCmd = &cobra.Command{
Use: "policy <name>", Use: "policy [name]",
Short: "Create or update an ImagePolicy object", Short: "Create or update an ImagePolicy object",
Long: `The create image policy command generates an ImagePolicy resource. Long: `The create image policy command generates an ImagePolicy resource.
An ImagePolicy object calculates a "latest image" given an image An ImagePolicy object calculates a "latest image" given an image
@@ -40,6 +40,17 @@ repository and a policy, e.g., semver.
The image that sorts highest according to the policy is recorded in The image that sorts highest according to the policy is recorded in
the status of the object.`, the status of the object.`,
Example: ` # Create an ImagePolicy to select the latest stable release
flux create image policy podinfo \
--image-ref=podinfo \
--select-semver=">=1.0.0"
# Create an ImagePolicy to select the latest main branch build tagged as "${GIT_BRANCH}-${GIT_SHA:0:7}-$(date +%s)"
flux create image policy podinfo \
--image-ref=podinfo \
--select-numeric=asc \
--filter-regex='^main-[a-f0-9]+-(?P<ts>[0-9]+)' \
--filter-extract='$ts'`,
RunE: createImagePolicyRun} RunE: createImagePolicyRun}
type imagePolicyFlags struct { type imagePolicyFlags struct {

View File

@@ -30,7 +30,7 @@ import (
) )
var createImageRepositoryCmd = &cobra.Command{ var createImageRepositoryCmd = &cobra.Command{
Use: "repository <name>", Use: "repository [name]",
Short: "Create or update an ImageRepository object", Short: "Create or update an ImageRepository object",
Long: `The create image repository command generates an ImageRepository resource. Long: `The create image repository command generates an ImageRepository resource.
An ImageRepository object specifies an image repository to scan.`, An ImageRepository object specifies an image repository to scan.`,
@@ -57,8 +57,7 @@ An ImageRepository object specifies an image repository to scan.`,
--cert-file client.crt --key-file client.key --cert-file client.crt --key-file client.key
flux create image repository app-repo \ flux create image repository app-repo \
--cert-secret-ref client-cert \ --cert-secret-ref client-cert \
--image registry.example.com/private/app --interval 5m --image registry.example.com/private/app --interval 5m`,
`,
RunE: createImageRepositoryRun, RunE: createImageRepositoryRun,
} }

View File

@@ -28,19 +28,37 @@ import (
) )
var createImageUpdateCmd = &cobra.Command{ var createImageUpdateCmd = &cobra.Command{
Use: "update <name>", Use: "update [name]",
Short: "Create or update an ImageUpdateAutomation object", Short: "Create or update an ImageUpdateAutomation object",
Long: `The create image update command generates an ImageUpdateAutomation resource. Long: `The create image update command generates an ImageUpdateAutomation resource.
An ImageUpdateAutomation object specifies an automated update to images An ImageUpdateAutomation object specifies an automated update to images
mentioned in YAMLs in a git repository.`, mentioned in YAMLs in a git repository.`,
Example: ` # Configure image updates for the main repository created by flux bootstrap
flux create image update flux-system \
--git-repo-ref=flux-system \
--git-repo-path="./clusters/my-cluster" \
--checkout-branch=main \
--author-name=flux \
--author-email=flux@example.com \
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}"
# Configure image updates to push changes to a different branch, if the branch doesn't exists it will be created
flux create image update flux-system \
--git-repo-ref=flux-system \
--git-repo-path="./clusters/my-cluster" \
--checkout-branch=main \
--push-branch=image-updates \
--author-name=flux \
--author-email=flux@example.com \
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}"`,
RunE: createImageUpdateRun, RunE: createImageUpdateRun,
} }
type imageUpdateFlags struct { type imageUpdateFlags struct {
// git checkout spec gitRepoRef string
gitRepoRef string gitRepoPath string
branch string checkoutBranch string
// commit spec pushBranch string
commitTemplate string commitTemplate string
authorName string authorName string
authorEmail string authorEmail string
@@ -50,8 +68,10 @@ var imageUpdateArgs = imageUpdateFlags{}
func init() { func init() {
flags := createImageUpdateCmd.Flags() flags := createImageUpdateCmd.Flags()
flags.StringVar(&imageUpdateArgs.gitRepoRef, "git-repo-ref", "", "the name of a GitRepository resource with details of the upstream git repository") flags.StringVar(&imageUpdateArgs.gitRepoRef, "git-repo-ref", "", "the name of a GitRepository resource with details of the upstream Git repository")
flags.StringVar(&imageUpdateArgs.branch, "branch", "", "the branch to checkout and push commits to") flags.StringVar(&imageUpdateArgs.gitRepoPath, "git-repo-path", "", "path to the directory containing the manifests to be updated, defaults to the repository root")
flags.StringVar(&imageUpdateArgs.checkoutBranch, "checkout-branch", "", "the branch to checkout")
flags.StringVar(&imageUpdateArgs.pushBranch, "push-branch", "", "the branch to push commits to, defaults to the checkout branch if not specified")
flags.StringVar(&imageUpdateArgs.commitTemplate, "commit-template", "", "a template for commit messages") flags.StringVar(&imageUpdateArgs.commitTemplate, "commit-template", "", "a template for commit messages")
flags.StringVar(&imageUpdateArgs.authorName, "author-name", "", "the name to use for commit author") flags.StringVar(&imageUpdateArgs.authorName, "author-name", "", "the name to use for commit author")
flags.StringVar(&imageUpdateArgs.authorEmail, "author-email", "", "the email to use for commit author") flags.StringVar(&imageUpdateArgs.authorEmail, "author-email", "", "the email to use for commit author")
@@ -69,8 +89,16 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("a reference to a GitRepository is required (--git-repo-ref)") return fmt.Errorf("a reference to a GitRepository is required (--git-repo-ref)")
} }
if imageUpdateArgs.branch == "" { if imageUpdateArgs.checkoutBranch == "" {
return fmt.Errorf("the Git repository branch is required (--branch)") return fmt.Errorf("the Git repository branch is required (--checkout-branch)")
}
if imageUpdateArgs.authorName == "" {
return fmt.Errorf("the author name is required (--author-name)")
}
if imageUpdateArgs.authorEmail == "" {
return fmt.Errorf("the author email is required (--author-email)")
} }
labels, err := parseLabels() labels, err := parseLabels()
@@ -89,9 +117,11 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
GitRepositoryRef: meta.LocalObjectReference{ GitRepositoryRef: meta.LocalObjectReference{
Name: imageUpdateArgs.gitRepoRef, Name: imageUpdateArgs.gitRepoRef,
}, },
Branch: imageUpdateArgs.branch, Branch: imageUpdateArgs.checkoutBranch,
},
Interval: metav1.Duration{
Duration: createArgs.interval,
}, },
Interval: metav1.Duration{Duration: createArgs.interval},
Commit: autov1.CommitSpec{ Commit: autov1.CommitSpec{
AuthorName: imageUpdateArgs.authorName, AuthorName: imageUpdateArgs.authorName,
AuthorEmail: imageUpdateArgs.authorEmail, AuthorEmail: imageUpdateArgs.authorEmail,
@@ -100,6 +130,19 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
}, },
} }
if imageUpdateArgs.pushBranch != "" {
update.Spec.Push = &autov1.PushSpec{
Branch: imageUpdateArgs.pushBranch,
}
}
if imageUpdateArgs.gitRepoPath != "" {
update.Spec.Update = &autov1.UpdateStrategy{
Path: imageUpdateArgs.gitRepoPath,
Strategy: autov1.UpdateStrategySetters,
}
}
if createArgs.export { if createArgs.export {
return printExport(exportImageUpdate(&update)) return printExport(exportImageUpdate(&update))
} }

View File

@@ -19,6 +19,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"path/filepath"
"strings" "strings"
"time" "time"
@@ -67,8 +68,7 @@ var createKsCmd = &cobra.Command{
flux create kustomization secrets \ flux create kustomization secrets \
--source=Bucket/secrets \ --source=Bucket/secrets \
--prune=true \ --prune=true \
--interval=5m --interval=5m`,
`,
RunE: createKsCmdRun, RunE: createKsCmdRun,
} }
@@ -142,7 +142,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
Interval: metav1.Duration{ Interval: metav1.Duration{
Duration: createArgs.interval, Duration: createArgs.interval,
}, },
Path: kustomizationArgs.path.String(), Path: filepath.ToSlash(kustomizationArgs.path.String()),
Prune: kustomizationArgs.prune, Prune: kustomizationArgs.prune,
SourceRef: kustomizev1.CrossNamespaceSourceReference{ SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: kustomizationArgs.source.Kind, Kind: kustomizationArgs.source.Kind,
@@ -210,7 +210,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
} }
if createArgs.export { if createArgs.export {
return exportKs(kustomization) return printExport(exportKs(&kustomization))
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -45,8 +45,7 @@ var createReceiverCmd = &cobra.Command{
--event push \ --event push \
--secret-ref webhook-token \ --secret-ref webhook-token \
--resource GitRepository/webapp \ --resource GitRepository/webapp \
--resource HelmRepository/webapp --resource HelmRepository/webapp`,
`,
RunE: createReceiverCmdRun, RunE: createReceiverCmdRun,
} }
@@ -125,7 +124,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
} }
if createArgs.export { if createArgs.export {
return exportReceiver(receiver) return printExport(exportReceiver(&receiver))
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -18,15 +18,12 @@ package main
import ( import (
"context" "context"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
) )
var createSecretCmd = &cobra.Command{ var createSecretCmd = &cobra.Command{
@@ -39,23 +36,6 @@ func init() {
createCmd.AddCommand(createSecretCmd) createCmd.AddCommand(createSecretCmd)
} }
func makeSecret(name string) (corev1.Secret, error) {
secretLabels, err := parseLabels()
if err != nil {
return corev1.Secret{}, err
}
return corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: rootArgs.namespace,
Labels: secretLabels,
},
StringData: map[string]string{},
Data: nil,
}, nil
}
func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.Secret) error { func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.Secret) error {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: secret.GetNamespace(), Namespace: secret.GetNamespace(),
@@ -81,19 +61,3 @@ func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.S
} }
return nil return nil
} }
func exportSecret(secret corev1.Secret) error {
secret.TypeMeta = metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
}
data, err := yaml.Marshal(secret)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
}

View File

@@ -20,22 +20,21 @@ import (
"context" "context"
"crypto/elliptic" "crypto/elliptic"
"fmt" "fmt"
"io/ioutil"
"net/url" "net/url"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/ssh" "github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
) )
var createSecretGitCmd = &cobra.Command{ var createSecretGitCmd = &cobra.Command{
Use: "git [name]", Use: "git [name]",
Short: "Create or update a Kubernetes secret for Git authentication", Short: "Create or update a Kubernetes secret for Git authentication",
Long: ` Long: `The create secret git command generates a Kubernetes secret with Git credentials.
The create secret git command generates a Kubernetes secret with Git credentials.
For Git over SSH, the host and SSH keys are automatically generated and stored in the secret. For Git over SSH, the host and SSH keys are automatically generated and stored in the secret.
For Git over HTTP/S, the provided basic authentication credentials are stored in the secret.`, For Git over HTTP/S, the provided basic authentication credentials are stored in the secret.`,
Example: ` # Create a Git SSH authentication secret using an ECDSA P-521 curve public key Example: ` # Create a Git SSH authentication secret using an ECDSA P-521 curve public key
@@ -45,6 +44,12 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
--ssh-key-algorithm=ecdsa \ --ssh-key-algorithm=ecdsa \
--ssh-ecdsa-curve=p521 --ssh-ecdsa-curve=p521
# Create a Git SSH authentication secret with a passwordless private key from file
# The public SSH host key will still be gathered from the host
flux create secret git podinfo-auth \
--url=ssh://git@github.com/stefanprodan/podinfo \
--private-key-file=./private.key
# Create a secret for a Git repository using basic authentication # Create a secret for a Git repository using basic authentication
flux create secret git podinfo-auth \ flux create secret git podinfo-auth \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
@@ -65,19 +70,19 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
--export > podinfo-auth.yaml --export > podinfo-auth.yaml
sops --encrypt --encrypted-regex '^(data|stringData)$' \ sops --encrypt --encrypted-regex '^(data|stringData)$' \
--in-place podinfo-auth.yaml --in-place podinfo-auth.yaml`,
`,
RunE: createSecretGitCmdRun, RunE: createSecretGitCmdRun,
} }
type secretGitFlags struct { type secretGitFlags struct {
url string url string
username string username string
password string password string
keyAlgorithm flags.PublicKeyAlgorithm keyAlgorithm flags.PublicKeyAlgorithm
rsaBits flags.RSAKeyBits rsaBits flags.RSAKeyBits
ecdsaCurve flags.ECDSACurve ecdsaCurve flags.ECDSACurve
caFile string caFile string
privateKeyFile string
} }
var secretGitArgs = NewSecretGitFlags() var secretGitArgs = NewSecretGitFlags()
@@ -90,13 +95,14 @@ func init() {
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description()) createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description()) createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates") createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
createSecretGitCmd.Flags().StringVar(&secretGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
createSecretCmd.AddCommand(createSecretGitCmd) createSecretCmd.AddCommand(createSecretGitCmd)
} }
func NewSecretGitFlags() secretGitFlags { func NewSecretGitFlags() secretGitFlags {
return secretGitFlags{ return secretGitFlags{
keyAlgorithm: "rsa", keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm),
rsaBits: 2048, rsaBits: 2048,
ecdsaCurve: flags.ECDSACurve{Curve: elliptic.P384()}, ecdsaCurve: flags.ECDSACurve{Curve: elliptic.P384()},
} }
@@ -107,11 +113,6 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("secret name is required") return fmt.Errorf("secret name is required")
} }
name := args[0] name := args[0]
secret, err := makeSecret(name)
if err != nil {
return err
}
if secretGitArgs.url == "" { if secretGitArgs.url == "" {
return fmt.Errorf("url is required") return fmt.Errorf("url is required")
} }
@@ -121,96 +122,64 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("git URL parse failed: %w", err) return fmt.Errorf("git URL parse failed: %w", err)
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) labels, err := parseLabels()
defer cancel()
switch u.Scheme {
case "ssh":
pair, err := generateKeyPair(ctx, secretGitArgs.keyAlgorithm, secretGitArgs.rsaBits, secretGitArgs.ecdsaCurve)
if err != nil {
return err
}
hostKey, err := scanHostKey(ctx, u)
if err != nil {
return err
}
secret.StringData = map[string]string{
"identity": string(pair.PrivateKey),
"identity.pub": string(pair.PublicKey),
"known_hosts": string(hostKey),
}
if !createArgs.export {
logger.Generatef("deploy key: %s", string(pair.PublicKey))
}
case "http", "https":
if secretGitArgs.username == "" || secretGitArgs.password == "" {
return fmt.Errorf("for Git over HTTP/S the username and password are required")
}
secret.StringData = map[string]string{
"username": secretGitArgs.username,
"password": secretGitArgs.password,
}
if secretGitArgs.caFile != "" {
ca, err := ioutil.ReadFile(secretGitArgs.caFile)
if err != nil {
return fmt.Errorf("failed to read CA file '%s': %w", secretGitArgs.caFile, err)
}
secret.StringData["caFile"] = string(ca)
}
default:
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
}
if createArgs.export {
return exportSecret(secret)
}
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
if err := upsertSecret(ctx, kubeClient, secret); err != nil { opts := sourcesecret.Options{
Name: name,
Namespace: rootArgs.namespace,
Labels: labels,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
}
switch u.Scheme {
case "ssh":
opts.SSHHostname = u.Host
opts.PrivateKeyPath = secretGitArgs.privateKeyFile
opts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(secretGitArgs.keyAlgorithm)
opts.RSAKeyBits = int(secretGitArgs.rsaBits)
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
case "http", "https":
if secretGitArgs.username == "" || secretGitArgs.password == "" {
return fmt.Errorf("for Git over HTTP/S the username and password are required")
}
opts.Username = secretGitArgs.username
opts.Password = secretGitArgs.password
opts.CAFilePath = secretGitArgs.caFile
default:
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
}
secret, err := sourcesecret.Generate(opts)
if err != nil {
return err
}
if createArgs.export {
fmt.Println(secret.Content)
return nil
}
var s corev1.Secret
if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
logger.Generatef("deploy key: %s", ppk)
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err return err
} }
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace) logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil return nil
} }
func generateKeyPair(ctx context.Context, alg flags.PublicKeyAlgorithm, rsa flags.RSAKeyBits, ecdsa flags.ECDSACurve) (*ssh.KeyPair, error) {
var keyGen ssh.KeyPairGenerator
switch algorithm := alg.String(); algorithm {
case "rsa":
keyGen = ssh.NewRSAGenerator(int(rsa))
case "ecdsa":
keyGen = ssh.NewECDSAGenerator(ecdsa.Curve)
case "ed25519":
keyGen = ssh.NewEd25519Generator()
default:
return nil, fmt.Errorf("unsupported public key algorithm: %s", algorithm)
}
pair, err := keyGen.Generate()
if err != nil {
return nil, fmt.Errorf("key pair generation failed, error: %w", err)
}
return pair, nil
}
func scanHostKey(ctx context.Context, url *url.URL) ([]byte, error) {
host := url.Host
if url.Port() == "" {
host = host + ":22"
}
hostKey, err := ssh.ScanHostKey(host, 30*time.Second)
if err != nil {
return nil, fmt.Errorf("SSH key scan for host %s failed, error: %w", host, err)
}
return hostKey, nil
}

View File

@@ -21,17 +21,18 @@ import (
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
) )
var createSecretHelmCmd = &cobra.Command{ var createSecretHelmCmd = &cobra.Command{
Use: "helm [name]", Use: "helm [name]",
Short: "Create or update a Kubernetes secret for Helm repository authentication", Short: "Create or update a Kubernetes secret for Helm repository authentication",
Long: ` Long: `The create secret helm command generates a Kubernetes secret with basic authentication credentials.`,
The create secret helm command generates a Kubernetes secret with basic authentication credentials.`, Example: ` # Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
Example: ` # Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
flux create secret helm repo-auth \ flux create secret helm repo-auth \
--namespace=my-namespace \ --namespace=my-namespace \
--username=my-username \ --username=my-username \
@@ -41,14 +42,13 @@ The create secret helm command generates a Kubernetes secret with basic authenti
sops --encrypt --encrypted-regex '^(data|stringData)$' \ sops --encrypt --encrypted-regex '^(data|stringData)$' \
--in-place repo-auth.yaml --in-place repo-auth.yaml
# Create an authentication secret using a custom TLS cert # Create a Helm authentication secret using a custom TLS cert
flux create secret helm repo-auth \ flux create secret helm repo-auth \
--username=username \ --username=username \
--password=password \ --password=password \
--cert-file=./cert.crt \ --cert-file=./cert.crt \
--key-file=./key.crt \ --key-file=./key.crt \
--ca-file=./ca.crt --ca-file=./ca.crt`,
`,
RunE: createSecretHelmCmdRun, RunE: createSecretHelmCmdRun,
} }
@@ -72,36 +72,45 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("secret name is required") return fmt.Errorf("secret name is required")
} }
name := args[0] name := args[0]
secret, err := makeSecret(name)
labels, err := parseLabels()
if err != nil { if err != nil {
return err return err
} }
if secretHelmArgs.username != "" && secretHelmArgs.password != "" { opts := sourcesecret.Options{
secret.StringData["username"] = secretHelmArgs.username Name: name,
secret.StringData["password"] = secretHelmArgs.password Namespace: rootArgs.namespace,
Labels: labels,
Username: secretHelmArgs.username,
Password: secretHelmArgs.password,
CAFilePath: secretHelmArgs.caFile,
CertFilePath: secretHelmArgs.certFile,
KeyFilePath: secretHelmArgs.keyFile,
} }
secret, err := sourcesecret.Generate(opts)
if err = populateSecretTLS(&secret, secretHelmArgs.secretTLSFlags); err != nil { if err != nil {
return err return err
} }
if createArgs.export { if createArgs.export {
return exportSecret(secret) fmt.Println(secret.Content)
return nil
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
var s corev1.Secret
if err := upsertSecret(ctx, kubeClient, secret); err != nil { if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err return err
} }
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil return nil
} }

View File

@@ -19,22 +19,21 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"io/ioutil"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
) )
var createSecretTLSCmd = &cobra.Command{ var createSecretTLSCmd = &cobra.Command{
Use: "tls [name]", Use: "tls [name]",
Short: "Create or update a Kubernetes secret with TLS certificates", Short: "Create or update a Kubernetes secret with TLS certificates",
Long: ` Long: `The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`,
The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`, Example: ` # Create a TLS secret on disk and encrypt it with Mozilla SOPS.
Example: `
# Create a TLS secret on disk and encrypt it with Mozilla SOPS.
# Files are expected to be PEM-encoded. # Files are expected to be PEM-encoded.
flux create secret tls certs \ flux create secret tls certs \
--namespace=my-namespace \ --namespace=my-namespace \
@@ -43,8 +42,7 @@ The create secret tls command generates a Kubernetes secret with certificates fo
--export > certs.yaml --export > certs.yaml
sops --encrypt --encrypted-regex '^(data|stringData)$' \ sops --encrypt --encrypted-regex '^(data|stringData)$' \
--in-place certs.yaml --in-place certs.yaml`,
`,
RunE: createSecretTLSCmdRun, RunE: createSecretTLSCmdRun,
} }
@@ -68,61 +66,48 @@ func init() {
createSecretCmd.AddCommand(createSecretTLSCmd) createSecretCmd.AddCommand(createSecretTLSCmd)
} }
func populateSecretTLS(secret *corev1.Secret, args secretTLSFlags) error {
if args.certFile != "" && args.keyFile != "" {
cert, err := ioutil.ReadFile(args.certFile)
if err != nil {
return fmt.Errorf("failed to read repository cert file '%s': %w", args.certFile, err)
}
secret.StringData["certFile"] = string(cert)
key, err := ioutil.ReadFile(args.keyFile)
if err != nil {
return fmt.Errorf("failed to read repository key file '%s': %w", args.keyFile, err)
}
secret.StringData["keyFile"] = string(key)
}
if args.caFile != "" {
ca, err := ioutil.ReadFile(args.caFile)
if err != nil {
return fmt.Errorf("failed to read repository CA file '%s': %w", args.caFile, err)
}
secret.StringData["caFile"] = string(ca)
}
return nil
}
func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error { func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("secret name is required") return fmt.Errorf("secret name is required")
} }
name := args[0] name := args[0]
secret, err := makeSecret(name)
labels, err := parseLabels()
if err != nil { if err != nil {
return err return err
} }
if err = populateSecretTLS(&secret, secretTLSArgs); err != nil { opts := sourcesecret.Options{
Name: name,
Namespace: rootArgs.namespace,
Labels: labels,
CAFilePath: secretTLSArgs.caFile,
CertFilePath: secretTLSArgs.certFile,
KeyFilePath: secretTLSArgs.keyFile,
}
secret, err := sourcesecret.Generate(opts)
if err != nil {
return err return err
} }
if createArgs.export { if createArgs.export {
return exportSecret(secret) fmt.Println(secret.Content)
return nil
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
var s corev1.Secret
if err := upsertSecret(ctx, kubeClient, secret); err != nil { if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err return err
} }
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil return nil
} }

View File

@@ -40,10 +40,9 @@ import (
var createSourceBucketCmd = &cobra.Command{ var createSourceBucketCmd = &cobra.Command{
Use: "bucket [name]", Use: "bucket [name]",
Short: "Create or update a Bucket source", Short: "Create or update a Bucket source",
Long: ` Long: `The create source bucket command generates a Bucket resource and waits for it to be downloaded.
The create source bucket command generates a Bucket resource and waits for it to be downloaded.
For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`, For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a Buckets using static authentication Example: ` # Create a source for a Bucket using static authentication
flux create source bucket podinfo \ flux create source bucket podinfo \
--bucket-name=podinfo \ --bucket-name=podinfo \
--endpoint=minio.minio.svc.cluster.local:9000 \ --endpoint=minio.minio.svc.cluster.local:9000 \
@@ -52,14 +51,13 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
--secret-key=mysecretkey \ --secret-key=mysecretkey \
--interval=10m --interval=10m
# Create a source from an Amazon S3 Bucket using IAM authentication # Create a source for an Amazon S3 Bucket using IAM authentication
flux create source bucket podinfo \ flux create source bucket podinfo \
--bucket-name=podinfo \ --bucket-name=podinfo \
--provider=aws \ --provider=aws \
--endpoint=s3.amazonaws.com \ --endpoint=s3.amazonaws.com \
--region=us-east-1 \ --region=us-east-1 \
--interval=10m --interval=10m`,
`,
RunE: createSourceBucketCmdRun, RunE: createSourceBucketCmdRun,
} }
@@ -144,7 +142,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
} }
if createArgs.export { if createArgs.export {
return exportBucket(*bucket) return printExport(exportBucket(bucket))
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)

View File

@@ -24,6 +24,8 @@ import (
"net/url" "net/url"
"os" "os"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@@ -33,12 +35,11 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
) )
type sourceGitFlags struct { type sourceGitFlags struct {
@@ -48,19 +49,19 @@ type sourceGitFlags struct {
semver string semver string
username string username string
password string password string
caFile string
keyAlgorithm flags.PublicKeyAlgorithm keyAlgorithm flags.PublicKeyAlgorithm
keyRSABits flags.RSAKeyBits keyRSABits flags.RSAKeyBits
keyECDSACurve flags.ECDSACurve keyECDSACurve flags.ECDSACurve
secretRef string secretRef string
gitImplementation flags.GitImplementation gitImplementation flags.GitImplementation
caFile string
privateKeyFile string
} }
var createSourceGitCmd = &cobra.Command{ var createSourceGitCmd = &cobra.Command{
Use: "git [name]", Use: "git [name]",
Short: "Create or update a GitRepository source", Short: "Create or update a GitRepository source",
Long: ` Long: `The create source git command generates a GitRepository resource and waits for it to sync.
The create source git command generates a GitRepository resource and waits for it to sync.
For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret. For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`, For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a public Git repository master branch Example: ` # Create a source from a public Git repository master branch
@@ -68,7 +69,7 @@ For private Git repositories, the basic authentication credentials are stored in
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--branch=master --branch=master
# Create a source from a Git repository pinned to specific git tag # Create a source for a Git repository pinned to specific git tag
flux create source git podinfo \ flux create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--tag="3.2.3" --tag="3.2.3"
@@ -78,12 +79,12 @@ For private Git repositories, the basic authentication credentials are stored in
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.0 <3.3.0" --tag-semver=">=3.2.0 <3.3.0"
# Create a source from a Git repository using SSH authentication # Create a source for a Git repository using SSH authentication
flux create source git podinfo \ flux create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \ --url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master --branch=master
# Create a source from a Git repository using SSH authentication and an # Create a source for a Git repository using SSH authentication and an
# ECDSA P-521 curve public key # ECDSA P-521 curve public key
flux create source git podinfo \ flux create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \ --url=ssh://git@github.com/stefanprodan/podinfo \
@@ -91,12 +92,19 @@ For private Git repositories, the basic authentication credentials are stored in
--ssh-key-algorithm=ecdsa \ --ssh-key-algorithm=ecdsa \
--ssh-ecdsa-curve=p521 --ssh-ecdsa-curve=p521
# Create a source from a Git repository using basic authentication # Create a source for a Git repository using SSH authentication and a
# passwordless private key from file
# The public SSH host key will still be gathered from the host
flux create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master \
--private-key-file=./private.key
# Create a source for a Git repository using basic authentication
flux create source git podinfo \ flux create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--username=username \ --username=username \
--password=password --password=password`,
`,
RunE: createSourceGitCmdRun, RunE: createSourceGitCmdRun,
} }
@@ -115,13 +123,14 @@ func init() {
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials") createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials")
createSourceGitCmd.Flags().Var(&sourceGitArgs.gitImplementation, "git-implementation", sourceGitArgs.gitImplementation.Description()) createSourceGitCmd.Flags().Var(&sourceGitArgs.gitImplementation, "git-implementation", sourceGitArgs.gitImplementation.Description())
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates, requires libgit2") createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates, requires libgit2")
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
createSourceCmd.AddCommand(createSourceGitCmd) createSourceCmd.AddCommand(createSourceGitCmd)
} }
func newSourceGitFlags() sourceGitFlags { func newSourceGitFlags() sourceGitFlags {
return sourceGitFlags{ return sourceGitFlags{
keyAlgorithm: "rsa", keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm),
keyRSABits: 2048, keyRSABits: 2048,
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()}, keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
} }
@@ -151,6 +160,9 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return fmt.Errorf("git URL parse failed: %w", err) return fmt.Errorf("git URL parse failed: %w", err)
} }
if u.Scheme != "ssh" && u.Scheme != "http" && u.Scheme != "https" {
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
}
sourceLabels, err := parseLabels() sourceLabels, err := parseLabels()
if err != nil { if err != nil {
@@ -184,13 +196,14 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
gitRepository.Spec.Reference.Branch = sourceGitArgs.branch gitRepository.Spec.Reference.Branch = sourceGitArgs.branch
} }
if createArgs.export { if sourceGitArgs.secretRef != "" {
if sourceGitArgs.secretRef != "" { gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{ Name: sourceGitArgs.secretRef,
Name: sourceGitArgs.secretRef,
}
} }
return exportGit(gitRepository) }
if createArgs.export {
return printExport(exportGit(&gitRepository))
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
@@ -201,91 +214,55 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
withAuth := false
// TODO(hidde): move all auth prep to separate func?
if sourceGitArgs.secretRef != "" {
withAuth = true
} else if u.Scheme == "ssh" {
logger.Generatef("generating deploy key pair")
pair, err := generateKeyPair(ctx, sourceGitArgs.keyAlgorithm, sourceGitArgs.keyRSABits, sourceGitArgs.keyECDSACurve)
if err != nil {
return err
}
logger.Successf("deploy key: %s", pair.PublicKey)
prompt := promptui.Prompt{
Label: "Have you added the deploy key to your repository",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
logger.Actionf("collecting preferred public key from SSH server")
hostKey, err := scanHostKey(ctx, u)
if err != nil {
return err
}
logger.Successf("collected public key from SSH server:\n%s", hostKey)
logger.Actionf("applying secret with keys")
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
StringData: map[string]string{
"identity": string(pair.PrivateKey),
"identity.pub": string(pair.PublicKey),
"known_hosts": string(hostKey),
},
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
withAuth = true
} else if sourceGitArgs.username != "" && sourceGitArgs.password != "" {
logger.Actionf("applying secret with basic auth credentials")
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
StringData: map[string]string{
"username": sourceGitArgs.username,
"password": sourceGitArgs.password,
},
}
if sourceGitArgs.caFile != "" {
ca, err := ioutil.ReadFile(sourceGitArgs.caFile)
if err != nil {
return fmt.Errorf("failed to read CA file '%s': %w", sourceGitArgs.caFile, err)
}
secret.StringData["caFile"] = string(ca)
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
withAuth = true
}
if withAuth {
logger.Successf("authentication configured")
}
logger.Generatef("generating GitRepository source") logger.Generatef("generating GitRepository source")
if sourceGitArgs.secretRef == "" {
if withAuth { secretOpts := sourcesecret.Options{
secretName := name Name: name,
if sourceGitArgs.secretRef != "" { Namespace: rootArgs.namespace,
secretName = sourceGitArgs.secretRef ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{ switch u.Scheme {
Name: secretName, case "ssh":
secretOpts.SSHHostname = u.Host
secretOpts.PrivateKeyPath = sourceGitArgs.privateKeyFile
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(sourceGitArgs.keyAlgorithm)
secretOpts.RSAKeyBits = int(sourceGitArgs.keyRSABits)
secretOpts.ECDSACurve = sourceGitArgs.keyECDSACurve.Curve
case "https":
secretOpts.Username = sourceGitArgs.username
secretOpts.Password = sourceGitArgs.password
secretOpts.CAFilePath = sourceGitArgs.caFile
}
secret, err := sourcesecret.Generate(secretOpts)
if err != nil {
return err
}
var s corev1.Secret
if err = yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if len(s.StringData) > 0 {
if hk, ok := s.StringData[sourcesecret.KnownHostsSecretKey]; ok {
logger.Successf("collected public key from SSH server:\n%s", hk)
}
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
logger.Generatef("deploy key: %s", ppk)
prompt := promptui.Prompt{
Label: "Have you added the deploy key to your repository",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("applying secret with repository credentials")
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err
}
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
Name: s.Name,
}
logger.Successf("authentication configured")
} }
} }

View File

@@ -32,36 +32,36 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
) )
var createSourceHelmCmd = &cobra.Command{ var createSourceHelmCmd = &cobra.Command{
Use: "helm [name]", Use: "helm [name]",
Short: "Create or update a HelmRepository source", Short: "Create or update a HelmRepository source",
Long: ` Long: `The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`, For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a public Helm repository Example: ` # Create a source for a public Helm repository
flux create source helm podinfo \ flux create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \ --url=https://stefanprodan.github.io/podinfo \
--interval=10m --interval=10m
# Create a source from a Helm repository using basic authentication # Create a source for a Helm repository using basic authentication
flux create source helm podinfo \ flux create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \ --url=https://stefanprodan.github.io/podinfo \
--username=username \ --username=username \
--password=password --password=password
# Create a source from a Helm repository using TLS authentication # Create a source for a Helm repository using TLS authentication
flux create source helm podinfo \ flux create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \ --url=https://stefanprodan.github.io/podinfo \
--cert-file=./cert.crt \ --cert-file=./cert.crt \
--key-file=./key.crt \ --key-file=./key.crt \
--ca-file=./ca.crt --ca-file=./ca.crt`,
`,
RunE: createSourceHelmCmdRun, RunE: createSourceHelmCmdRun,
} }
@@ -135,7 +135,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
} }
if createArgs.export { if createArgs.export {
return exportHelmRepository(*helmRepository) return printExport(exportHelmRepository(helmRepository))
} }
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
@@ -149,46 +149,27 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
logger.Generatef("generating HelmRepository source") logger.Generatef("generating HelmRepository source")
if sourceHelmArgs.secretRef == "" { if sourceHelmArgs.secretRef == "" {
secretName := fmt.Sprintf("helm-%s", name) secretName := fmt.Sprintf("helm-%s", name)
secretOpts := sourcesecret.Options{
secret := corev1.Secret{ Name: secretName,
ObjectMeta: metav1.ObjectMeta{ Namespace: rootArgs.namespace,
Name: secretName, Username: sourceHelmArgs.username,
Namespace: rootArgs.namespace, Password: sourceHelmArgs.password,
Labels: sourceLabels, CertFilePath: sourceHelmArgs.certFile,
}, KeyFilePath: sourceHelmArgs.keyFile,
StringData: map[string]string{}, CAFilePath: sourceHelmArgs.caFile,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
secret, err := sourcesecret.Generate(secretOpts)
if sourceHelmArgs.username != "" && sourceHelmArgs.password != "" { if err != nil {
secret.StringData["username"] = sourceHelmArgs.username return err
secret.StringData["password"] = sourceHelmArgs.password
} }
var s corev1.Secret
if sourceHelmArgs.certFile != "" && sourceHelmArgs.keyFile != "" { if err = yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
cert, err := ioutil.ReadFile(sourceHelmArgs.certFile) return err
if err != nil {
return fmt.Errorf("failed to read repository cert file '%s': %w", sourceHelmArgs.certFile, err)
}
secret.StringData["certFile"] = string(cert)
key, err := ioutil.ReadFile(sourceHelmArgs.keyFile)
if err != nil {
return fmt.Errorf("failed to read repository key file '%s': %w", sourceHelmArgs.keyFile, err)
}
secret.StringData["keyFile"] = string(key)
} }
if len(s.StringData) > 0 {
if sourceHelmArgs.caFile != "" {
ca, err := ioutil.ReadFile(sourceHelmArgs.caFile)
if err != nil {
return fmt.Errorf("failed to read repository CA file '%s': %w", sourceHelmArgs.caFile, err)
}
secret.StringData["caFile"] = string(ca)
}
if len(secret.StringData) > 0 {
logger.Actionf("applying secret with repository credentials") logger.Actionf("applying secret with repository credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil { if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err return err
} }
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{ helmRepository.Spec.SecretRef = &meta.LocalObjectReference{

View File

@@ -37,8 +37,7 @@ import (
var createTenantCmd = &cobra.Command{ var createTenantCmd = &cobra.Command{
Use: "tenant", Use: "tenant",
Short: "Create or update a tenant", Short: "Create or update a tenant",
Long: ` Long: `The create tenant command generates namespaces, service accounts and role bindings to limit the
The create tenant command generates namespaces, service accounts and role bindings to limit the
reconcilers scope to the tenant namespaces.`, reconcilers scope to the tenant namespaces.`,
Example: ` # Create a tenant with access to a namespace Example: ` # Create a tenant with access to a namespace
flux create tenant dev-team \ flux create tenant dev-team \
@@ -49,8 +48,7 @@ reconcilers scope to the tenant namespaces.`,
flux create tenant dev-team \ flux create tenant dev-team \
--with-namespace=frontend \ --with-namespace=frontend \
--with-namespace=backend \ --with-namespace=backend \
--export > dev-team.yaml --export > dev-team.yaml`,
`,
RunE: createTenantCmdRun, RunE: createTenantCmdRun,
} }

View File

@@ -17,15 +17,8 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var deleteAlertCmd = &cobra.Command{ var deleteAlertCmd = &cobra.Command{
@@ -33,56 +26,13 @@ var deleteAlertCmd = &cobra.Command{
Short: "Delete a Alert resource", Short: "Delete a Alert resource",
Long: "The delete alert command removes the given Alert from the cluster.", Long: "The delete alert command removes the given Alert from the cluster.",
Example: ` # Delete an Alert and the Kubernetes resources created by it Example: ` # Delete an Alert and the Kubernetes resources created by it
flux delete alert main flux delete alert main`,
`, RunE: deleteCommand{
RunE: deleteAlertCmdRun, apiType: alertType,
object: universalAdapter{&notificationv1.Alert{}},
}.run,
} }
func init() { func init() {
deleteCmd.AddCommand(deleteAlertCmd) deleteCmd.AddCommand(deleteAlertCmd)
} }
func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("alert name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Alert",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting alert %s in %s namespace", name, rootArgs.namespace)
err = kubeClient.Delete(ctx, &alert)
if err != nil {
return err
}
logger.Successf("alert deleted")
return nil
}

View File

@@ -17,15 +17,8 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var deleteAlertProviderCmd = &cobra.Command{ var deleteAlertProviderCmd = &cobra.Command{
@@ -33,56 +26,13 @@ var deleteAlertProviderCmd = &cobra.Command{
Short: "Delete a Provider resource", Short: "Delete a Provider resource",
Long: "The delete alert-provider command removes the given Provider from the cluster.", Long: "The delete alert-provider command removes the given Provider from the cluster.",
Example: ` # Delete a Provider and the Kubernetes resources created by it Example: ` # Delete a Provider and the Kubernetes resources created by it
flux delete alert-provider slack flux delete alert-provider slack`,
`, RunE: deleteCommand{
RunE: deleteAlertProviderCmdRun, apiType: alertProviderType,
object: universalAdapter{&notificationv1.Provider{}},
}.run,
} }
func init() { func init() {
deleteCmd.AddCommand(deleteAlertProviderCmd) deleteCmd.AddCommand(deleteAlertProviderCmd)
} }
func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("provider name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
return err
}
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Provider",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting provider %s in %s namespace", name, rootArgs.namespace)
err = kubeClient.Delete(ctx, &alertProvider)
if err != nil {
return err
}
logger.Successf("provider deleted")
return nil
}

View File

@@ -27,8 +27,7 @@ var deleteHelmReleaseCmd = &cobra.Command{
Short: "Delete a HelmRelease resource", Short: "Delete a HelmRelease resource",
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.", Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
Example: ` # Delete a Helm release and the Kubernetes resources created by it Example: ` # Delete a Helm release and the Kubernetes resources created by it
flux delete hr podinfo flux delete hr podinfo`,
`,
RunE: deleteCommand{ RunE: deleteCommand{
apiType: helmReleaseType, apiType: helmReleaseType,
object: universalAdapter{&helmv2.HelmRelease{}}, object: universalAdapter{&helmv2.HelmRelease{}},

View File

@@ -27,8 +27,7 @@ var deleteImagePolicyCmd = &cobra.Command{
Short: "Delete an ImagePolicy object", Short: "Delete an ImagePolicy object",
Long: "The delete image policy command deletes the given ImagePolicy from the cluster.", Long: "The delete image policy command deletes the given ImagePolicy from the cluster.",
Example: ` # Delete an image policy Example: ` # Delete an image policy
flux delete image policy alpine3.x flux delete image policy alpine3.x`,
`,
RunE: deleteCommand{ RunE: deleteCommand{
apiType: imagePolicyType, apiType: imagePolicyType,
object: universalAdapter{&imagev1.ImagePolicy{}}, object: universalAdapter{&imagev1.ImagePolicy{}},

View File

@@ -27,8 +27,7 @@ var deleteImageRepositoryCmd = &cobra.Command{
Short: "Delete an ImageRepository object", Short: "Delete an ImageRepository object",
Long: "The delete image repository command deletes the given ImageRepository from the cluster.", Long: "The delete image repository command deletes the given ImageRepository from the cluster.",
Example: ` # Delete an image repository Example: ` # Delete an image repository
flux delete image repository alpine flux delete image repository alpine`,
`,
RunE: deleteCommand{ RunE: deleteCommand{
apiType: imageRepositoryType, apiType: imageRepositoryType,
object: universalAdapter{&imagev1.ImageRepository{}}, object: universalAdapter{&imagev1.ImageRepository{}},

View File

@@ -27,8 +27,7 @@ var deleteImageUpdateCmd = &cobra.Command{
Short: "Delete an ImageUpdateAutomation object", Short: "Delete an ImageUpdateAutomation object",
Long: "The delete image update command deletes the given ImageUpdateAutomation from the cluster.", Long: "The delete image update command deletes the given ImageUpdateAutomation from the cluster.",
Example: ` # Delete an image update automation Example: ` # Delete an image update automation
flux delete image update latest-images flux delete image update latest-images`,
`,
RunE: deleteCommand{ RunE: deleteCommand{
apiType: imageUpdateAutomationType, apiType: imageUpdateAutomationType,
object: universalAdapter{&autov1.ImageUpdateAutomation{}}, object: universalAdapter{&autov1.ImageUpdateAutomation{}},

View File

@@ -27,8 +27,7 @@ var deleteKsCmd = &cobra.Command{
Short: "Delete a Kustomization resource", Short: "Delete a Kustomization resource",
Long: "The delete kustomization command deletes the given Kustomization from the cluster.", Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
Example: ` # Delete a kustomization and the Kubernetes resources created by it Example: ` # Delete a kustomization and the Kubernetes resources created by it
flux delete kustomization podinfo flux delete kustomization podinfo`,
`,
RunE: deleteCommand{ RunE: deleteCommand{
apiType: kustomizationType, apiType: kustomizationType,
object: universalAdapter{&kustomizev1.Kustomization{}}, object: universalAdapter{&kustomizev1.Kustomization{}},

View File

@@ -17,15 +17,8 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var deleteReceiverCmd = &cobra.Command{ var deleteReceiverCmd = &cobra.Command{
@@ -33,56 +26,13 @@ var deleteReceiverCmd = &cobra.Command{
Short: "Delete a Receiver resource", Short: "Delete a Receiver resource",
Long: "The delete receiver command removes the given Receiver from the cluster.", Long: "The delete receiver command removes the given Receiver from the cluster.",
Example: ` # Delete an Receiver and the Kubernetes resources created by it Example: ` # Delete an Receiver and the Kubernetes resources created by it
flux delete receiver main flux delete receiver main`,
`, RunE: deleteCommand{
RunE: deleteReceiverCmdRun, apiType: receiverType,
object: universalAdapter{&notificationv1.Receiver{}},
}.run,
} }
func init() { func init() {
deleteCmd.AddCommand(deleteReceiverCmd) deleteCmd.AddCommand(deleteReceiverCmd)
} }
func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("receiver name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Receiver",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting receiver %s in %s namespace", name, rootArgs.namespace)
err = kubeClient.Delete(ctx, &receiver)
if err != nil {
return err
}
logger.Successf("receiver deleted")
return nil
}

View File

@@ -26,8 +26,7 @@ var deleteSourceBucketCmd = &cobra.Command{
Short: "Delete a Bucket source", Short: "Delete a Bucket source",
Long: "The delete source bucket command deletes the given Bucket from the cluster.", Long: "The delete source bucket command deletes the given Bucket from the cluster.",
Example: ` # Delete a Bucket source Example: ` # Delete a Bucket source
flux delete source bucket podinfo flux delete source bucket podinfo`,
`,
RunE: deleteCommand{ RunE: deleteCommand{
apiType: bucketType, apiType: bucketType,
object: universalAdapter{&sourcev1.Bucket{}}, object: universalAdapter{&sourcev1.Bucket{}},

View File

@@ -26,8 +26,7 @@ var deleteSourceGitCmd = &cobra.Command{
Short: "Delete a GitRepository source", Short: "Delete a GitRepository source",
Long: "The delete source git command deletes the given GitRepository from the cluster.", Long: "The delete source git command deletes the given GitRepository from the cluster.",
Example: ` # Delete a Git repository Example: ` # Delete a Git repository
flux delete source git podinfo flux delete source git podinfo`,
`,
RunE: deleteCommand{ RunE: deleteCommand{
apiType: gitRepositoryType, apiType: gitRepositoryType,
object: universalAdapter{&sourcev1.GitRepository{}}, object: universalAdapter{&sourcev1.GitRepository{}},

View File

@@ -17,14 +17,8 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
) )
var deleteSourceHelmCmd = &cobra.Command{ var deleteSourceHelmCmd = &cobra.Command{
@@ -32,8 +26,7 @@ var deleteSourceHelmCmd = &cobra.Command{
Short: "Delete a HelmRepository source", Short: "Delete a HelmRepository source",
Long: "The delete source helm command deletes the given HelmRepository from the cluster.", Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
Example: ` # Delete a Helm repository Example: ` # Delete a Helm repository
flux delete source helm podinfo flux delete source helm podinfo`,
`,
RunE: deleteCommand{ RunE: deleteCommand{
apiType: helmRepositoryType, apiType: helmRepositoryType,
object: universalAdapter{&sourcev1.HelmRepository{}}, object: universalAdapter{&sourcev1.HelmRepository{}},
@@ -43,48 +36,3 @@ var deleteSourceHelmCmd = &cobra.Command{
func init() { func init() {
deleteSourceCmd.AddCommand(deleteSourceHelmCmd) deleteSourceCmd.AddCommand(deleteSourceHelmCmd)
} }
func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var helmRepository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
return err
}
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this source",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting source %s in %s namespace", name, rootArgs.namespace)
err = kubeClient.Delete(ctx, &helmRepository)
if err != nil {
return err
}
logger.Successf("source deleted")
return nil
}

69
cmd/flux/docgen.go Normal file
View File

@@ -0,0 +1,69 @@
/*
Copyright 2021 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"path"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)
const fmTemplate = `---
title: "%s"
---
`
var (
cmdDocPath string
)
var docgenCmd = &cobra.Command{
Use: "docgen",
Short: "Generate the documentation for the CLI commands.",
Hidden: true,
RunE: docgenCmdRun,
}
func init() {
docgenCmd.Flags().StringVar(&cmdDocPath, "path", "./docs/cmd", "path to write the generated documentation to")
rootCmd.AddCommand(docgenCmd)
}
func docgenCmdRun(cmd *cobra.Command, args []string) error {
err := doc.GenMarkdownTreeCustom(rootCmd, cmdDocPath, frontmatterPrepender, linkHandler)
if err != nil {
return err
}
return nil
}
func frontmatterPrepender(filename string) string {
name := filepath.Base(filename)
base := strings.TrimSuffix(name, path.Ext(name))
title := strings.Replace(base, "_", " ", -1) + " command"
return fmt.Sprintf(fmTemplate, title)
}
func linkHandler(name string) string {
base := strings.TrimSuffix(name, path.Ext(name))
return "../" + strings.ToLower(base) + "/"
}

View File

@@ -20,7 +20,6 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
@@ -86,8 +85,7 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
} }
if export.list.len() == 0 { if export.list.len() == 0 {
logger.Failuref("no objects found in %s namespace", rootArgs.namespace) return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
return nil
} }
for i := 0; i < export.list.len(); i++ { for i := 0; i < export.list.len(); i++ {

View File

@@ -17,17 +17,9 @@ limitations under the License.
package main package main
import ( import (
"context" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
) )
var exportAlertCmd = &cobra.Command{ var exportAlertCmd = &cobra.Command{
@@ -38,62 +30,18 @@ var exportAlertCmd = &cobra.Command{
flux export alert --all > alerts.yaml flux export alert --all > alerts.yaml
# Export a Alert # Export a Alert
flux export alert main > main.yaml flux export alert main > main.yaml`,
`, RunE: exportCommand{
RunE: exportAlertCmdRun, object: alertAdapter{&notificationv1.Alert{}},
list: alertListAdapter{&notificationv1.AlertList{}},
}.run,
} }
func init() { func init() {
exportCmd.AddCommand(exportAlertCmd) exportCmd.AddCommand(exportAlertCmd)
} }
func exportAlertCmdRun(cmd *cobra.Command, args []string) error { func exportAlert(alert *notificationv1.Alert) interface{} {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
var list notificationv1.AlertList
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alerts found in %s namespace", rootArgs.namespace)
return nil
}
for _, alert := range list.Items {
if err := exportAlert(alert); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
return exportAlert(alert)
}
return nil
}
func exportAlert(alert notificationv1.Alert) error {
gvk := notificationv1.GroupVersion.WithKind("Alert") gvk := notificationv1.GroupVersion.WithKind("Alert")
export := notificationv1.Alert{ export := notificationv1.Alert{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@@ -109,12 +57,13 @@ func exportAlert(alert notificationv1.Alert) error {
Spec: alert.Spec, Spec: alert.Spec,
} }
data, err := yaml.Marshal(export) return export
if err != nil { }
return err
} func (ex alertAdapter) export() interface{} {
return exportAlert(ex.Alert)
fmt.Println("---") }
fmt.Println(resourceToString(data))
return nil func (ex alertListAdapter) exportItem(i int) interface{} {
return exportAlert(&ex.AlertList.Items[i])
} }

View File

@@ -17,17 +17,9 @@ limitations under the License.
package main package main
import ( import (
"context" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
) )
var exportAlertProviderCmd = &cobra.Command{ var exportAlertProviderCmd = &cobra.Command{
@@ -38,62 +30,18 @@ var exportAlertProviderCmd = &cobra.Command{
flux export alert-provider --all > alert-providers.yaml flux export alert-provider --all > alert-providers.yaml
# Export a Provider # Export a Provider
flux export alert-provider slack > slack.yaml flux export alert-provider slack > slack.yaml`,
`, RunE: exportCommand{
RunE: exportAlertProviderCmdRun, object: alertProviderAdapter{&notificationv1.Provider{}},
list: alertProviderListAdapter{&notificationv1.ProviderList{}},
}.run,
} }
func init() { func init() {
exportCmd.AddCommand(exportAlertProviderCmd) exportCmd.AddCommand(exportAlertProviderCmd)
} }
func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error { func exportAlertProvider(alertProvider *notificationv1.Provider) interface{} {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
var list notificationv1.ProviderList
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alertproviders found in %s namespace", rootArgs.namespace)
return nil
}
for _, alertProvider := range list.Items {
if err := exportAlertProvider(alertProvider); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
return err
}
return exportAlertProvider(alertProvider)
}
return nil
}
func exportAlertProvider(alertProvider notificationv1.Provider) error {
gvk := notificationv1.GroupVersion.WithKind("Provider") gvk := notificationv1.GroupVersion.WithKind("Provider")
export := notificationv1.Provider{ export := notificationv1.Provider{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@@ -108,13 +56,13 @@ func exportAlertProvider(alertProvider notificationv1.Provider) error {
}, },
Spec: alertProvider.Spec, Spec: alertProvider.Spec,
} }
return export
data, err := yaml.Marshal(export) }
if err != nil {
return err func (ex alertProviderAdapter) export() interface{} {
} return exportAlertProvider(ex.Provider)
}
fmt.Println("---")
fmt.Println(resourceToString(data)) func (ex alertProviderListAdapter) exportItem(i int) interface{} {
return nil return exportAlertProvider(&ex.ProviderList.Items[i])
} }

View File

@@ -17,17 +17,9 @@ limitations under the License.
package main package main
import ( import (
"context" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
) )
var exportHelmReleaseCmd = &cobra.Command{ var exportHelmReleaseCmd = &cobra.Command{
@@ -39,62 +31,18 @@ var exportHelmReleaseCmd = &cobra.Command{
flux export helmrelease --all > kustomizations.yaml flux export helmrelease --all > kustomizations.yaml
# Export a HelmRelease # Export a HelmRelease
flux export hr my-app > app-release.yaml flux export hr my-app > app-release.yaml`,
`, RunE: exportCommand{
RunE: exportHelmReleaseCmdRun, object: helmReleaseAdapter{&helmv2.HelmRelease{}},
list: helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
}.run,
} }
func init() { func init() {
exportCmd.AddCommand(exportHelmReleaseCmd) exportCmd.AddCommand(exportHelmReleaseCmd)
} }
func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { func exportHelmRelease(helmRelease *helmv2.HelmRelease) interface{} {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
var list helmv2.HelmReleaseList
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no helmrelease found in %s namespace", rootArgs.namespace)
return nil
}
for _, helmRelease := range list.Items {
if err := exportHelmRelease(helmRelease); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var helmRelease helmv2.HelmRelease
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
return exportHelmRelease(helmRelease)
}
return nil
}
func exportHelmRelease(helmRelease helmv2.HelmRelease) error {
gvk := helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind) gvk := helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)
export := helmv2.HelmRelease{ export := helmv2.HelmRelease{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@@ -109,13 +57,13 @@ func exportHelmRelease(helmRelease helmv2.HelmRelease) error {
}, },
Spec: helmRelease.Spec, Spec: helmRelease.Spec,
} }
return export
data, err := yaml.Marshal(export) }
if err != nil {
return err func (ex helmReleaseAdapter) export() interface{} {
} return exportHelmRelease(ex.HelmRelease)
}
fmt.Println("---")
fmt.Println(resourceToString(data)) func (ex helmReleaseListAdapter) exportItem(i int) interface{} {
return nil return exportHelmRelease(&ex.HelmReleaseList.Items[i])
} }

View File

@@ -31,8 +31,7 @@ var exportImagePolicyCmd = &cobra.Command{
flux export image policy --all > image-policies.yaml flux export image policy --all > image-policies.yaml
# Export a specific policy # Export a specific policy
flux export image policy alpine1x > alpine1x.yaml flux export image policy alpine1x > alpine1x.yaml`,
`,
RunE: exportCommand{ RunE: exportCommand{
object: imagePolicyAdapter{&imagev1.ImagePolicy{}}, object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}}, list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},

View File

@@ -31,8 +31,7 @@ var exportImageRepositoryCmd = &cobra.Command{
flux export image repository --all > image-repositories.yaml flux export image repository --all > image-repositories.yaml
# Export a specific ImageRepository resource # Export a specific ImageRepository resource
flux export image repository alpine > alpine.yaml flux export image repository alpine > alpine.yaml`,
`,
RunE: exportCommand{ RunE: exportCommand{
object: imageRepositoryAdapter{&imagev1.ImageRepository{}}, object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}}, list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},

View File

@@ -31,8 +31,7 @@ var exportImageUpdateCmd = &cobra.Command{
flux export image update --all > updates.yaml flux export image update --all > updates.yaml
# Export a specific automation # Export a specific automation
flux export image update latest-images > latest.yaml flux export image update latest-images > latest.yaml`,
`,
RunE: exportCommand{ RunE: exportCommand{
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}}, object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}}, list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},

View File

@@ -17,17 +17,9 @@ limitations under the License.
package main package main
import ( import (
"context" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
) )
var exportKsCmd = &cobra.Command{ var exportKsCmd = &cobra.Command{
@@ -39,62 +31,18 @@ var exportKsCmd = &cobra.Command{
flux export kustomization --all > kustomizations.yaml flux export kustomization --all > kustomizations.yaml
# Export a Kustomization # Export a Kustomization
flux export kustomization my-app > kustomization.yaml flux export kustomization my-app > kustomization.yaml`,
`, RunE: exportCommand{
RunE: exportKsCmdRun, object: kustomizationAdapter{&kustomizev1.Kustomization{}},
list: kustomizationListAdapter{&kustomizev1.KustomizationList{}},
}.run,
} }
func init() { func init() {
exportCmd.AddCommand(exportKsCmd) exportCmd.AddCommand(exportKsCmd)
} }
func exportKsCmdRun(cmd *cobra.Command, args []string) error { func exportKs(kustomization *kustomizev1.Kustomization) interface{} {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("kustomization name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
var list kustomizev1.KustomizationList
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no kustomizations found in %s namespace", rootArgs.namespace)
return nil
}
for _, kustomization := range list.Items {
if err := exportKs(kustomization); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var kustomization kustomizev1.Kustomization
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
return exportKs(kustomization)
}
return nil
}
func exportKs(kustomization kustomizev1.Kustomization) error {
gvk := kustomizev1.GroupVersion.WithKind("Kustomization") gvk := kustomizev1.GroupVersion.WithKind("Kustomization")
export := kustomizev1.Kustomization{ export := kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@@ -110,12 +58,13 @@ func exportKs(kustomization kustomizev1.Kustomization) error {
Spec: kustomization.Spec, Spec: kustomization.Spec,
} }
data, err := yaml.Marshal(export) return export
if err != nil { }
return err
} func (ex kustomizationAdapter) export() interface{} {
return exportKs(ex.Kustomization)
fmt.Println("---") }
fmt.Println(resourceToString(data))
return nil func (ex kustomizationListAdapter) exportItem(i int) interface{} {
return exportKs(&ex.KustomizationList.Items[i])
} }

View File

@@ -17,17 +17,9 @@ limitations under the License.
package main package main
import ( import (
"context" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
) )
var exportReceiverCmd = &cobra.Command{ var exportReceiverCmd = &cobra.Command{
@@ -38,62 +30,18 @@ var exportReceiverCmd = &cobra.Command{
flux export receiver --all > receivers.yaml flux export receiver --all > receivers.yaml
# Export a Receiver # Export a Receiver
flux export receiver main > main.yaml flux export receiver main > main.yaml`,
`, RunE: exportCommand{
RunE: exportReceiverCmdRun, list: receiverListAdapter{&notificationv1.ReceiverList{}},
object: receiverAdapter{&notificationv1.Receiver{}},
}.run,
} }
func init() { func init() {
exportCmd.AddCommand(exportReceiverCmd) exportCmd.AddCommand(exportReceiverCmd)
} }
func exportReceiverCmdRun(cmd *cobra.Command, args []string) error { func exportReceiver(receiver *notificationv1.Receiver) interface{} {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
var list notificationv1.ReceiverList
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no receivers found in %s namespace", rootArgs.namespace)
return nil
}
for _, receiver := range list.Items {
if err := exportReceiver(receiver); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
return exportReceiver(receiver)
}
return nil
}
func exportReceiver(receiver notificationv1.Receiver) error {
gvk := notificationv1.GroupVersion.WithKind("Receiver") gvk := notificationv1.GroupVersion.WithKind("Receiver")
export := notificationv1.Receiver{ export := notificationv1.Receiver{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@@ -109,12 +57,13 @@ func exportReceiver(receiver notificationv1.Receiver) error {
Spec: receiver.Spec, Spec: receiver.Spec,
} }
data, err := yaml.Marshal(export) return export
if err != nil { }
return err
} func (ex receiverAdapter) export() interface{} {
return exportReceiver(ex.Receiver)
fmt.Println("---") }
fmt.Println(resourceToString(data))
return nil func (ex receiverListAdapter) exportItem(i int) interface{} {
return exportReceiver(&ex.ReceiverList.Items[i])
} }

133
cmd/flux/export_secret.go Normal file
View File

@@ -0,0 +1,133 @@
/*
Copyright 2021 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// exportableWithSecret represents a type that you can fetch from the Kubernetes
// API, get a secretRef from the spec, then tidy up for serialising.
type exportableWithSecret interface {
adapter
exportable
secret() *types.NamespacedName
}
// exportableWithSecretList represents a type that has a list of values, each of
// which is exportableWithSecret.
type exportableWithSecretList interface {
listAdapter
exportableList
secretItem(i int) *types.NamespacedName
}
type exportWithSecretCommand struct {
apiType
object exportableWithSecret
list exportableWithSecretList
}
func (export exportWithSecretCommand) run(cmd *cobra.Command, args []string) error {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if export.list.len() == 0 {
return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
}
for i := 0; i < export.list.len(); i++ {
if err = printExport(export.list.exportItem(i)); err != nil {
return err
}
if exportSourceWithCred {
if export.list.secretItem(i) != nil {
namespacedName := *export.list.secretItem(i)
return printSecretCredentials(ctx, kubeClient, namespacedName)
}
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())
if err != nil {
return err
}
if err := printExport(export.object.export()); err != nil {
return err
}
if exportSourceWithCred {
if export.object.secret() != nil {
namespacedName := *export.object.secret()
return printSecretCredentials(ctx, kubeClient, namespacedName)
}
}
}
return nil
}
func printSecretCredentials(ctx context.Context, kubeClient client.Client, nsName types.NamespacedName) error {
var cred corev1.Secret
err := kubeClient.Get(ctx, nsName, &cred)
if err != nil {
return fmt.Errorf("failed to retrieve secret %s, error: %w", nsName.Name, err)
}
exported := corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: nsName.Name,
Namespace: nsName.Namespace,
},
Data: cred.Data,
Type: cred.Type,
}
return printExport(exported)
}

View File

@@ -17,94 +17,32 @@ limitations under the License.
package main package main
import ( import (
"context" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var exportSourceBucketCmd = &cobra.Command{ var exportSourceBucketCmd = &cobra.Command{
Use: "bucket [name]", Use: "bucket [name]",
Short: "Export Bucket sources in YAML format", Short: "Export Bucket sources in YAML format",
Long: "The export source git command exports on or all Bucket sources in YAML format.", Long: "The export source git command exports one or all Bucket sources in YAML format.",
Example: ` # Export all Bucket sources Example: ` # Export all Bucket sources
flux export source bucket --all > sources.yaml flux export source bucket --all > sources.yaml
# Export a Bucket source including the static credentials # Export a Bucket source including the static credentials
flux export source bucket my-bucket --with-credentials > source.yaml flux export source bucket my-bucket --with-credentials > source.yaml`,
`, RunE: exportWithSecretCommand{
RunE: exportSourceBucketCmdRun, list: bucketListAdapter{&sourcev1.BucketList{}},
object: bucketAdapter{&sourcev1.Bucket{}},
}.run,
} }
func init() { func init() {
exportSourceCmd.AddCommand(exportSourceBucketCmd) exportSourceCmd.AddCommand(exportSourceBucketCmd)
} }
func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error { func exportBucket(source *sourcev1.Bucket) interface{} {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
var list sourcev1.BucketList
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
return nil
}
for _, bucket := range list.Items {
if err := exportBucket(bucket); err != nil {
return err
}
if exportSourceWithCred {
if err := exportBucketCredentials(ctx, kubeClient, bucket); err != nil {
return err
}
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var bucket sourcev1.Bucket
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return err
}
if err := exportBucket(bucket); err != nil {
return err
}
if exportSourceWithCred {
return exportBucketCredentials(ctx, kubeClient, bucket)
}
}
return nil
}
func exportBucket(source sourcev1.Bucket) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.BucketKind) gvk := sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)
export := sourcev1.Bucket{ export := sourcev1.Bucket{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@@ -119,49 +57,34 @@ func exportBucket(source sourcev1.Bucket) error {
}, },
Spec: source.Spec, Spec: source.Spec,
} }
return export
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
} }
func exportBucketCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.Bucket) error { func getBucketSecret(source *sourcev1.Bucket) *types.NamespacedName {
if source.Spec.SecretRef != nil { if source.Spec.SecretRef != nil {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: source.Namespace, Namespace: source.Namespace,
Name: source.Spec.SecretRef.Name, Name: source.Spec.SecretRef.Name,
} }
var cred corev1.Secret
err := kubeClient.Get(ctx, namespacedName, &cred)
if err != nil {
return fmt.Errorf("failed to retrieve secret %s, error: %w", namespacedName.Name, err)
}
exported := corev1.Secret{ return &namespacedName
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: namespacedName.Name,
Namespace: namespacedName.Namespace,
},
Data: cred.Data,
Type: cred.Type,
}
data, err := yaml.Marshal(exported)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
} }
return nil return nil
} }
func (ex bucketAdapter) secret() *types.NamespacedName {
return getBucketSecret(ex.Bucket)
}
func (ex bucketListAdapter) secretItem(i int) *types.NamespacedName {
return getBucketSecret(&ex.BucketList.Items[i])
}
func (ex bucketAdapter) export() interface{} {
return exportBucket(ex.Bucket)
}
func (ex bucketListAdapter) exportItem(i int) interface{} {
return exportBucket(&ex.BucketList.Items[i])
}

View File

@@ -17,94 +17,32 @@ limitations under the License.
package main package main
import ( import (
"context" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var exportSourceGitCmd = &cobra.Command{ var exportSourceGitCmd = &cobra.Command{
Use: "git [name]", Use: "git [name]",
Short: "Export GitRepository sources in YAML format", Short: "Export GitRepository sources in YAML format",
Long: "The export source git command exports on or all GitRepository sources in YAML format.", Long: "The export source git command exports one or all GitRepository sources in YAML format.",
Example: ` # Export all GitRepository sources Example: ` # Export all GitRepository sources
flux export source git --all > sources.yaml flux export source git --all > sources.yaml
# Export a GitRepository source including the SSH key pair or basic auth credentials # Export a GitRepository source including the SSH key pair or basic auth credentials
flux export source git my-private-repo --with-credentials > source.yaml flux export source git my-private-repo --with-credentials > source.yaml`,
`, RunE: exportWithSecretCommand{
RunE: exportSourceGitCmdRun, object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
list: gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
}.run,
} }
func init() { func init() {
exportSourceCmd.AddCommand(exportSourceGitCmd) exportSourceCmd.AddCommand(exportSourceGitCmd)
} }
func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error { func exportGit(source *sourcev1.GitRepository) interface{} {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
var list sourcev1.GitRepositoryList
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
return nil
}
for _, repository := range list.Items {
if err := exportGit(repository); err != nil {
return err
}
if exportSourceWithCred {
if err := exportGitCredentials(ctx, kubeClient, repository); err != nil {
return err
}
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var repository sourcev1.GitRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
if err := exportGit(repository); err != nil {
return err
}
if exportSourceWithCred {
return exportGitCredentials(ctx, kubeClient, repository)
}
}
return nil
}
func exportGit(source sourcev1.GitRepository) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind) gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)
export := sourcev1.GitRepository{ export := sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@@ -120,48 +58,33 @@ func exportGit(source sourcev1.GitRepository) error {
Spec: source.Spec, Spec: source.Spec,
} }
data, err := yaml.Marshal(export) return export
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
} }
func exportGitCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.GitRepository) error { func getGitSecret(source *sourcev1.GitRepository) *types.NamespacedName {
if source.Spec.SecretRef != nil { if source.Spec.SecretRef != nil {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: source.Namespace, Namespace: source.Namespace,
Name: source.Spec.SecretRef.Name, Name: source.Spec.SecretRef.Name,
} }
var cred corev1.Secret return &namespacedName
err := kubeClient.Get(ctx, namespacedName, &cred)
if err != nil {
return fmt.Errorf("failed to retrieve secret %s, error: %w", namespacedName.Name, err)
}
exported := corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: namespacedName.Name,
Namespace: namespacedName.Namespace,
},
Data: cred.Data,
Type: cred.Type,
}
data, err := yaml.Marshal(exported)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
} }
return nil return nil
} }
func (ex gitRepositoryAdapter) secret() *types.NamespacedName {
return getGitSecret(ex.GitRepository)
}
func (ex gitRepositoryListAdapter) secretItem(i int) *types.NamespacedName {
return getGitSecret(&ex.GitRepositoryList.Items[i])
}
func (ex gitRepositoryAdapter) export() interface{} {
return exportGit(ex.GitRepository)
}
func (ex gitRepositoryListAdapter) exportItem(i int) interface{} {
return exportGit(&ex.GitRepositoryList.Items[i])
}

View File

@@ -17,94 +17,32 @@ limitations under the License.
package main package main
import ( import (
"context" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var exportSourceHelmCmd = &cobra.Command{ var exportSourceHelmCmd = &cobra.Command{
Use: "helm [name]", Use: "helm [name]",
Short: "Export HelmRepository sources in YAML format", Short: "Export HelmRepository sources in YAML format",
Long: "The export source git command exports on or all HelmRepository sources in YAML format.", Long: "The export source git command exports one or all HelmRepository sources in YAML format.",
Example: ` # Export all HelmRepository sources Example: ` # Export all HelmRepository sources
flux export source helm --all > sources.yaml flux export source helm --all > sources.yaml
# Export a HelmRepository source including the basic auth credentials # Export a HelmRepository source including the basic auth credentials
flux export source helm my-private-repo --with-credentials > source.yaml flux export source helm my-private-repo --with-credentials > source.yaml`,
`, RunE: exportWithSecretCommand{
RunE: exportSourceHelmCmdRun, list: helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
}.run,
} }
func init() { func init() {
exportSourceCmd.AddCommand(exportSourceHelmCmd) exportSourceCmd.AddCommand(exportSourceHelmCmd)
} }
func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error { func exportHelmRepository(source *sourcev1.HelmRepository) interface{} {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportArgs.all {
var list sourcev1.HelmRepositoryList
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
return nil
}
for _, repository := range list.Items {
if err := exportHelmRepository(repository); err != nil {
return err
}
if exportSourceWithCred {
if err := exportHelmCredentials(ctx, kubeClient, repository); err != nil {
return err
}
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var repository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
if err := exportHelmRepository(repository); err != nil {
return err
}
if exportSourceWithCred {
return exportHelmCredentials(ctx, kubeClient, repository)
}
}
return nil
}
func exportHelmRepository(source sourcev1.HelmRepository) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind) gvk := sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)
export := sourcev1.HelmRepository{ export := sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@@ -119,49 +57,32 @@ func exportHelmRepository(source sourcev1.HelmRepository) error {
}, },
Spec: source.Spec, Spec: source.Spec,
} }
return export
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
} }
func exportHelmCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.HelmRepository) error { func getHelmSecret(source *sourcev1.HelmRepository) *types.NamespacedName {
if source.Spec.SecretRef != nil { if source.Spec.SecretRef != nil {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: source.Namespace, Namespace: source.Namespace,
Name: source.Spec.SecretRef.Name, Name: source.Spec.SecretRef.Name,
} }
var cred corev1.Secret return &namespacedName
err := kubeClient.Get(ctx, namespacedName, &cred)
if err != nil {
return fmt.Errorf("failed to retrieve secret %s, error: %w", namespacedName.Name, err)
}
exported := corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: namespacedName.Name,
Namespace: namespacedName.Namespace,
},
Data: cred.Data,
Type: cred.Type,
}
data, err := yaml.Marshal(exported)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
} }
return nil return nil
} }
func (ex helmRepositoryAdapter) secret() *types.NamespacedName {
return getHelmSecret(ex.HelmRepository)
}
func (ex helmRepositoryListAdapter) secretItem(i int) *types.NamespacedName {
return getHelmSecret(&ex.HelmRepositoryList.Items[i])
}
func (ex helmRepositoryAdapter) export() interface{} {
return exportHelmRepository(ex.HelmRepository)
}
func (ex helmRepositoryListAdapter) exportItem(i int) interface{} {
return exportHelmRepository(&ex.HelmRepositoryList.Items[i])
}

View File

@@ -18,7 +18,9 @@ package main
import ( import (
"context" "context"
"fmt"
"os" "os"
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta" apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -32,8 +34,8 @@ import (
var getCmd = &cobra.Command{ var getCmd = &cobra.Command{
Use: "get", Use: "get",
Short: "Get sources and resources", Short: "Get the resources and their status",
Long: "The get sub-commands print the statuses of sources and resources.", Long: "The get sub-commands print the statuses of Flux resources.",
} }
type GetFlags struct { type GetFlags struct {
@@ -50,7 +52,7 @@ func init() {
type summarisable interface { type summarisable interface {
listAdapter listAdapter
summariseItem(i int, includeNamespace bool) []string summariseItem(i int, includeNamespace bool, includeKind bool) []string
headers(includeNamespace bool) []string headers(includeNamespace bool) []string
} }
@@ -63,11 +65,17 @@ func statusAndMessage(conditions []metav1.Condition) (string, string) {
return string(metav1.ConditionFalse), "waiting to be reconciled" return string(metav1.ConditionFalse), "waiting to be reconciled"
} }
func nameColumns(item named, includeNamespace bool) []string { func nameColumns(item named, includeNamespace bool, includeKind bool) []string {
if includeNamespace { name := item.GetName()
return []string{item.GetNamespace(), item.GetName()} if includeKind {
name = fmt.Sprintf("%s/%s",
strings.ToLower(item.GetObjectKind().GroupVersionKind().Kind),
item.GetName())
} }
return []string{item.GetName()} if includeNamespace {
return []string{item.GetNamespace(), name}
}
return []string{name}
} }
var namespaceHeader = []string{"Namespace"} var namespaceHeader = []string{"Namespace"}
@@ -100,17 +108,25 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
return err return err
} }
getAll := cmd.Use == "all"
if get.list.len() == 0 { if get.list.len() == 0 {
logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace) if !getAll {
logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace)
}
return nil return nil
} }
header := get.list.headers(getArgs.allNamespaces) header := get.list.headers(getArgs.allNamespaces)
var rows [][]string var rows [][]string
for i := 0; i < get.list.len(); i++ { for i := 0; i < get.list.len(); i++ {
row := get.list.summariseItem(i, getArgs.allNamespaces) row := get.list.summariseItem(i, getArgs.allNamespaces, getAll)
rows = append(rows, row) rows = append(rows, row)
} }
utils.PrintTable(os.Stdout, header, rows) utils.PrintTable(os.Stdout, header, rows)
if getAll {
fmt.Println()
}
return nil return nil
} }

View File

@@ -17,19 +17,11 @@ limitations under the License.
package main package main
import ( import (
"context"
"os"
"strconv" "strconv"
"strings" "strings"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta" "github.com/spf13/cobra"
) )
var getAlertCmd = &cobra.Command{ var getAlertCmd = &cobra.Command{
@@ -38,66 +30,27 @@ var getAlertCmd = &cobra.Command{
Short: "Get Alert statuses", Short: "Get Alert statuses",
Long: "The get alert command prints the statuses of the resources.", Long: "The get alert command prints the statuses of the resources.",
Example: ` # List all Alerts and their status Example: ` # List all Alerts and their status
flux get alerts flux get alerts`,
`, RunE: getCommand{
RunE: getAlertCmdRun, apiType: alertType,
list: &alertListAdapter{&notificationv1.AlertList{}},
}.run,
} }
func init() { func init() {
getCmd.AddCommand(getAlertCmd) getCmd.AddCommand(getAlertCmd)
} }
func getAlertCmdRun(cmd *cobra.Command, args []string) error { func (s alertListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) item := s.Items[i]
defer cancel() status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), status, msg, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) }
if err != nil {
return err func (s alertListAdapter) headers(includeNamespace bool) []string {
} headers := []string{"Name", "Ready", "Message", "Suspended"}
if includeNamespace {
var listOpts []client.ListOption return append(namespaceHeader, headers...)
if !getArgs.allNamespaces { }
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace)) return headers
}
var list notificationv1.AlertList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alerts found in %s namespace", rootArgs.namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Suspended"}
if getArgs.allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, alert := range list.Items {
row := []string{}
if c := apimeta.FindStatusCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
alert.GetName(),
string(c.Status),
c.Message,
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
}
} else {
row = []string{
alert.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
}
}
if getArgs.allNamespaces {
row = append([]string{alert.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
} }

View File

@@ -17,17 +17,8 @@ limitations under the License.
package main package main
import ( import (
"context"
"os"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta" "github.com/spf13/cobra"
) )
var getAlertProviderCmd = &cobra.Command{ var getAlertProviderCmd = &cobra.Command{
@@ -36,64 +27,27 @@ var getAlertProviderCmd = &cobra.Command{
Short: "Get Provider statuses", Short: "Get Provider statuses",
Long: "The get alert-provider command prints the statuses of the resources.", Long: "The get alert-provider command prints the statuses of the resources.",
Example: ` # List all Providers and their status Example: ` # List all Providers and their status
flux get alert-providers flux get alert-providers`,
`, RunE: getCommand{
RunE: getAlertProviderCmdRun, apiType: alertProviderType,
list: alertProviderListAdapter{&notificationv1.ProviderList{}},
}.run,
} }
func init() { func init() {
getCmd.AddCommand(getAlertProviderCmd) getCmd.AddCommand(getAlertProviderCmd)
} }
func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error { func (s alertProviderListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) item := s.Items[i]
defer cancel() status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), status, msg)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) }
if err != nil {
return err func (s alertProviderListAdapter) headers(includeNamespace bool) []string {
} headers := []string{"Name", "Ready", "Message"}
if includeNamespace {
var listOpts []client.ListOption return append(namespaceHeader, headers...)
if !getArgs.allNamespaces { }
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace)) return headers
}
var list notificationv1.ProviderList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no providers found in %s namespace", rootArgs.namespace)
return nil
}
header := []string{"Name", "Ready", "Message"}
if getArgs.allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, provider := range list.Items {
row := []string{}
if c := apimeta.FindStatusCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
provider.GetName(),
string(c.Status),
c.Message,
}
} else {
row = []string{
provider.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
}
}
if getArgs.allNamespaces {
row = append([]string{provider.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
} }

View File

@@ -30,8 +30,7 @@ var getHelmReleaseCmd = &cobra.Command{
Short: "Get HelmRelease statuses", Short: "Get HelmRelease statuses",
Long: "The get helmreleases command prints the statuses of the resources.", Long: "The get helmreleases command prints the statuses of the resources.",
Example: ` # List all Helm releases and their status Example: ` # List all Helm releases and their status
flux get helmreleases flux get helmreleases`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: helmReleaseType, apiType: helmReleaseType,
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}}, list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
@@ -42,11 +41,11 @@ func init() {
getCmd.AddCommand(getHelmReleaseCmd) getCmd.AddCommand(getHelmReleaseCmd)
} }
func (a helmReleaseListAdapter) summariseItem(i int, includeNamespace bool) []string { func (a helmReleaseListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i] item := a.Items[i]
revision := item.Status.LastAppliedRevision revision := item.Status.LastAppliedRevision
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace), return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }

65
cmd/flux/get_image_all.go Normal file
View File

@@ -0,0 +1,65 @@
/*
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 (
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
"github.com/spf13/cobra"
)
var getImageAllCmd = &cobra.Command{
Use: "all",
Short: "Get all image statuses",
Long: "The get image sub-commands print the statuses of all image objects.",
Example: ` # List all image objects in a namespace
flux get images all --namespace=flux-system
# List all image objects in all namespaces
flux get images all --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
c := getCommand{
apiType: imageRepositoryType,
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
}
c = getCommand{
apiType: imagePolicyType,
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
}
c = getCommand{
apiType: imageUpdateAutomationType,
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
}
return nil
},
}
func init() {
getImageCmd.AddCommand(getImageAllCmd)
}

View File

@@ -30,8 +30,7 @@ var getImagePolicyCmd = &cobra.Command{
flux get image policy flux get image policy
# List image policies from all namespaces # List image policies from all namespaces
flux get image policy --all-namespaces flux get image policy --all-namespaces`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: imagePolicyType, apiType: imagePolicyType,
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}}, list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
@@ -42,10 +41,10 @@ func init() {
getImageCmd.AddCommand(getImagePolicyCmd) getImageCmd.AddCommand(getImagePolicyCmd)
} }
func (s imagePolicyListAdapter) summariseItem(i int, includeNamespace bool) []string { func (s imagePolicyListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace), status, msg, item.Status.LatestImage) return append(nameColumns(&item, includeNamespace, includeKind), status, msg, item.Status.LatestImage)
} }
func (s imagePolicyListAdapter) headers(includeNamespace bool) []string { func (s imagePolicyListAdapter) headers(includeNamespace bool) []string {

View File

@@ -34,8 +34,7 @@ var getImageRepositoryCmd = &cobra.Command{
flux get image repository flux get image repository
# List image repositories from all namespaces # List image repositories from all namespaces
flux get image repository --all-namespaces flux get image repository --all-namespaces`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: imageRepositoryType, apiType: imageRepositoryType,
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}}, list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
@@ -46,14 +45,14 @@ func init() {
getImageCmd.AddCommand(getImageRepositoryCmd) getImageCmd.AddCommand(getImageRepositoryCmd)
} }
func (s imageRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string { func (s imageRepositoryListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
var lastScan string var lastScan string
if item.Status.LastScanResult != nil { if item.Status.LastScanResult != nil {
lastScan = item.Status.LastScanResult.ScanTime.Time.Format(time.RFC3339) lastScan = item.Status.LastScanResult.ScanTime.Time.Format(time.RFC3339)
} }
return append(nameColumns(&item, includeNamespace), return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, lastScan, strings.Title(strconv.FormatBool(item.Spec.Suspend))) status, msg, lastScan, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }

View File

@@ -34,8 +34,7 @@ var getImageUpdateCmd = &cobra.Command{
flux get image update flux get image update
# List image update automations from all namespaces # List image update automations from all namespaces
flux get image update --all-namespaces flux get image update --all-namespaces`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: imageUpdateAutomationType, apiType: imageUpdateAutomationType,
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}}, list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
@@ -46,14 +45,14 @@ func init() {
getImageCmd.AddCommand(getImageUpdateCmd) getImageCmd.AddCommand(getImageUpdateCmd)
} }
func (s imageUpdateAutomationListAdapter) summariseItem(i int, includeNamespace bool) []string { func (s imageUpdateAutomationListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
var lastRun string var lastRun string
if item.Status.LastAutomationRunTime != nil { if item.Status.LastAutomationRunTime != nil {
lastRun = item.Status.LastAutomationRunTime.Time.Format(time.RFC3339) lastRun = item.Status.LastAutomationRunTime.Time.Format(time.RFC3339)
} }
return append(nameColumns(&item, includeNamespace), status, msg, lastRun, strings.Title(strconv.FormatBool(item.Spec.Suspend))) return append(nameColumns(&item, includeNamespace, includeKind), status, msg, lastRun, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []string { func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []string {

View File

@@ -30,8 +30,7 @@ var getKsCmd = &cobra.Command{
Short: "Get Kustomization statuses", Short: "Get Kustomization statuses",
Long: "The get kustomizations command prints the statuses of the resources.", Long: "The get kustomizations command prints the statuses of the resources.",
Example: ` # List all kustomizations and their status Example: ` # List all kustomizations and their status
flux get kustomizations flux get kustomizations`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: kustomizationType, apiType: kustomizationType,
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}}, list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
@@ -42,11 +41,11 @@ func init() {
getCmd.AddCommand(getKsCmd) getCmd.AddCommand(getKsCmd)
} }
func (a kustomizationListAdapter) summariseItem(i int, includeNamespace bool) []string { func (a kustomizationListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i] item := a.Items[i]
revision := item.Status.LastAppliedRevision revision := item.Status.LastAppliedRevision
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace), return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }

View File

@@ -17,19 +17,11 @@ limitations under the License.
package main package main
import ( import (
"context"
"os"
"strconv" "strconv"
"strings" "strings"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta" "github.com/spf13/cobra"
) )
var getReceiverCmd = &cobra.Command{ var getReceiverCmd = &cobra.Command{
@@ -38,63 +30,27 @@ var getReceiverCmd = &cobra.Command{
Short: "Get Receiver statuses", Short: "Get Receiver statuses",
Long: "The get receiver command prints the statuses of the resources.", Long: "The get receiver command prints the statuses of the resources.",
Example: ` # List all Receiver and their status Example: ` # List all Receiver and their status
flux get receivers flux get receivers`,
`, RunE: getCommand{
RunE: getReceiverCmdRun, apiType: receiverType,
list: receiverListAdapter{&notificationv1.ReceiverList{}},
}.run,
} }
func init() { func init() {
getCmd.AddCommand(getReceiverCmd) getCmd.AddCommand(getReceiverCmd)
} }
func getReceiverCmdRun(cmd *cobra.Command, args []string) error { func (s receiverListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) item := s.Items[i]
defer cancel() status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), status, msg, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) }
if err != nil {
return err func (s receiverListAdapter) headers(includeNamespace bool) []string {
} headers := []string{"Name", "Ready", "Message", "Suspended"}
if includeNamespace {
var listOpts []client.ListOption return append(namespaceHeader, headers...)
if !getArgs.allNamespaces { }
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace)) return headers
}
var list notificationv1.ReceiverList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no receivers found in %s namespace", rootArgs.namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Suspended"}
if getArgs.allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, receiver := range list.Items {
row := []string{}
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
receiver.GetName(),
string(c.Status),
c.Message,
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
}
} else {
row = []string{
receiver.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
}
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
} }

View File

@@ -0,0 +1,72 @@
/*
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 (
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
)
var getSourceAllCmd = &cobra.Command{
Use: "all",
Short: "Get all source statuses",
Long: "The get sources all command print the statuses of all sources.",
Example: ` # List all sources in a namespace
flux get sources all --namespace=flux-system
# List all sources in all namespaces
flux get sources all --all-namespaces`,
RunE: func(cmd *cobra.Command, args []string) error {
c := getCommand{
apiType: bucketType,
list: &bucketListAdapter{&sourcev1.BucketList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
}
c = getCommand{
apiType: gitRepositoryType,
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
}
c = getCommand{
apiType: helmRepositoryType,
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
}
c = getCommand{
apiType: helmChartType,
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
}
if err := c.run(cmd, args); err != nil {
logger.Failuref(err.Error())
}
return nil
},
}
func init() {
getSourceCmd.AddCommand(getSourceAllCmd)
}

View File

@@ -32,8 +32,7 @@ var getSourceBucketCmd = &cobra.Command{
flux get sources bucket flux get sources bucket
# List buckets from all namespaces # List buckets from all namespaces
flux get sources helm --all-namespaces flux get sources helm --all-namespaces`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: bucketType, apiType: bucketType,
list: &bucketListAdapter{&sourcev1.BucketList{}}, list: &bucketListAdapter{&sourcev1.BucketList{}},
@@ -44,14 +43,14 @@ func init() {
getSourceCmd.AddCommand(getSourceBucketCmd) getSourceCmd.AddCommand(getSourceBucketCmd)
} }
func (a *bucketListAdapter) summariseItem(i int, includeNamespace bool) []string { func (a *bucketListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i] item := a.Items[i]
var revision string var revision string
if item.GetArtifact() != nil { if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision revision = item.GetArtifact().Revision
} }
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace), return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }

View File

@@ -32,8 +32,7 @@ var getSourceHelmChartCmd = &cobra.Command{
flux get sources chart flux get sources chart
# List Helm charts from all namespaces # List Helm charts from all namespaces
flux get sources chart --all-namespaces flux get sources chart --all-namespaces`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: helmChartType, apiType: helmChartType,
list: &helmChartListAdapter{&sourcev1.HelmChartList{}}, list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
@@ -44,14 +43,14 @@ func init() {
getSourceCmd.AddCommand(getSourceHelmChartCmd) getSourceCmd.AddCommand(getSourceHelmChartCmd)
} }
func (a *helmChartListAdapter) summariseItem(i int, includeNamespace bool) []string { func (a *helmChartListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i] item := a.Items[i]
var revision string var revision string
if item.GetArtifact() != nil { if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision revision = item.GetArtifact().Revision
} }
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace), return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }

View File

@@ -32,8 +32,7 @@ var getSourceGitCmd = &cobra.Command{
flux get sources git flux get sources git
# List Git repositories from all namespaces # List Git repositories from all namespaces
flux get sources git --all-namespaces flux get sources git --all-namespaces`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: gitRepositoryType, apiType: gitRepositoryType,
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}}, list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
@@ -44,14 +43,14 @@ func init() {
getSourceCmd.AddCommand(getSourceGitCmd) getSourceCmd.AddCommand(getSourceGitCmd)
} }
func (a *gitRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string { func (a *gitRepositoryListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i] item := a.Items[i]
var revision string var revision string
if item.GetArtifact() != nil { if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision revision = item.GetArtifact().Revision
} }
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace), return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }

View File

@@ -32,8 +32,7 @@ var getSourceHelmCmd = &cobra.Command{
flux get sources helm flux get sources helm
# List Helm repositories from all namespaces # List Helm repositories from all namespaces
flux get sources helm --all-namespaces flux get sources helm --all-namespaces`,
`,
RunE: getCommand{ RunE: getCommand{
apiType: helmRepositoryType, apiType: helmRepositoryType,
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}}, list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
@@ -44,14 +43,14 @@ func init() {
getSourceCmd.AddCommand(getSourceHelmCmd) getSourceCmd.AddCommand(getSourceHelmCmd)
} }
func (a *helmRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string { func (a *helmRepositoryListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i] item := a.Items[i]
var revision string var revision string
if item.GetArtifact() != nil { if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision revision = item.GetArtifact().Revision
} }
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace), return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend))) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }

View File

@@ -30,6 +30,7 @@ import (
"github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install" "github.com/fluxcd/flux2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/pkg/status"
) )
var installCmd = &cobra.Command{ var installCmd = &cobra.Command{
@@ -50,84 +51,85 @@ If a previous version is installed, then an in-place upgrade will be performed.`
flux install --dry-run --verbose flux install --dry-run --verbose
# Write install manifests to file # Write install manifests to file
flux install --export > flux-system.yaml flux install --export > flux-system.yaml`,
`,
RunE: installCmdRun, RunE: installCmdRun,
} }
var ( type installFlags struct {
installExport bool export bool
installDryRun bool dryRun bool
installManifestsPath string version string
installVersion string defaultComponents []string
installDefaultComponents []string extraComponents []string
installExtraComponents []string registry string
installRegistry string imagePullSecret string
installImagePullSecret string branch string
installWatchAllNamespaces bool watchAllNamespaces bool
installNetworkPolicy bool networkPolicy bool
installArch flags.Arch manifestsPath string
installLogLevel = flags.LogLevel(rootArgs.defaults.LogLevel) arch flags.Arch
installClusterDomain string logLevel flags.LogLevel
installTolerationKeys []string tokenAuth bool
) clusterDomain string
tolerationKeys []string
}
var installArgs = NewInstallFlags()
func init() { func init() {
installCmd.Flags().BoolVar(&installExport, "export", false, installCmd.Flags().BoolVar(&installArgs.export, "export", false,
"write the install manifests to stdout and exit") "write the install manifests to stdout and exit")
installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false, installCmd.Flags().BoolVarP(&installArgs.dryRun, "dry-run", "", false,
"only print the object that would be applied") "only print the object that would be applied")
installCmd.Flags().StringVarP(&installVersion, "version", "v", rootArgs.defaults.Version, installCmd.Flags().StringVarP(&installArgs.version, "version", "v", "",
"toolkit version") "toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
installCmd.Flags().StringSliceVar(&installDefaultComponents, "components", rootArgs.defaults.Components, installCmd.Flags().StringSliceVar(&installArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
installCmd.Flags().StringSliceVar(&installExtraComponents, "components-extra", nil, installCmd.Flags().StringSliceVar(&installArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts comma-separated values") "list of components in addition to those supplied or defaulted, accepts comma-separated values")
installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory") installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory")
installCmd.Flags().StringVar(&installRegistry, "registry", rootArgs.defaults.Registry, installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry,
"container registry where the toolkit images are published") "container registry where the toolkit images are published")
installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "", installCmd.Flags().StringVar(&installArgs.imagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry") "Kubernetes secret name used for pulling the toolkit images from a private registry")
installCmd.Flags().Var(&installArch, "arch", installArch.Description()) installCmd.Flags().Var(&installArgs.arch, "arch", installArgs.arch.Description())
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces, installCmd.Flags().BoolVar(&installArgs.watchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces,
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed") "watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description()) installCmd.Flags().Var(&installArgs.logLevel, "log-level", installArgs.logLevel.Description())
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy, installCmd.Flags().BoolVar(&installArgs.networkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy,
"deny ingress access to the toolkit controllers from other namespaces using network policies") "deny ingress access to the toolkit controllers from other namespaces using network policies")
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain") installCmd.Flags().StringVar(&installArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
installCmd.Flags().StringSliceVar(&installTolerationKeys, "toleration-keys", nil, installCmd.Flags().StringSliceVar(&installArgs.tolerationKeys, "toleration-keys", nil,
"list of toleration keys used to schedule the components pods onto nodes with matching taints") "list of toleration keys used to schedule the components pods onto nodes with matching taints")
installCmd.Flags().MarkHidden("manifests") installCmd.Flags().MarkHidden("manifests")
installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64") installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
rootCmd.AddCommand(installCmd) rootCmd.AddCommand(installCmd)
} }
func NewInstallFlags() installFlags {
return installFlags{
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
}
}
func installCmdRun(cmd *cobra.Command, args []string) error { func installCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
components := append(installDefaultComponents, installExtraComponents...) components := append(installArgs.defaultComponents, installArgs.extraComponents...)
err := utils.ValidateComponents(components) err := utils.ValidateComponents(components)
if err != nil { if err != nil {
return err return err
} }
if installVersion == install.MakeDefaultOptions().Version { if ver, err := getVersion(installArgs.version); err != nil {
installVersion, err = install.GetLatestVersion() return err
if err != nil {
return err
}
} else { } else {
if ok, err := install.ExistingVersion(installVersion); err != nil || !ok { installArgs.version = ver
if err == nil {
err = fmt.Errorf("targeted version '%s' does not exist", installVersion)
}
return err
}
} }
if !utils.CompatibleVersion(VERSION, installVersion) { if !installArgs.export {
return fmt.Errorf("targeted version '%s' is not compatible with your current version of flux (%s)", installVersion, VERSION) logger.Generatef("generating manifests")
} }
tmpDir, err := ioutil.TempDir("", rootArgs.namespace) tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
@@ -136,32 +138,36 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
if !installExport { manifestsBase := ""
logger.Generatef("generating manifests") if isEmbeddedVersion(installArgs.version) {
if err := writeEmbeddedManifests(tmpDir); err != nil {
return err
}
manifestsBase = tmpDir
} }
opts := install.Options{ opts := install.Options{
BaseURL: installManifestsPath, BaseURL: installArgs.manifestsPath,
Version: installVersion, Version: installArgs.version,
Namespace: rootArgs.namespace, Namespace: rootArgs.namespace,
Components: components, Components: components,
Registry: installRegistry, Registry: installArgs.registry,
ImagePullSecret: installImagePullSecret, ImagePullSecret: installArgs.imagePullSecret,
WatchAllNamespaces: installWatchAllNamespaces, WatchAllNamespaces: installArgs.watchAllNamespaces,
NetworkPolicy: installNetworkPolicy, NetworkPolicy: installArgs.networkPolicy,
LogLevel: installLogLevel.String(), LogLevel: installArgs.logLevel.String(),
NotificationController: rootArgs.defaults.NotificationController, NotificationController: rootArgs.defaults.NotificationController,
ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace), ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace),
Timeout: rootArgs.timeout, Timeout: rootArgs.timeout,
ClusterDomain: installClusterDomain, ClusterDomain: installArgs.clusterDomain,
TolerationKeys: installTolerationKeys, TolerationKeys: installArgs.tolerationKeys,
} }
if installManifestsPath == "" { if installArgs.manifestsPath == "" {
opts.BaseURL = install.MakeDefaultOptions().BaseURL opts.BaseURL = install.MakeDefaultOptions().BaseURL
} }
manifest, err := install.Generate(opts) manifest, err := install.Generate(opts, manifestsBase)
if err != nil { if err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }
@@ -172,9 +178,9 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
if rootArgs.verbose { if rootArgs.verbose {
fmt.Print(manifest.Content) fmt.Print(manifest.Content)
} else if installExport { } else if installArgs.export {
fmt.Println("---") fmt.Println("---")
fmt.Println("# Flux version:", installVersion) fmt.Println("# Flux version:", installArgs.version)
fmt.Println("# Components:", strings.Join(components, ",")) fmt.Println("# Components:", strings.Join(components, ","))
fmt.Print(manifest.Content) fmt.Print(manifest.Content)
fmt.Println("---") fmt.Println("---")
@@ -189,26 +195,33 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
} }
kubectlArgs := []string{"apply", "-f", filepath.Join(tmpDir, manifest.Path)} kubectlArgs := []string{"apply", "-f", filepath.Join(tmpDir, manifest.Path)}
if installDryRun { if installArgs.dryRun {
kubectlArgs = append(kubectlArgs, "--dry-run=client") kubectlArgs = append(kubectlArgs, "--dry-run=client")
applyOutput = utils.ModeOS applyOutput = utils.ModeOS
} }
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil { if _, err := utils.ExecKubectlCommand(ctx, applyOutput, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed") return fmt.Errorf("install failed: %w", err)
} }
if installDryRun { if installArgs.dryRun {
logger.Successf("install dry-run finished") logger.Successf("install dry-run finished")
return nil return nil
} }
statusChecker, err := NewStatusChecker(time.Second, time.Minute) kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
componentRefs, err := buildComponentObjectRefs(components...)
if err != nil { if err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }
logger.Waitingf("verifying installation") logger.Waitingf("verifying installation")
if err := statusChecker.Assess(components...); err != nil { if err := statusChecker.Assess(componentRefs...); err != nil {
return fmt.Errorf("install failed") return fmt.Errorf("install failed")
} }

261
cmd/flux/logs.go Normal file
View File

@@ -0,0 +1,261 @@
/*
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"
"context"
"encoding/json"
"fmt"
"html/template"
"io"
"os"
"strings"
"sync"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
)
var logsCmd = &cobra.Command{
Use: "logs",
Short: "Display formatted logs for Flux components",
Long: "The logs command displays formatted logs from various Flux components.",
Example: ` # Print the reconciliation logs of all Flux custom resources in your cluster
flux logs --all-namespaces
# Stream logs for a particular log level
flux logs --follow --level=error --all-namespaces
# Filter logs by kind, name and namespace
flux logs --kind=Kustomization --name=podinfo --namespace=default
# Print logs when Flux is installed in a different namespace than flux-system
flux logs --flux-namespace=my-namespace
`,
RunE: logsCmdRun,
}
type logsFlags struct {
logLevel flags.LogLevel
follow bool
tail int64
kind string
name string
fluxNamespace string
allNamespaces bool
}
var logsArgs = &logsFlags{
tail: -1,
}
func init() {
logsCmd.Flags().Var(&logsArgs.logLevel, "level", logsArgs.logLevel.Description())
logsCmd.Flags().StringVarP(&logsArgs.kind, "kind", "", logsArgs.kind, "displays errors of a particular toolkit kind e.g GitRepository")
logsCmd.Flags().StringVarP(&logsArgs.name, "name", "", logsArgs.name, "specifies the name of the object logs to be displayed")
logsCmd.Flags().BoolVarP(&logsArgs.follow, "follow", "f", logsArgs.follow, "specifies if the logs should be streamed")
logsCmd.Flags().Int64VarP(&logsArgs.tail, "tail", "", logsArgs.tail, "lines of recent log file to display")
logsCmd.Flags().StringVarP(&logsArgs.fluxNamespace, "flux-namespace", "", rootArgs.defaults.Namespace, "the namespace where the Flux components are running")
logsCmd.Flags().BoolVarP(&logsArgs.allNamespaces, "all-namespaces", "A", false, "displays logs for objects across all namespaces")
rootCmd.AddCommand(logsCmd)
}
func logsCmdRun(cmd *cobra.Command, args []string) error {
fluxSelector := fmt.Sprintf("app.kubernetes.io/instance=%s", logsArgs.fluxNamespace)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
var pods []corev1.Pod
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
clientset, err := kubernetes.NewForConfig(cfg)
if err != nil {
return err
}
if len(args) > 0 {
return fmt.Errorf("no argument required")
}
pods, err = getPods(ctx, clientset, fluxSelector)
if err != nil {
return err
}
logOpts := &corev1.PodLogOptions{
Follow: logsArgs.follow,
}
if logsArgs.tail > -1 {
logOpts.TailLines = &logsArgs.tail
}
var requests []rest.ResponseWrapper
for _, pod := range pods {
req := clientset.CoreV1().Pods(logsArgs.fluxNamespace).GetLogs(pod.Name, logOpts)
requests = append(requests, req)
}
if logsArgs.follow && len(requests) > 1 {
return parallelPodLogs(ctx, requests)
}
return podLogs(ctx, requests)
}
func getPods(ctx context.Context, c *kubernetes.Clientset, label string) ([]corev1.Pod, error) {
var ret []corev1.Pod
opts := metav1.ListOptions{
LabelSelector: label,
}
deployList, err := c.AppsV1().Deployments(logsArgs.fluxNamespace).List(ctx, opts)
if err != nil {
return ret, err
}
for _, deploy := range deployList.Items {
label := deploy.Spec.Template.Labels
opts := metav1.ListOptions{
LabelSelector: createLabelStringFromMap(label),
}
podList, err := c.CoreV1().Pods(logsArgs.fluxNamespace).List(ctx, opts)
if err != nil {
return ret, err
}
ret = append(ret, podList.Items...)
}
return ret, nil
}
func parallelPodLogs(ctx context.Context, requests []rest.ResponseWrapper) error {
reader, writer := io.Pipe()
wg := &sync.WaitGroup{}
wg.Add(len(requests))
var mutex = &sync.Mutex{}
for _, request := range requests {
go func(req rest.ResponseWrapper) {
defer wg.Done()
if err := logRequest(mutex, ctx, req, os.Stdout); err != nil {
writer.CloseWithError(err)
return
}
}(request)
}
go func() {
wg.Wait()
writer.Close()
}()
_, err := io.Copy(os.Stdout, reader)
return err
}
func podLogs(ctx context.Context, requests []rest.ResponseWrapper) error {
mutex := &sync.Mutex{}
for _, req := range requests {
if err := logRequest(mutex, ctx, req, os.Stdout); err != nil {
return err
}
}
return nil
}
func createLabelStringFromMap(m map[string]string) string {
var strArr []string
for key, val := range m {
pair := fmt.Sprintf("%v=%v", key, val)
strArr = append(strArr, pair)
}
return strings.Join(strArr, ",")
}
func logRequest(mu *sync.Mutex, ctx context.Context, request rest.ResponseWrapper, w io.Writer) error {
stream, err := request.Stream(ctx)
if err != nil {
return err
}
defer stream.Close()
scanner := bufio.NewScanner(stream)
const logTmpl = "{{.Timestamp}} {{.Level}} {{.Kind}}{{if .Name}}/{{.Name}}.{{.Namespace}}{{end}} - {{.Message}} {{.Error}}\n"
t, err := template.New("log").Parse(logTmpl)
if err != nil {
return fmt.Errorf("unable to create template, err: %s", err)
}
for scanner.Scan() {
line := scanner.Text()
if !strings.HasPrefix(line, "{") {
continue
}
var l ControllerLogEntry
if err := json.Unmarshal([]byte(line), &l); err != nil {
logger.Failuref("parse error: %s", err)
break
}
mu.Lock()
filterPrintLog(t, &l)
mu.Unlock()
}
return nil
}
func filterPrintLog(t *template.Template, l *ControllerLogEntry) {
if logsArgs.logLevel != "" && logsArgs.logLevel != l.Level ||
logsArgs.kind != "" && strings.ToLower(logsArgs.kind) != strings.ToLower(l.Kind) ||
logsArgs.name != "" && strings.ToLower(logsArgs.name) != strings.ToLower(l.Name) ||
!logsArgs.allNamespaces && strings.ToLower(rootArgs.namespace) != strings.ToLower(l.Namespace) {
return
}
err := t.Execute(os.Stdout, l)
if err != nil {
logger.Failuref("log template error: %s", err)
}
}
type ControllerLogEntry struct {
Timestamp string `json:"ts"`
Level flags.LogLevel `json:"level"`
Message string `json:"msg"`
Error string `json:"error,omitempty"`
Logger string `json:"logger"`
Kind string `json:"reconciler kind,omitempty"`
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
}

View File

@@ -23,7 +23,6 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
"github.com/fluxcd/flux2/pkg/manifestgen/install" "github.com/fluxcd/flux2/pkg/manifestgen/install"
@@ -37,14 +36,15 @@ var rootCmd = &cobra.Command{
SilenceUsage: true, SilenceUsage: true,
SilenceErrors: true, SilenceErrors: true,
Short: "Command line utility for assembling Kubernetes CD pipelines", Short: "Command line utility for assembling Kubernetes CD pipelines",
Long: `Command line utility for assembling Kubernetes CD pipelines the GitOps way.`, Long: `
Command line utility for assembling Kubernetes CD pipelines the GitOps way.`,
Example: ` # Check prerequisites Example: ` # Check prerequisites
flux check --pre flux check --pre
# Install the latest version of Flux # Install the latest version of Flux
flux install --version=master flux install --version=master
# Create a source from a public Git repository # Create a source for a public Git repository
flux create source git webapp-latest \ flux create source git webapp-latest \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--branch=master \ --branch=master \
@@ -89,8 +89,7 @@ var rootCmd = &cobra.Command{
flux delete source git webapp-latest flux delete source git webapp-latest
# Uninstall Flux and delete CRDs # Uninstall Flux and delete CRDs
flux uninstall flux uninstall`,
`,
} }
var logger = stderrLogger{stderr: os.Stderr} var logger = stderrLogger{stderr: os.Stderr}
@@ -111,51 +110,40 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&rootArgs.namespace, "namespace", "n", rootArgs.defaults.Namespace, "the namespace scope for this operation") rootCmd.PersistentFlags().StringVarP(&rootArgs.namespace, "namespace", "n", rootArgs.defaults.Namespace, "the namespace scope for this operation")
rootCmd.PersistentFlags().DurationVar(&rootArgs.timeout, "timeout", 5*time.Minute, "timeout for this operation") rootCmd.PersistentFlags().DurationVar(&rootArgs.timeout, "timeout", 5*time.Minute, "timeout for this operation")
rootCmd.PersistentFlags().BoolVar(&rootArgs.verbose, "verbose", false, "print generated objects") rootCmd.PersistentFlags().BoolVar(&rootArgs.verbose, "verbose", false, "print generated objects")
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "",
"absolute path to the kubeconfig file")
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubecontext, "context", "", "", "kubernetes context to use") rootCmd.PersistentFlags().StringVarP(&rootArgs.kubecontext, "context", "", "", "kubernetes context to use")
rootCmd.DisableAutoGenTag = true
} }
func NewRootFlags() rootFlags { func NewRootFlags() rootFlags {
return rootFlags{ rf := rootFlags{
pollInterval: 2 * time.Second, pollInterval: 2 * time.Second,
defaults: install.MakeDefaultOptions(), defaults: install.MakeDefaultOptions(),
} }
rf.defaults.Version = "v" + VERSION
return rf
} }
func main() { func main() {
log.SetFlags(0) log.SetFlags(0)
generateDocs() configureKubeconfig()
kubeconfigFlag()
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
logger.Failuref("%v", err) logger.Failuref("%v", err)
os.Exit(1) os.Exit(1)
} }
} }
func kubeconfigFlag() { func configureKubeconfig() {
if home := homeDir(); home != "" { switch {
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", filepath.Join(home, ".kube", "config"), case len(rootArgs.kubeconfig) > 0:
"path to the kubeconfig file") case len(os.Getenv("KUBECONFIG")) > 0:
} else {
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "",
"absolute path to the kubeconfig file")
}
if len(os.Getenv("KUBECONFIG")) > 0 {
rootArgs.kubeconfig = os.Getenv("KUBECONFIG") rootArgs.kubeconfig = os.Getenv("KUBECONFIG")
} default:
} if home := homeDir(); len(home) > 0 {
rootArgs.kubeconfig = filepath.Join(home, ".kube", "config")
func generateDocs() {
args := os.Args[1:]
if len(args) > 0 && args[0] == "docgen" {
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "~/.kube/config",
"path to the kubeconfig file")
rootCmd.DisableAutoGenTag = true
err := doc.GenMarkdownTree(rootCmd, "./docs/cmd")
if err != nil {
log.Fatal(err)
} }
os.Exit(0)
} }
} }

View File

@@ -0,0 +1,31 @@
package main
import (
"embed"
"fmt"
"io/fs"
"os"
"path"
)
//go:embed manifests/*.yaml
var embeddedManifests embed.FS
func writeEmbeddedManifests(dir string) error {
manifests, err := fs.ReadDir(embeddedManifests, "manifests")
if err != nil {
return err
}
for _, manifest := range manifests {
data, err := fs.ReadFile(embeddedManifests, path.Join("manifests", manifest.Name()))
if err != nil {
return fmt.Errorf("reading file failed: %w", err)
}
err = os.WriteFile(path.Join(dir, manifest.Name()), data, 0666)
if err != nil {
return fmt.Errorf("writing file failed: %w", err)
}
}
return nil
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package main package main
import ( import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
) )
@@ -62,6 +63,7 @@ func (c universalAdapter) asClientObject() client.Object {
type named interface { type named interface {
GetName() string GetName() string
GetNamespace() string GetNamespace() string
GetObjectKind() schema.ObjectKind
SetName(string) SetName(string)
SetNamespace(string) SetNamespace(string)
} }

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

@@ -0,0 +1,51 @@
/*
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 (
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// notificationv1.Receiver
var receiverType = apiType{
kind: notificationv1.ReceiverKind,
humanKind: "receiver",
}
type receiverAdapter struct {
*notificationv1.Receiver
}
func (a receiverAdapter) asClientObject() client.Object {
return a.Receiver
}
// notificationv1.Receiver
type receiverListAdapter struct {
*notificationv1.ReceiverList
}
func (a receiverListAdapter) asClientList() client.ObjectList {
return a.ReceiverList
}
func (a receiverListAdapter) len() int {
return len(a.ReceiverList.Items)
}

View File

@@ -36,8 +36,7 @@ var reconcileAlertCmd = &cobra.Command{
Short: "Reconcile an Alert", Short: "Reconcile an Alert",
Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`, Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing alert Example: ` # Trigger a reconciliation for an existing alert
flux reconcile alert main flux reconcile alert main`,
`,
RunE: reconcileAlertCmdRun, RunE: reconcileAlertCmdRun,
} }

View File

@@ -36,8 +36,7 @@ var reconcileAlertProviderCmd = &cobra.Command{
Short: "Reconcile a Provider", Short: "Reconcile a Provider",
Long: `The reconcile alert-provider command triggers a reconciliation of a Provider resource and waits for it to finish.`, Long: `The reconcile alert-provider command triggers a reconciliation of a Provider resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing provider Example: ` # Trigger a reconciliation for an existing provider
flux reconcile alert-provider slack flux reconcile alert-provider slack`,
`,
RunE: reconcileAlertProviderCmdRun, RunE: reconcileAlertProviderCmdRun,
} }

View File

@@ -46,8 +46,7 @@ The reconcile kustomization command triggers a reconciliation of a HelmRelease r
flux reconcile hr podinfo flux reconcile hr podinfo
# Trigger a reconciliation of the HelmRelease's source and apply changes # Trigger a reconciliation of the HelmRelease's source and apply changes
flux reconcile hr podinfo --with-source flux reconcile hr podinfo --with-source`,
`,
RunE: reconcileHrCmdRun, RunE: reconcileHrCmdRun,
} }

View File

@@ -29,8 +29,7 @@ var reconcileImageRepositoryCmd = &cobra.Command{
Short: "Reconcile an ImageRepository", Short: "Reconcile an ImageRepository",
Long: `The reconcile image repository command triggers a reconciliation of an ImageRepository resource and waits for it to finish.`, Long: `The reconcile image repository command triggers a reconciliation of an ImageRepository resource and waits for it to finish.`,
Example: ` # Trigger an scan for an existing image repository Example: ` # Trigger an scan for an existing image repository
flux reconcile image repository alpine flux reconcile image repository alpine`,
`,
RunE: reconcileCommand{ RunE: reconcileCommand{
apiType: imageRepositoryType, apiType: imageRepositoryType,
object: imageRepositoryAdapter{&imagev1.ImageRepository{}}, object: imageRepositoryAdapter{&imagev1.ImageRepository{}},

View File

@@ -31,8 +31,7 @@ var reconcileImageUpdateCmd = &cobra.Command{
Short: "Reconcile an ImageUpdateAutomation", Short: "Reconcile an ImageUpdateAutomation",
Long: `The reconcile image update command triggers a reconciliation of an ImageUpdateAutomation resource and waits for it to finish.`, Long: `The reconcile image update command triggers a reconciliation of an ImageUpdateAutomation resource and waits for it to finish.`,
Example: ` # Trigger an automation run for an existing image update automation Example: ` # Trigger an automation run for an existing image update automation
flux reconcile image update latest-images flux reconcile image update latest-images`,
`,
RunE: reconcileCommand{ RunE: reconcileCommand{
apiType: imageUpdateAutomationType, apiType: imageUpdateAutomationType,
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}}, object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},

View File

@@ -45,8 +45,7 @@ The reconcile kustomization command triggers a reconciliation of a Kustomization
flux reconcile kustomization podinfo flux reconcile kustomization podinfo
# Trigger a sync of the Kustomization's source and apply changes # Trigger a sync of the Kustomization's source and apply changes
flux reconcile kustomization podinfo --with-source flux reconcile kustomization podinfo --with-source`,
`,
RunE: reconcileKsCmdRun, RunE: reconcileKsCmdRun,
} }

View File

@@ -36,8 +36,7 @@ var reconcileReceiverCmd = &cobra.Command{
Short: "Reconcile a Receiver", Short: "Reconcile a Receiver",
Long: `The reconcile receiver command triggers a reconciliation of a Receiver resource and waits for it to finish.`, Long: `The reconcile receiver command triggers a reconciliation of a Receiver resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing receiver Example: ` # Trigger a reconciliation for an existing receiver
flux reconcile receiver main flux reconcile receiver main`,
`,
RunE: reconcileReceiverCmdRun, RunE: reconcileReceiverCmdRun,
} }

View File

@@ -35,8 +35,7 @@ var reconcileSourceBucketCmd = &cobra.Command{
Short: "Reconcile a Bucket source", Short: "Reconcile a Bucket source",
Long: `The reconcile source command triggers a reconciliation of a Bucket resource and waits for it to finish.`, Long: `The reconcile source command triggers a reconciliation of a Bucket resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing source Example: ` # Trigger a reconciliation for an existing source
flux reconcile source bucket podinfo flux reconcile source bucket podinfo`,
`,
RunE: reconcileCommand{ RunE: reconcileCommand{
apiType: bucketType, apiType: bucketType,
object: bucketAdapter{&sourcev1.Bucket{}}, object: bucketAdapter{&sourcev1.Bucket{}},

View File

@@ -27,8 +27,7 @@ var reconcileSourceGitCmd = &cobra.Command{
Short: "Reconcile a GitRepository source", Short: "Reconcile a GitRepository source",
Long: `The reconcile source command triggers a reconciliation of a GitRepository resource and waits for it to finish.`, Long: `The reconcile source command triggers a reconciliation of a GitRepository resource and waits for it to finish.`,
Example: ` # Trigger a git pull for an existing source Example: ` # Trigger a git pull for an existing source
flux reconcile source git podinfo flux reconcile source git podinfo`,
`,
RunE: reconcileCommand{ RunE: reconcileCommand{
apiType: gitRepositoryType, apiType: gitRepositoryType,
object: gitRepositoryAdapter{&sourcev1.GitRepository{}}, object: gitRepositoryAdapter{&sourcev1.GitRepository{}},

View File

@@ -27,8 +27,7 @@ var reconcileSourceHelmCmd = &cobra.Command{
Short: "Reconcile a HelmRepository source", Short: "Reconcile a HelmRepository source",
Long: `The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish.`, Long: `The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing source Example: ` # Trigger a reconciliation for an existing source
flux reconcile source helm podinfo flux reconcile source helm podinfo`,
`,
RunE: reconcileCommand{ RunE: reconcileCommand{
apiType: helmRepositoryType, apiType: helmRepositoryType,
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}}, object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},

View File

@@ -39,8 +39,7 @@ var resumeAlertCmd = &cobra.Command{
Long: `The resume command marks a previously suspended Alert resource for reconciliation and waits for it to Long: `The resume command marks a previously suspended Alert resource for reconciliation and waits for it to
finish the apply.`, finish the apply.`,
Example: ` # Resume reconciliation for an existing Alert Example: ` # Resume reconciliation for an existing Alert
flux resume alert main flux resume alert main`,
`,
RunE: resumeAlertCmdRun, RunE: resumeAlertCmdRun,
} }

View File

@@ -29,8 +29,7 @@ var resumeHrCmd = &cobra.Command{
Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to
finish the apply.`, finish the apply.`,
Example: ` # Resume reconciliation for an existing Helm release Example: ` # Resume reconciliation for an existing Helm release
flux resume hr podinfo flux resume hr podinfo`,
`,
RunE: resumeCommand{ RunE: resumeCommand{
apiType: helmReleaseType, apiType: helmReleaseType,
object: helmReleaseAdapter{&helmv2.HelmRelease{}}, object: helmReleaseAdapter{&helmv2.HelmRelease{}},

View File

@@ -27,8 +27,7 @@ var resumeImageRepositoryCmd = &cobra.Command{
Short: "Resume a suspended ImageRepository", Short: "Resume a suspended ImageRepository",
Long: `The resume command marks a previously suspended ImageRepository resource for reconciliation and waits for it to finish.`, Long: `The resume command marks a previously suspended ImageRepository resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing ImageRepository Example: ` # Resume reconciliation for an existing ImageRepository
flux resume image repository alpine flux resume image repository alpine`,
`,
RunE: resumeCommand{ RunE: resumeCommand{
apiType: imageRepositoryType, apiType: imageRepositoryType,
object: imageRepositoryAdapter{&imagev1.ImageRepository{}}, object: imageRepositoryAdapter{&imagev1.ImageRepository{}},

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