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

Compare commits

...

307 Commits

Author SHA1 Message Date
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
Hidde Beydals
aab3452773 Merge pull request #949 from fluxcd/k8s-ver-check
Include prerelease data in k8s version constraints
2021-02-19 12:01:14 +01:00
Hidde Beydals
5903dfc627 Include prerelease data in k8s version constraints
This ensures the advertised version from e.g. GKE or EKS (for example
`v1.17.15-gke.800`) do not trigger a false warning.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-19 11:39:16 +01:00
Stefan Prodan
a5272b2b39 Merge pull request #945 from fluxcd/docs-ga-roadmap
Add production ready and GA sections to roadmap
2021-02-18 19:57:13 +02:00
Stefan Prodan
771fc20bb2 Add production ready and GA sections to roadmap
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-18 19:03:49 +02:00
Hidde Beydals
b5f48bee78 Merge pull request #944 from fluxcd/build/codeql-on-push 2021-02-18 15:55:49 +01:00
Hidde Beydals
ff78af5808 Run bootstrap for trusted PRs
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-18 15:41:37 +01:00
Hidde Beydals
7eab649abc Bundle CodeQL, FOSSA, Snyk as jobs in workflow
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-18 15:41:37 +01:00
Hidde Beydals
1b5db157b1 Align formatting and extensions workflows
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-18 15:41:32 +01:00
Hidde Beydals
0fa9aebf7a Split analysis workflows into CodeQL, FOSSA, Snyk
This reverts #934, but takes into account the mistakes that were fixed
there.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-18 14:36:02 +01:00
Hidde Beydals
9fb9965ffe Merge pull request #943 from fluxcd/disable-darwin-arm64
Exclude ARM archs from Darwin release builds
2021-02-18 12:17:50 +01:00
Hidde Beydals
02c9f29499 Exclude ARM archs from Darwin release builds
As we are not on Go 1.16 yet.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-18 12:04:32 +01:00
Hidde Beydals
c6243ad002 Merge pull request #942 from fluxcd/update-components
Update toolkit components
2021-02-18 11:36:09 +01:00
fluxcdbot
f4650c7a84 Update toolkit components
- kustomize-controller to v0.8.1
  https://github.com/fluxcd/kustomize-controller/blob/v0.8.1/CHANGELOG.md
- source-controller to v0.8.1
  https://github.com/fluxcd/source-controller/blob/v0.8.1/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-02-18 10:22:26 +00:00
Stefan Prodan
6df691598e Merge pull request #934 from fluxcd/merge-scan-actions
Merge scanning actions
2021-02-17 14:43:35 +02:00
Stefan Prodan
26964b7f88 Merge scanning actions
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-17 11:12:55 +02:00
Stefan Prodan
71712ae1c3 Merge pull request #935 from Niksko/patch-1
Fix typo in gotk_reconcile_condition status
2021-02-17 09:03:53 +02:00
Nik Skoufis
0b6c979e47 Fix typo in gotk_reconcile_condition status
Signed-off-by: Nikolas Skoufis <nskoufis@seek.com.au>
2021-02-17 15:30:24 +11:00
Stefan Prodan
cc2cbc2519 Merge pull request #929 from fluxcd/flux-action
Refactor Flux GitHub Action
2021-02-17 00:33:56 +02:00
Stefan Prodan
8043ae961a Refactor Flux GitHub Action
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-17 00:07:39 +02:00
Hidde Beydals
b1d1d30cdb Merge pull request #932 from idvoretskyi/idvoretskyi-snyk
Snyk GitHub Action added
2021-02-16 22:50:46 +01:00
Ihor Dvoretskyi
28d606cb0e Snyk GitHub Action added
Signed-off-by: Ihor Dvoretskyi <ihor@linux.com>
2021-02-16 23:38:04 +02:00
Hidde Beydals
e9c5b6e917 Merge pull request #933 from adrian/updates-to-sealed-secrets-docs
A few minor updates to sealed secrets docs
2021-02-16 22:20:11 +01:00
Adrian Smith
68f96235fc A few minor updates to sealed secrets docs
* Update to sealed secrets 1.13 helm chart. 1.10 is no longer available.
* Use `--dry-run=client` with kubectl. `--dry-run` on its own is
deprecated.

Signed-off-by: Adrian Smith <adrian@17od.com>
2021-02-16 21:06:50 +00:00
Hidde Beydals
12f22ddeba Merge pull request #931 from fluxcd/add-codeql
Add CodeQL Security Scanning
2021-02-16 19:06:19 +01:00
Chris Aniszczyk
9db5c644a6 Create codeql-analysis.yml
Add CodeSQL Security Scanning to start.

Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
2021-02-16 11:46:25 -06:00
Hidde Beydals
ef6a1fb09b Merge pull request #923 from arbourd/update-runtime
Use `MergeMaps` from pkg/runtime v0.8.2
2021-02-16 00:43:23 +01:00
Dylan Arbour
99002f92f4 Update pkg/runtime to v0.8.2
pkg/runtime v0.8.2 introduces runtime/transform which includes
`MergeMaps`:
https://github.com/fluxcd/pkg/pull/85

Signed-off-by: Dylan Arbour <arbourd@users.noreply.github.com>
2021-02-15 18:06:54 -05:00
Hidde Beydals
298d6a1a15 Merge pull request #922 from fluxcd/check-improvements 2021-02-15 22:14:00 +01:00
Hidde Beydals
5a21f50230 Remove unused util functions
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-15 21:57:11 +01:00
Hidde Beydals
5263dabd22 Check if targeted version is supported by binary
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-15 21:57:11 +01:00
Hidde Beydals
9b649f6c72 Check if targeted bootstrap/install version exists
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-15 21:57:06 +01:00
Hidde Beydals
b903cd5b68 Check for new Flux binary version
This prints a warning if the user has internet access and is running
an older version of the binary.

It also replaces the `blang/semver` package with `pkg/version` and
`Masterminds/semver` to align with controller dependencies.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-15 21:48:27 +01:00
Hidde Beydals
890b5c5202 Use label selector to check components
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-15 16:39:29 +01:00
Hidde Beydals
a14e88d04d Merge pull request #915 from fluxcd/docs-helm-features 2021-02-13 19:08:11 +01:00
Hidde Beydals
58e09b4c68 docs: list Helm post renderer feature
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-13 15:56:43 +01:00
Stefan Prodan
631201d541 Merge pull request #914 from fluxcd/img-update-roadmap
Update image update feature parity roadmap
2021-02-12 18:35:48 +02:00
Stefan Prodan
0fbeb6d2cd Update semver flag in image update guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-12 18:23:25 +02:00
Stefan Prodan
11f8e2ffde Update image update feature parity roadmap
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-12 18:21:29 +02:00
Hidde Beydals
055eb4a61a Merge pull request #806 from fluxcd/image-auto-migration-howto 2021-02-12 17:20:35 +01:00
Hidde Beydals
30c1c5c3d3 Link to image automation guides in menu
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 17:01:43 +01:00
Hidde Beydals
e034ec3207 Add missing link to image update automation ref
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 17:01:08 +01:00
Hidde Beydals
8edc4bd24b Add missing link to SemVer spec
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 16:49:33 +01:00
Hidde Beydals
6e1672f73c Change policy example to numerical in asc order
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 16:47:16 +01:00
Michael Bridgen
5e1f6f7317 Fix up internal links
Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Michael Bridgen
386780ba12 Make hrefs absolute and spelt correctly
Stray characters here and there threw off the markdown engine.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Michael Bridgen
e785971ba8 Rewrite to account for numerical sorting
Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Michael Bridgen
daaae07649 Persuade markdown relative paths are links
Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Michael Bridgen
6cd567dc66 Remove draft TODO comments
I have moved TODO comments (that still apply) to the PR description.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Michael Bridgen
a541a7ee85 Remove suggestions of using commit number
Using a commit number is trickier than it sounds. It would need to be
padded to sort correctly, for one thing. It is better to leave it out
than to give an incomplete account.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Michael Bridgen
43572bba04 Rearrange so that observing an auto commit is last
Previously, creating an automation object was the last instruction. It
is easier to describe what to expect at each step when the last step
is to add an update marker in the file to be updated, since the next
thing that should happen is that the automation makes an update as a
consequence.

This commit shifts the sections around so that setting up the
GitRepository and ImageUpdateAutomation are done earlier, and
migrating each file are done after that, and completes the steps
described including checking the expected status at each stage.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Michael Bridgen
2a3a4456c1 Separate image tags howto from migration howto
The Flux v1 migration how-to flows better if the section on how to set
builds up to tag images in the right way is its own document. It's a
lot to skim past when you don't need it, and (since it's a different
layer of yak hair) something you might want to figure out first if you
do need it.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Michael Bridgen
15f8e6369b Add image automation migration how-to
This doc describes how to move from using Flux v1 to update image refs
in git, to using Flux v2. There is some overlap with the tutorial on
how to use Flux v2 automation. This how-to spends more time on how to
convert existing configuration to be used with Flux v2.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 16:46:30 +01:00
Hidde Beydals
cfad9a19eb Merge pull request #911 from fluxcd/select-numeric-validation
Validate if only one image policy selector is given
2021-02-12 16:14:21 +01:00
Hidde Beydals
e4c3136433 Merge pull request #910 from fluxcd/git-ca-file
Add caFile to create source/secret git commands
2021-02-12 16:01:27 +01:00
Hidde Beydals
73b8a26850 Validate if only 1 image policy selector is given
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 16:01:25 +01:00
Stefan Prodan
aa533b28fb Add caFile to create source/secret git commands
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-12 16:47:00 +02:00
Hidde Beydals
9d70e09a57 Add numeric selector to create image policy cmd
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 15:38:01 +01:00
Stefan Prodan
17e18985e6 Merge pull request #908 from fluxcd/update-kustomize-api
Update sigs.k8s.io/kustomize/api to v0.7.4
2021-02-12 15:49:39 +02:00
Hidde Beydals
7c39aaf463 Update sigs.k8s.io/kustomize/api to v0.7.4
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 14:32:44 +01:00
Hidde Beydals
bae5c125e8 Merge pull request #907 from fluxcd/update-components
Update toolkit components
2021-02-12 14:29:23 +01:00
fluxcdbot
1c84fa0d97 Update toolkit components
- helm-controller to v0.7.0
  https://github.com/fluxcd/helm-controller/blob/v0.7.0/CHANGELOG.md
- kustomize-controller to v0.8.0
  https://github.com/fluxcd/kustomize-controller/blob/v0.8.0/CHANGELOG.md
- source-controller to v0.8.0
  https://github.com/fluxcd/source-controller/blob/v0.8.0/CHANGELOG.md
- notification-controller to v0.8.0
  https://github.com/fluxcd/notification-controller/blob/v0.8.0/CHANGELOG.md
- image-reflector-controller to v0.6.0
  https://github.com/fluxcd/image-reflector-controller/blob/v0.6.0/CHANGELOG.md
- image-automation-controller to v0.5.0
  https://github.com/fluxcd/image-automation-controller/blob/v0.5.0/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-02-12 13:16:13 +00:00
Hidde Beydals
6f583f9f0e Merge pull request #878 from fluxcd/pprof-guide 2021-02-12 14:02:36 +01:00
Hidde Beydals
217574b75c Add debugging to dev guides menu
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 13:44:35 +01:00
Hidde Beydals
1378530aeb Add section about resource usage
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 13:44:35 +01:00
Hidde Beydals
0b10ed4d88 Add guide for pprof endpoints
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-12 13:44:35 +01:00
Stefan Prodan
a2887f5776 Merge pull request #891 from fluxcd/refac-uninstall
Refactor flux uninstall command
2021-02-12 14:44:23 +02:00
Stefan Prodan
0f1d27f1e6 Remove network policies on uninstall
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-12 14:30:57 +02:00
Stefan Prodan
850ab0942b Implement uninstall dry run
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-12 14:30:50 +02:00
Stefan Prodan
f5ae8f44b4 Refactor flux uninstall command
- deletes Flux components (deployments and services)
- deletes Flux RBAC (service accounts, cluster roles and cluster role bindings)
- removes the Kubernetes finalizers from Flux custom resources
- deletes Flux custom resource definitions and custom resources
- deletes the namespace where Flux was installed
- preserves the Kubernetes objects and Helm releases that were reconciled on the cluster by Flux

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-12 14:30:50 +02:00
Michael Bridgen
7f98cfd506 Merge pull request #906 from fluxcd/personal-flag
Give more explanation for --personal flag
2021-02-12 12:02:22 +00:00
Michael Bridgen
bc45a79b92 Give more explanation for --personal flag
Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-12 11:41:27 +00:00
Stefan Prodan
5003cf674d Merge pull request #904 from fluxcd/add-version-to-commits
Add flux version to bootstrap commit messages
2021-02-12 11:38:35 +02:00
Stefan Prodan
bc9cbc387c Add flux version to bootstrap commit messages
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-12 10:47:00 +02:00
Stefan Prodan
60a1e78869 Merge pull request #899 from fluxcd/toleration-keys
Allow Flux to be deployed on tainted Kubernetes nodes
2021-02-11 15:46:45 +02:00
Stefan Prodan
37f5587085 Allow Flux to be deployed on tainted Kubernetes nodes
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-11 15:20:19 +02:00
Hidde Beydals
fa6e3d3706 Merge pull request #898 from fluxcd/docs-fix-list 2021-02-11 13:20:19 +01:00
Hidde Beydals
bb8bc875b4 docs: improve Kustomize behavior FAQ
- Fix the formatting of the list.
- Add a hint block for validating changes locally and/or in CI.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-11 12:54:03 +01:00
Stefan Prodan
b3dca737be Merge pull request #897 from fluxcd/fix-timeout
Map timeout arg to bootstrap status check
2021-02-11 13:30:05 +02:00
Stefan Prodan
9094f85487 Add image automation to readme
Sync community section readme/docs index

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-11 13:11:03 +02:00
Stefan Prodan
1256bbfbaf Fix bootstrap status check timeout
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-11 13:09:16 +02:00
Daniel Holbach
24fe74f2f6 Merge pull request #893 from dholbach/link-to-community-page
Link to community page from docs home page
2021-02-10 08:40:15 +01:00
Daniel Holbach
908f501e03 link to community page from toolkit.f.i
Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-02-09 14:52:17 +01:00
Stefan Prodan
35507c7854 Merge pull request #860 from jonathan-innis/joinnis/image-policy
Adding --select-alpha and --extract to create image policy
2021-02-09 15:05:57 +02:00
Jonathan Innis
eb7102ecac Adding extract pattern validation
Signed-off-by: Jonathan Innis <jonathan.innis.ji@gmail.com>
2021-02-08 16:51:45 -08:00
Jonathan Innis
ade6bfcbca Update e2e testing with new cli args
Signed-off-by: Jonathan Innis <jonathan.innis.ji@gmail.com>
2021-02-08 16:49:07 -08:00
Jonathan Innis
fa98403aa8 Add newly generated create image doc
Signed-off-by: Jonathan Innis <jonathan.innis.ji@gmail.com>
2021-02-08 16:49:07 -08:00
jonathan-innis
3f0cb1637c Add select-alpha and extract to create policy
Signed-off-by: jonathan-innis <jonathan.innis.ji@gmail.com>
2021-02-08 16:49:07 -08:00
Stefan Prodan
42011d028e Merge pull request #879 from fluxcd/azure-devops-pat
Add Azure DevOps PAT auth to install docs
2021-02-08 19:01:49 +02:00
Stefan Prodan
307bb0dea1 Add Azure DevOps PAT auth to install docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-08 18:44:22 +02:00
Stefan Prodan
ec2a8347d4 Merge pull request #877 from stealthybox/integrations-registry-credentials-sync
Add ACR auth to Image Updates examples
2021-02-08 18:43:22 +02:00
leigh capili
e99b1c3ed8 Document ACR / AKS Image Update Considerations
Signed-off-by: leigh capili <leigh@null.net>
2021-02-08 09:15:42 -07:00
leigh capili
99825f2663 Add registry cred Deployments/CronJobs for aws/gcp/azure via kustomize
Signed-off-by: leigh capili <leigh@null.net>
2021-02-08 09:15:42 -07:00
Stefan Prodan
afffdfbc5c Merge pull request #880 from chanwit/add_kustomize_fag
Add FAQ to explain the current Kustomize behavior
2021-02-08 17:54:28 +02:00
Chanwit Kaewkasi
cd874acfd5 add FAQ to explain the current Kustomize behavior
Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
2021-02-08 21:24:31 +07:00
Stefan Prodan
34edbf469e Merge pull request #871 from fluxcd/incident-mgmt
Add incident management section to image automation docs
2021-02-06 12:47:10 +02:00
Stefan Prodan
d9ed30e436 Add incident management section to image automation docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-06 10:51:37 +02:00
Stefan Prodan
30008de400 Merge pull request #867 from fluxcd/get-resource-by-name
Add support for getting resources by name
2021-02-05 17:24:50 +02:00
Stefan Prodan
a5fa731545 Add support for getting resources by name
- add singular alias to get commands
- allow filtering the get commands result by resource name
- add the image commands to mkdocs index

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-05 16:35:23 +02:00
Stefan Prodan
493ee3c956 Merge pull request #866 from fluxcd/hr-values
Add support for multiple values files to create hr
2021-02-05 16:09:29 +02:00
Stefan Prodan
3dd574ee51 Add support for multiple values files to create hr
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-05 15:43:03 +02:00
Hidde Beydals
5416c19b2e Merge pull request #863 from fluxcd/update-git-pkg 2021-02-05 14:42:32 +01:00
Hidde Beydals
2f31d80c7a Update git from fluxcd/pkg
This incorporates the changes made to the GitLab provider.

This means that we no longer rely on UI names, but rather use the unique
path identifier (the elements you see in your address bar when looking
at e.g. a group in your GitLab environment).

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-05 13:38:49 +01:00
Stefan Prodan
27d1833854 Merge pull request #848 from ViBiOh/patch-1
Exclude deleted resources on prometheus alerting query
2021-02-05 09:28:49 +02:00
Vincent Boutour
84ed716908 Exclude deleted resources on prometheus alerting query
Signed-off-by: Vincent Boutour <bob@vibioh.fr>
2021-02-04 18:10:42 +01:00
Michael Bridgen
6c9c9c7578 Merge pull request #790 from fluxcd/certs-for-imagerepo
Give image repository a cert-secret-ref flag
2021-02-04 13:13:43 +00:00
Michael Bridgen
cc7b7b0689 Give examples of create image repository
Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-04 12:55:42 +00:00
Michael Bridgen
5df8e05d1a Give image repository a cert-secret-ref flag
ImageRepository objects can now refer to a secret containing
certificates to use for TLS. This adds the flag

    flux create image repository --cert-secret-ref

for naming a secret to use. You can create such a secret with

    flux create secret tls

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-04 12:55:42 +00:00
Michael Bridgen
b3b224b0ca Merge pull request #862 from fluxcd/correct-image-delete
Rename flux delete auto to flux delete image
2021-02-04 12:54:05 +00:00
Michael Bridgen
75ab28ee5d Rename flux delete auto to flux delete image
This slipped through the auto->image change made in the course of
preparing #538.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-04 11:57:40 +00:00
Michael Bridgen
aa9ea2b4ab Merge pull request #843 from fluxcd/create-image-update-typo
Correct spelling of repository in error
2021-02-04 10:46:27 +00:00
Michael Bridgen
1e6be99c36 Correct spelling of repository in error
Signed-off-by: Michael Bridgen <michael@weave.works>
2021-02-04 10:16:09 +00:00
Stefan Prodan
49fb396bf8 Merge pull request #861 from fluxcd/refactor-checks
Refactor components status check
2021-02-04 11:56:22 +02:00
Stefan Prodan
e055c9ddc1 Refactor components status check
- run install/bootstrap checks in parallel (1m timeout)
- list not found components

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-02-04 11:16:27 +02:00
Stefan Prodan
c708e390a7 Merge pull request #845 from jonathan-innis/jonathan-innis/kstatus
Replace kubectl rollout with kstatus checks
2021-02-04 09:11:41 +02:00
jonathan-innis
d5ad26c934 Change failed message for bootstrap
Signed-off-by: jonathan-innis <jonathan.innis.ji@gmail.com>
2021-02-03 12:08:10 -08:00
jonathan-innis
144b7cd922 Update errors returned to user
Signed-off-by: jonathan-innis <jonathan.innis.ji@gmail.com>
2021-02-03 12:07:29 -08:00
jonathan-innis
9e86fbb311 Tidy up the mod imports
Signed-off-by: jonathan-innis <jonathan.innis.ji@gmail.com>
2021-02-03 12:07:29 -08:00
jonathan-innis
b528428d02 Add kstatus to install and check commands
Signed-off-by: jonathan-innis <jonathan.innis.ji@gmail.com>
2021-02-03 12:07:29 -08:00
jonathan-innis
b3d7730e79 Use status polling in bootstrap command
Signed-off-by: jonathan-innis <jonathan.innis.ji@gmail.com>
2021-02-03 12:07:19 -08:00
Hidde Beydals
f2ba567ca4 Merge pull request #857 from fluxcd/update-components
Update toolkit components
2021-02-03 19:34:43 +01:00
fluxcdbot
8342f77087 Update toolkit components
- source-controller to v0.7.4
  https://github.com/fluxcd/source-controller/blob/v0.7.4/CHANGELOG.md

Signed-off-by: GitHub <noreply@github.com>
2021-02-03 14:51:22 +00:00
Hidde Beydals
7cade1b98f Merge pull request #858 from fluxcd/component-update-cfg
Put CHANGELOG URL on new line in commit / PR body
2021-02-03 15:50:47 +01:00
Hidde Beydals
ee4c1fb36c Put CHANGELOG URL on new line in commit / PR body
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-03 15:22:57 +01:00
Hidde Beydals
dbc4e537fe Merge pull request #854 from fluxcd/move-migration-menu 2021-02-03 13:10:59 +01:00
Hidde Beydals
e28990b96c Move migration sub-menu to top-menu
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-03 11:49:51 +01:00
Hidde Beydals
408cf92c04 Merge pull request #853 from fluxcd/component-update-cfg
Tune component update configuration
2021-02-03 10:22:58 +01:00
Hidde Beydals
425af2e0dc Tune component update configuration
- Include link to changelog of component in commit and PR message
- Label pull request automatically with `area/build`
- Enable sign-off of commits to free us from manual labour

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-02 18:48:49 +01:00
Stefan Prodan
22df860eca Merge pull request #849 from fluxcd/update-components
Update toolkit components
2021-02-02 18:07:29 +02:00
fluxcdbot
f395044d65 Update toolkit components 2021-02-02 15:40:37 +00:00
Hidde Beydals
afe0ddcd84 Merge pull request #824 from fluxcd/upgrade-semver-tip
Highlight PATCH versions can be used to upgrade
2021-02-01 18:41:34 +01:00
Hidde Beydals
2c0323684c Highlight PATCH versions can be used to upgrade
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-02-01 18:21:54 +01:00
Hidde Beydals
6d5ffdea57 Merge pull request #841 from fluxcd/update-components
Update toolkit components
2021-02-01 18:07:15 +01:00
fluxcdbot
648af6e645 Update toolkit components 2021-02-01 16:50:07 +00:00
Hidde Beydals
e1895a4e21 Merge pull request #840 from relu/fix-image-update-docs-ecr-cronjob
Improve image update CronJob examples
2021-02-01 17:49:31 +01:00
Aurel Canciu
d5f45800ae Clarify how to use the generated cronjob secret
Signed-off-by: Aurel Canciu <aurelcanciu@gmail.com>
2021-02-01 17:11:14 +01:00
Aurel Canciu
51f9d249ff Fix image update guide ECR cronjob manifest
Signed-off-by: Aurel Canciu <aurelcanciu@gmail.com>
2021-02-01 17:11:14 +01:00
Hidde Beydals
6f525356cb Merge pull request #837 from aholbreich/patch-1 2021-02-01 17:10:54 +01:00
Alexander Holbreich
5008f9064e Update image-update.md
Increase attention to missing flux components.

Signed-off-by: Alexander Holbreich <alexander@holbreich.org>
2021-02-01 16:53:19 +01:00
Stefan Prodan
cff96ed7ca Merge pull request #834 from fluxcd/fix-secret-cmd
Fix create secret commands
2021-01-30 16:36:44 +02:00
Stefan Prodan
4e8a600f34 Add e2e tests for create secret commands
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-30 15:19:45 +02:00
Stefan Prodan
4fd5684277 Fix create secret commands
Regression bug introduced in https://github.com/fluxcd/flux2/pull/788

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-30 15:07:48 +02:00
Hidde Beydals
06bf469ba7 Merge pull request #825 from SomtochiAma/refactor-reconcile-command
Refactor resume command
2021-01-29 19:52:07 +01:00
Somtochi Onyekwere
b8a215230c refactor resume command
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-01-29 19:05:38 +01:00
Stefan Prodan
2460cfcf1c Merge pull request #821 from fluxcd/docs-helm-faq
Add HelmChart not ready to FAQ
2021-01-29 17:05:39 +02:00
Stefan Prodan
364242c857 Add HelmChart not ready to FAQ
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-29 16:20:35 +02:00
Stefan Prodan
29e2900f59 Merge pull request #818 from fluxcd/docs-image-webhook
Add webhook section to image update docs
2021-01-29 13:33:17 +02:00
Stefan Prodan
61e1fb770e Add webhook section to image update docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-29 13:09:01 +02:00
Stefan Prodan
2d3fcbdea3 Merge pull request #815 from fluxcd/e2e-kube-1.20.2
Update e2e tests to Kubernetes v1.20.2
2021-01-29 09:49:42 +02:00
Stefan Prodan
47e15cee3d Update e2e tests to Kubernetes v1.20.2
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-28 19:50:19 +02:00
Stefan Prodan
adeb3e3f42 Merge pull request #814 from mewzherder/patch-9
Community section clarity of purpose + support page link
2021-01-28 19:42:42 +02:00
mewzherder
fb1278285b Community section clarity of purpose + support page link
Signed-off-by: mewzherder <tamao@weave.works>
2021-01-28 09:24:08 -08:00
Stefan Prodan
e371610849 Merge pull request #812 from chanwit/network_policy_e2e
Enable network policy in e2e
2021-01-28 16:46:45 +02:00
Chanwit Kaewkasi
424de63bd1 update KIND to v0.10.0 and node to 1.16.15
Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
2021-01-28 21:21:02 +07:00
Chanwit Kaewkasi
832c925d39 setup Calico to enable network policy for e2e testing
Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
2021-01-28 21:21:02 +07:00
Chanwit Kaewkasi
378f118d51 add kind config to disable kind-net
Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
2021-01-28 21:21:02 +07:00
Hidde Beydals
d651777122 Merge pull request #813 from fluxcd/iua-docs-example-update
Update docs ImageUpdateAutomation example
2021-01-28 15:13:40 +01:00
Aurel Canciu
65d8ebabb8 Update docs ImageUpdateAutomation example
Signed-off-by: Aurel Canciu <aurelcanciu@gmail.com>
2021-01-28 15:57:25 +02:00
Hidde Beydals
9195ed9a1b Merge pull request #809 from SomtochiAma/refactor-reconcile-command
Refactor reconcile commands
2021-01-28 14:16:15 +01:00
Somtochi Onyekwere
5df8f7313c Refactor reconcile commands
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-01-28 13:21:29 +01:00
Hidde Beydals
25ed6ca0a4 Merge pull request #780 from dholbach/link-to-support-page 2021-01-28 11:59:08 +01:00
Daniel Holbach
9f972995bd add very basic issue template
Also link to support page.

	Fixes: fluxcd/website#77

Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-01-27 17:34:26 +01:00
Hidde Beydals
29c46a9892 Merge pull request #791 from SomtochiAma/refactor-reconcile-commands
Refactor suspend commands
2021-01-27 10:05:32 +01:00
Somtochi Onyekwere
ef579fe596 Refactor suspend commands
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-01-27 09:57:37 +01:00
Hidde Beydals
5b268f62a3 Merge pull request #789 from SomtochiAma/refactor-delete-command 2021-01-27 09:57:17 +01:00
Somtochi Onyekwere
1f1c8286a5 Refactor delete command for kustomizations, sources and helmreleases
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-01-27 09:43:39 +01:00
Hidde Beydals
5401e1ace4 Merge pull request #794 from fluxcd/get-type-fixes
Use correct type in various get source commands
2021-01-27 09:37:25 +01:00
Hidde Beydals
69294ef56d Use correct type in various get source commands
This fixes a bug where the wrong type was displayed for various
`get source` commands.

```console
$ flux get sources helm --namespace default
✗ no Bucket objects found in default namespace
```

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-01-27 09:28:48 +01:00
Hidde Beydals
a685ed8029 Merge pull request #793 from fluxcd/reconcile-w-source-other-ns
Set source namespace when reconciling with source
2021-01-27 09:21:12 +01:00
Hidde Beydals
68d0be3818 Set source namespace when reconciling with source
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-01-26 22:21:40 +01:00
Michael Bridgen
84e2cb4c1f Merge pull request #788 from fluxcd/create-secret-tls
Create secret for TLS command
2021-01-26 17:33:50 +00:00
Michael Bridgen
263c664acd Factor out more common secrets command code
Making the secret without data is always the same, so factor that out.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-01-26 17:25:33 +00:00
Michael Bridgen
b12c4c22fb Add command for creating TLS secrets
The image-reflector controller now accepts a secret containing a
client certificate and key, and/or a CA certificate; so it's useful to
have a command for creating them.

`flux create secret helm` is close, but accepts username/password
(which would be ignored), and has the wrong name of course. Happily
though, much can be shared between the implementations.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-01-26 17:25:33 +00:00
Stefan Prodan
9f39fadb9e Merge pull request #787 from fluxcd/fix-rbac-namespace
RBAC Fix: Replace SA namespace in ClusterRoleBindings
2021-01-26 19:21:19 +02:00
Stefan Prodan
4c29a1ca27 Replace SA namespace in RBAC
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-26 18:57:36 +02:00
Stefan Prodan
f4db124d50 Merge pull request #783 from fluxcd/rbac-fix
RBAC Fix: Add SA namespace to cluster role bindings
2021-01-26 16:24:16 +02:00
Stefan Prodan
8f8c7cccc6 Add SA namespace to RBAC
Fix flux install when not all controllers have been selected

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-26 16:16:34 +02:00
Hidde Beydals
e2097c28bd Merge pull request #782 from fluxcd/docs-img-auto-links
docs: fix image automation menu links
2021-01-26 14:34:29 +01:00
Hidde Beydals
871eb444fc docs: fix image automation menu links
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-01-26 14:26:18 +01:00
Hidde Beydals
bc5fbb9fa5 Merge pull request #774 from fluxcd/doc-helm-storage-ns 2021-01-26 14:15:00 +01:00
Hidde Beydals
3e9749c6b1 Document StorageNamespace in Helm migration guide
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-01-26 14:05:54 +01:00
Hidde Beydals
eaf08fbe90 Merge pull request #781 from fluxcd/docs-image-update 2021-01-26 14:05:47 +01:00
Hidde Beydals
df411cdb88 Update alpha warning
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-01-26 13:54:28 +01:00
Hidde Beydals
a83c26a864 Provide link to CalVer, build ID, etc. policies
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-01-26 13:54:28 +01:00
Hidde Beydals
6aa853491c Add image automation to menu
Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-01-26 13:54:28 +01:00
Hidde Beydals
3af1d7a5b8 Merge pull request #771 from fluxcd/update-components 2021-01-26 13:53:47 +01:00
fluxcdbot
c7c57f6717 Update toolkit components 2021-01-26 12:32:15 +00:00
Stefan Prodan
cbb97768d1 Merge pull request #777 from fluxcd/dedicated-service-accounts
Add a dedicated service account per controller
2021-01-26 14:31:38 +02:00
Stefan Prodan
2bb40d335b Replace the default service account in docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-26 12:57:00 +02:00
Stefan Prodan
1b581d6f51 Add dedicated service accounts per controller
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-26 12:27:58 +02:00
Stefan Prodan
c219eb2883 Merge pull request #776 from SomtochiAma/refactor-get-commands
Refactor get command for kustomization and helmrelease
2021-01-26 11:55:27 +02:00
Somtochi Onyekwere
65a2c87b5a Refactor get cmd for kustomization and helmrelease
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-01-26 10:15:35 +01:00
Stefan Prodan
13aa523c37 Merge pull request #761 from SomtochiAma/cli-refactoring
Refactor get source commands
2021-01-26 10:42:49 +02:00
Somtochi Onyekwere
584f0eea58 Refactor get source commands
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-01-26 09:26:55 +01:00
Stefan Prodan
b5ebdb16b2 Merge pull request #773 from nairb774/sharp_payne
Add permissions for controller leader election
2021-01-25 23:35:19 +02:00
Brian Atkinson
a18f84c27b Add permissions for controller leader election.
The v0.8.0 version of the controller-runtime uses both config maps and
leases to perform leader election. These permissions seem to be in the
individual controller repos, but not here. For example
2d38de8779/config/rbac/leader_election_role.yaml (L33-L44)

Signed-off-by: Brian Atkinson <brian@atkinson.mn>
2021-01-25 10:15:57 -08:00
Stefan Prodan
eb86505375 Merge pull request #769 from fluxcd/update-release-doc
Add the image automation controllers to release doc
2021-01-25 15:31:31 +02:00
Stefan Prodan
984f1c8669 Add the image automation controllers to release doc
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-25 15:21:43 +02:00
Hidde Beydals
c7378edabc Merge pull request #765 from fluxcd/update-controllers 2021-01-22 16:35:30 +00:00
Hidde Beydals
ac6c6e3a30 Incorporate required API changes
* Use `LocalObjectReference` and `NamespacedObjectKindReference`
  from `meta` package, as required by controller API changes.
* Remove `Update` field from created `ImageUpdateAutomation`,
  as the API changed and the default is now defined in the Custom
  Resource Definition.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2021-01-22 17:21:36 +01:00
fluxcdbot
1bca31f32c Update toolkit components 2021-01-22 16:07:19 +00:00
Hidde Beydals
28d099a6b2 Merge pull request #741 from fluxcd/include-image-spec-docs 2021-01-22 16:06:33 +00:00
Michael Bridgen
6a2b53fbf5 Put image specs and refs in place
This adds the image controller specs and refs to the workflow that
prepares the docs, and links to them in the index.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-01-22 16:49:18 +01:00
Michael Bridgen
7568f301b8 Make home for image-* component docs
This has a description of the controllers, and is where the API specs
and refs will get copied to.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-01-22 16:49:18 +01:00
Stefan Prodan
6add511447 Merge pull request #755 from SomtochiAma/cli-refactoring
Refactor cmd global variables into structs
2021-01-22 12:27:39 +02:00
Somtochi Onyekwere
a7586e69fd Refactor cmd global variables into structs
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-01-22 10:42:31 +01:00
Stefan Prodan
77db369213 Merge pull request #757 from fluxcd/gitlab-fix
Update fluxcd/pkg/git to v0.2.3
2021-01-21 18:12:11 +02:00
Stefan Prodan
2eb6a0513e Update fluxcd/pkg/git to v0.2.3
Fixes Gitlab bootstrap when used with a project token

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-01-21 17:51:07 +02:00
Stefan Prodan
38468c72db Merge pull request #750 from SomtochiAma/kms-docs
Update guide for mozilla sops
2021-01-20 17:04:56 +02:00
Somtochi Onyekwere
5f759200c6 update guide for mozilla sops
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
2021-01-20 15:54:26 +01:00
Daniel Holbach
81f68157fa Merge pull request #753 from dholbach/f-community-24
move talk info, part of fluxcd/community#24
2021-01-20 15:21:15 +01:00
Daniel Holbach
0f3e8abf06 move talk info, part of fluxcd/community#24
Signed-off-by: Daniel Holbach <daniel@weave.works>
2021-01-20 15:07:08 +01:00
Stefan Prodan
fcadd5312d Merge pull request #747 from mewzherder/patch-8
Clarify options on our GH Discussions page
2021-01-20 10:59:22 +02:00
mewzherder
6e32820910 Clarify options on our GH Discussions page
Signed-off-by: mewzherder <tamao@weave.works>
2021-01-19 22:27:50 -08:00
257 changed files with 7964 additions and 3935 deletions

46
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

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

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Ask a question
url: https://github.com/fluxcd/flux2/discussions
about: Please ask and answer questions here.

View File

@@ -8,18 +8,20 @@ pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
url="https://fluxcd.io/"
arch=("x86_64" "armv6h" "armv7h" "aarch64")
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=(
"$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=(
"$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=(
"$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=(
"$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=(
${SHA256SUM_AMD64}
@@ -33,7 +35,12 @@ sha256sums_armv7h=(
sha256sums_aarch64=(
${SHA256SUM_ARM64}
)
_srcname=flux
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")
replaces=("flux-cli")
depends=("glibc")
makedepends=("go")
optdepends=("kubectl")
makedepends=('go>=1.16', 'kustomize>=3.0')
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=(
"$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=(
${SHA256SUM}
)
_srcname=flux
build() {
cd "flux2-$pkgver"
cd "flux2-${pkgver}"
export CGO_LDFLAGS="$LDFLAGS"
export CGO_CFLAGS="$CFLAGS"
export CGO_CXXFLAGS="$CXXFLAGS"
export CGO_CPPFLAGS="$CPPFLAGS"
export GOFLAGS="-buildmode=pie -trimpath -ldflags=-linkmode=external -mod=readonly -modcacherw"
go build -ldflags "-X main.VERSION=$pkgver" -o flux-bin ./cmd/flux
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
./manifests/scripts/bundle.sh "${PWD}/manifests" "${PWD}/cmd/flux/manifests"
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
}
check() {
cd "flux2-$pkgver"
cd "flux2-${pkgver}"
make test
}
package() {
cd "flux2-$pkgver"
install -Dm755 flux-bin "$pkgdir/usr/bin/flux"
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
cd "flux2-${pkgver}"
install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
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")
conflicts=("flux-bin")
depends=("glibc")
makedepends=("go")
optdepends=("kubectl")
makedepends=('go>=1.16', 'kustomize>=3.0')
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=(
"git+https://github.com/fluxcd/flux2.git"
)
md5sums=('SKIP')
_srcname=flux
pkgver() {
cd "flux2"
@@ -29,8 +32,9 @@ build() {
export CGO_CFLAGS="$CFLAGS"
export CGO_CXXFLAGS="$CXXFLAGS"
export CGO_CPPFLAGS="$CPPFLAGS"
export GOFLAGS="-buildmode=pie -trimpath -ldflags=-linkmode=external -mod=readonly -modcacherw"
go build -ldflags "-X main.VERSION=$pkgver" -o flux-bin ./cmd/flux
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
make cmd/flux/manifests
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
}
check() {
@@ -40,6 +44,10 @@ check() {
package() {
cd "flux2"
install -Dm755 flux-bin "$pkgdir/usr/bin/flux"
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
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}"
}

5
.github/kind/config.yaml vendored Normal file
View File

@@ -0,0 +1,5 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
disableDefaultCNI: true # disable kindnet
podSubnet: 192.168.0.0/16 # set to Calico's default subnet

View File

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

View File

@@ -1,9 +1,8 @@
name: Publish docs via GitHub Pages
on:
push:
branches:
- docs*
- main
branches: [ 'docs*', main ]
jobs:
build:
@@ -17,7 +16,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
controller_version() {
sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml
sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml
}
{
@@ -54,6 +53,18 @@ jobs:
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/receiver.md" > docs/components/notification/receiver.md
}
{
# image-*-controller CRDs; these use the same API group
IMG_REFL_VER=$(controller_version image-reflector-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/api/image-reflector.md" > docs/components/image/reflector-api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/spec/v1alpha1/imagerepositories.md" > docs/components/image/imagerepositories.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/spec/v1alpha1/imagepolicies.md" > docs/components/image/imagepolicies.md
IMG_AUTO_VER=$(controller_version image-automation-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-automation-controller/$IMG_AUTO_VER/docs/api/image-automation.md" > docs/components/image/automation-api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-automation-controller/$IMG_AUTO_VER/docs/spec/v1alpha1/imageupdateautomations.md" > docs/components/image/imageupdateautomations.md
}
{
# install script
cp install/flux.sh docs/install.sh

View File

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

View File

@@ -1,25 +0,0 @@
name: FOSSA
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: "^1.14.x"
- name: Add GOPATH to GITHUB_ENV
run: echo "GOPATH=$(go env GOPATH)" >>"$GITHUB_ENV"
- name: Add GOPATH to GITHUB_PATH
run: echo "$GOPATH/bin" >>"$GITHUB_PATH"
- name: Run FOSSA scan and upload build data
uses: fossa-contrib/fossa-action@v1
with:
# FOSSA Push-Only API Token
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
github-token: ${{ github.token }}

View File

@@ -2,9 +2,9 @@ name: rebase
on:
pull_request:
types: [opened]
types: [ opened ]
issue_comment:
types: [created]
types: [ created ]
jobs:
rebase:

View File

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

60
.github/workflows/scan.yaml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: Scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '18 10 * * 3'
jobs:
fossa:
name: FOSSA
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run FOSSA scan and upload build data
uses: fossa-contrib/fossa-action@v1
with:
# FOSSA Push-Only API Token
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
github-token: ${{ github.token }}
snyk:
name: Snyk
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
steps:
- uses: actions/checkout@v2
- name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@main
- name: Build manifests
run: |
make cmd/flux/manifests
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/golang@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --sarif-file-output=snyk.sarif
- name: Upload result to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: snyk.sarif
codeql:
name: CodeQL
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: go
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -13,7 +13,10 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.16.x
- name: Update component versions
id: update
run: |
@@ -21,18 +24,19 @@ jobs:
bump_version() {
local RELEASE_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
local CURRENT_VERSION=$(sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
local CURRENT_VERSION=$(sed -n "s/.*$1\/releases\/download\/\(.*\)\/.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
if [[ "${RELEASE_VERSION}" != "${CURRENT_VERSION}" ]]; then
# bump kustomize
sed -i "s/\($1\/archive\/\)v.*\(.zip\/\/$1-\).*\(\/config.*\)/\1${RELEASE_VERSION}\2${RELEASE_VERSION/v}\3/g" "manifests/bases/$1/kustomization.yaml"
sed -i "s/\($1\/releases\/download\/\)v.*\(\/.*\)/\1${RELEASE_VERSION}\2/g" "manifests/bases/$1/kustomization.yaml"
if [[ ! -z $(go list -m all | grep "github.com/fluxcd/$1/api" | awk '{print $2}') ]]; then
if [[ ! -z $(grep "github.com/fluxcd/$1/api" go.mod | awk '{print $2}') ]]; then
# bump go mod
go mod edit -require="github.com/fluxcd/$1/api@${RELEASE_VERSION}"
fi
PR_BODY="$PR_BODY- $1 to ${RELEASE_VERSION}%0A"
# NB: special URL encoded formatting required for newlines
PR_BODY="$PR_BODY- $1 to ${RELEASE_VERSION}%0A https://github.com/fluxcd/$1/blob/${RELEASE_VERSION}/CHANGELOG.md%0A"
fi
}
@@ -51,7 +55,7 @@ jobs:
# diff change
git diff
# export PR_BODY for PR
# export PR_BODY for PR and commit
echo "::set-output name=pr_body::$PR_BODY"
}
@@ -60,19 +64,22 @@ jobs:
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.BOT_GITHUB_TOKEN }}
commit-message: Update toolkit components
commit-message: |
Update toolkit components
${{ steps.update.outputs.pr_body }}
committer: GitHub <noreply@github.com>
author: fluxcdbot <fluxcdbot@users.noreply.github.com>
signoff: true
branch: update-components
title: Update toolkit components
body: |
${{ steps.update.outputs.pr_body }}
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
branch: update-components
labels: |
area/build
reviewers: ${{ secrets.ASSIGNEES }}
- name: Check output
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"

3
.gitignore vendored
View File

@@ -14,4 +14,5 @@
# Dependency directories (remove the comment below to include it)
# vendor/
bin/
output/
output/
cmd/flux/manifests/

View File

@@ -20,6 +20,9 @@ builds:
id: darwin
goos:
- darwin
goarch:
- amd64
- arm64
- <<: *build_defaults
id: windows
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:
- [/f/flux2](https://github.com/fluxcd/flux2): The Flux CLI
- [/f/source-manager](https://github.com/fluxcd/source-controller): Kubernetes operator for managing sources
- [/f/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
- [/f/notification-controller](https://github.com/fluxcd/notification-controller): Kubernetes operator for handling inbound and outbound events
- [flux2](https://github.com/fluxcd/flux2): The Flux CLI
- [source-manager](https://github.com/fluxcd/source-controller): Kubernetes operator for managing sources (Git and Helm repositories, S3-compatible Buckets)
- [kustomize-controller](https://github.com/fluxcd/kustomize-controller): Kubernetes operator for building GitOps pipelines with Kustomize
- [helm-controller](https://github.com/fluxcd/helm-controller): Kubernetes operator for building GitOps pipelines with Helm
- [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
@@ -63,6 +65,12 @@ for source changes.
### 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
```bash

View File

@@ -1,4 +1,7 @@
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
@@ -11,10 +14,13 @@ fmt:
vet:
go vet ./...
test: tidy fmt vet docs
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet docs
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
install:
@@ -22,7 +28,7 @@ install:
.PHONY: docs
docs:
rm docs/cmd/*
rm -rf docs/cmd/*
mkdir -p ./docs/cmd && go run ./cmd/flux/ docgen
install-dev:

View File

@@ -60,11 +60,12 @@ To get started with Flux, start [browsing the
documentation](https://toolkit.fluxcd.io) or get started with one of
the following guides:
- [Get started with Flux (deep dive)](https://toolkit.fluxcd.io/get-started/)
- [Installation](https://toolkit.fluxcd.io/guides/installation/)
- [Get started with Flux](https://toolkit.fluxcd.io/get-started/)
- [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/)
- [Setup Notifications](https://toolkit.fluxcd.io/guides/notifications/)
- [Setup Webhook Receivers](https://toolkit.fluxcd.io/guides/webhook-receivers/)
- [Automate image updates to Git](https://toolkit.fluxcd.io/guides/image-update/)
- [Manage Kubernetes secrets with Mozilla SOPS](https://toolkit.fluxcd.io/guides/mozilla-sops/)
If you need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
## GitOps Toolkit
@@ -94,32 +95,33 @@ guides](https://toolkit.fluxcd.io/dev-guides/source-watcher/).
- [Provider CRD](https://toolkit.fluxcd.io/components/notification/provider/)
- [Alert CRD](https://toolkit.fluxcd.io/components/notification/alert/)
- [Receiver CRD](https://toolkit.fluxcd.io/components/notification/receiver/)
- [Image Automation Controllers](https://toolkit.fluxcd.io/components/image/controller/)
- [ImageRepository CRD](https://toolkit.fluxcd.io/components/image/imagerepositories/)
- [ImagePolicy CRD](https://toolkit.fluxcd.io/components/image/imagepolicies/)
- [ImageUpdateAutomation CRD](https://toolkit.fluxcd.io/components/image/imageupdateautomations/)
## Community
The Flux project is always looking for new contributors and there are a multitude of ways to get involved.
Depending on what you want to do, some of the following bits might be your first steps:
Need help or want to contribute? Please see the links below. The Flux project is always looking for
new contributors and there are a multitude of ways to get involved.
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
- Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
- Join the [planning discussions](https://github.com/fluxcd/flux2/discussions)
- And if you are completely new to Flux and the GitOps Toolkit, take a look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
- To be part of the conversation about Flux's development, [join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
- Check out [how to contribute](CONTRIBUTING.md) to the project
- Getting Started?
- Look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
- Need help?
- First: Ask questions on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
- Second: Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
- Please follow our [Support Guidelines](https://fluxcd.io/support/)
(in short: be nice, be respectful of volunteers' time, understand that maintainers and
contributors cannot respond to all DMs, and keep discussions in the public #flux channel as much as possible).
- Have feature proposals or want to contribute?
- Propose features on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
- [Join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
- Check out [how to contribute](CONTRIBUTING.md) to the project
### Upcoming Events
- 11 Jan 2021 - [Helm + GitOps = ⚡️⚡️⚡️ with Scott Rigby](https://www.meetup.com/GitOps-Community/events/275348736/)
- 25 Jan 2021 - [GitOps Core Concepts & How to Teach Your Teams with Leigh Capili](https://www.meetup.com/GitOps-Community/events/275625806/)
### Events
### Featured Talks
- 14 Dec 2020 - [The Power of GitOps with Flux and Flagger (GitOps Hands-On) with Leigh Capili](https://youtu.be/cB7iXeNLteE)
- 30 Nov 2020 - [The Power of GitOps with Flux 2 - Part 3 with Leigh Capili](https://youtu.be/N_K5g7o9JKg)
- 24 Nov 2020 - [Flux CD v2 with GitOps Toolkit - Kubernetes Deployment and Sync Mechanism](https://youtu.be/R6OeIgb7lUI)
- 02 Nov 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 2 with Leigh Capili](https://youtu.be/fC2YCxQRUwU)
- 28 Oct 2020 - [The Kubelist Podcast: Flux with Michael Bridgen](https://www.heavybit.com/library/podcasts/the-kubelist-podcast/ep-5-flux-with-michael-bridgen-of-weaveworks/)
- 19 Oct 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 1 with Leigh Capili](https://youtu.be/0v5bjysXTL8)
- 12 Oct 2020 - [Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
- 04 Sep 2020 - [KubeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
- 25 Jun 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
Check out our **[events calendar](https://fluxcd.io/community/#talks)**,
both with upcoming talks you can attend or past events videos you can watch.
We look forward to seeing you with us!

View File

@@ -1,6 +0,0 @@
FROM stefanprodan/alpine-base:latest
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -10,19 +10,24 @@ Usage:
run: flux -v
```
This action places the `flux` binary inside your repository root under `bin/flux`.
You should add `bin/flux` to your `.gitignore` file, as in the following example:
```gitignore
# ignore flux binary
bin/flux
```
Note that this action can only be used on GitHub **Linux AMD64** runners.
The latest stable version of the `flux` binary is downloaded from
GitHub [releases](https://github.com/fluxcd/flux2/releases)
and placed at `/usr/local/bin/flux`.
You can download a specific version with:
```yaml
steps:
- name: Setup Flux CLI
uses: fluxcd/flux2/action@main
with:
version: 0.8.0
```
### Automate Flux updates
Example workflow for updating Flux's components generated with `flux bootstrap --arch=amd64 --path=clusters/production`:
Example workflow for updating Flux's components generated with `flux bootstrap --path=clusters/production`:
```yaml
name: update-flux
@@ -43,7 +48,7 @@ jobs:
- name: Check for updates
id: update
run: |
flux install --arch=amd64 \
flux install \
--export > ./clusters/production/flux-system/gotk-components.yaml
VERSION="$(flux -v)"

View File

@@ -1,15 +1,38 @@
name: 'kustomize'
description: 'A GitHub Action for running Flux commands'
author: 'Flux project'
name: Setup Flux CLI
description: A GitHub Action for running Flux commands
author: Stefan Prodan
branding:
icon: 'command'
color: 'blue'
color: blue
icon: command
inputs:
version:
description: 'strict semver'
description: "Flux version e.g. 0.8.0 (defaults to latest stable release)"
required: false
runs:
using: 'docker'
image: 'Dockerfile'
args:
- ${{ inputs.version }}
using: composite
steps:
- name: "Download flux binary to tmp"
shell: bash
run: |
VERSION=${{ inputs.version }}
if [ -z $VERSION ]; then
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
fi
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_amd64.tar.gz"
curl -sL ${BIN_URL} -o /tmp/flux.tar.gz
mkdir -p /tmp/flux
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz
- name: "Add flux binary to /usr/local/bin"
shell: bash
run: |
sudo cp /tmp/flux/flux /usr/local/bin
- name: "Cleanup tmp"
shell: bash
run: |
rm -rf /tmp/flux/ /tmp/flux.tar.gz
- name: "Verify correct installation of binary"
shell: bash
run: |
flux -v

View File

@@ -1,40 +0,0 @@
#!/bin/bash
# Copyright 2020 The Flux authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
VERSION=$1
if [ -z $VERSION ]; then
# Find latest release if no version is specified
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
fi
# Download linux binary
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_amd64.tar.gz"
curl -sL $BIN_URL | tar xz
# Copy binary to GitHub runner
mkdir -p $GITHUB_WORKSPACE/bin
mv ./flux $GITHUB_WORKSPACE/bin
chmod +x $GITHUB_WORKSPACE/bin/flux
# Print version
$GITHUB_WORKSPACE/bin/flux -v
# Add binary to GitHub runner path
echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH
echo "$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)/bin" >> $GITHUB_PATH

View File

@@ -19,13 +19,11 @@ package main
import (
"context"
"fmt"
"net/url"
"path/filepath"
"time"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -36,7 +34,9 @@ import (
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
kus "github.com/fluxcd/flux2/pkg/manifestgen/kustomization"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
"github.com/fluxcd/flux2/pkg/status"
)
var bootstrapCmd = &cobra.Command{
@@ -45,62 +45,74 @@ var bootstrapCmd = &cobra.Command{
Long: "The bootstrap sub-commands bootstrap the toolkit components on the targeted Git provider.",
}
var (
bootstrapVersion string
bootstrapDefaultComponents []string
bootstrapExtraComponents []string
bootstrapRegistry string
bootstrapImagePullSecret string
bootstrapBranch string
bootstrapWatchAllNamespaces bool
bootstrapNetworkPolicy bool
bootstrapManifestsPath string
bootstrapArch flags.Arch
bootstrapLogLevel = flags.LogLevel(defaults.LogLevel)
bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"}
bootstrapTokenAuth bool
bootstrapClusterDomain string
)
type bootstrapFlags struct {
version string
defaultComponents []string
extraComponents []string
registry string
imagePullSecret string
branch string
watchAllNamespaces bool
networkPolicy bool
manifestsPath string
arch flags.Arch
logLevel flags.LogLevel
requiredComponents []string
tokenAuth bool
clusterDomain string
tolerationKeys []string
}
const (
bootstrapDefaultBranch = "main"
)
var bootstrapArgs = NewBootstrapFlags()
func init() {
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapVersion, "version", "v", defaults.Version,
"toolkit version")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapDefaultComponents, "components", defaults.Components,
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", "",
"toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapExtraComponents, "components-extra", nil,
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapRegistry, "registry", "ghcr.io/fluxcd",
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
"container registry where the toolkit images are published")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapImagePullSecret, "image-pull-secret", "",
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry")
bootstrapCmd.PersistentFlags().Var(&bootstrapArch, "arch", bootstrapArch.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapBranch, "branch", bootstrapDefaultBranch,
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch,
"default branch (for GitHub this must match the default branch setting for the organization)")
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapWatchAllNamespaces, "watch-all-namespaces", true,
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.watchAllNamespaces, "watch-all-namespaces", true,
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapNetworkPolicy, "network-policy", true,
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.networkPolicy, "network-policy", true,
"deny ingress access to the toolkit controllers from other namespaces using network policies")
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapTokenAuth, "token-auth", false,
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false,
"when enabled, the personal access token will be used instead of SSH deploy key")
bootstrapCmd.PersistentFlags().Var(&bootstrapLogLevel, "log-level", bootstrapLogLevel.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapClusterDomain, "cluster-domain", defaults.ClusterDomain, "internal cluster domain")
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
rootCmd.AddCommand(bootstrapCmd)
}
func NewBootstrapFlags() bootstrapFlags {
return bootstrapFlags{
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
requiredComponents: []string{"source-controller", "kustomize-controller"},
}
}
func bootstrapComponents() []string {
return append(bootstrapDefaultComponents, bootstrapExtraComponents...)
return append(bootstrapArgs.defaultComponents, bootstrapArgs.extraComponents...)
}
func bootstrapValidate() error {
components := bootstrapComponents()
for _, component := range bootstrapRequiredComponents {
for _, component := range bootstrapArgs.requiredComponents {
if !utils.ContainsItemString(components, component) {
return fmt.Errorf("component %s is required", component)
}
@@ -114,28 +126,43 @@ func bootstrapValidate() error {
}
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
if ver, err := getVersion(bootstrapArgs.version); err != nil {
return "", err
} else {
bootstrapArgs.version = ver
}
manifestsBase := ""
if isEmbeddedVersion(bootstrapArgs.version) {
if err := writeEmbeddedManifests(tmpDir); err != nil {
return "", err
}
manifestsBase = tmpDir
}
opts := install.Options{
BaseURL: localManifests,
Version: bootstrapVersion,
Version: bootstrapArgs.version,
Namespace: namespace,
Components: bootstrapComponents(),
Registry: bootstrapRegistry,
ImagePullSecret: bootstrapImagePullSecret,
WatchAllNamespaces: bootstrapWatchAllNamespaces,
NetworkPolicy: bootstrapNetworkPolicy,
LogLevel: bootstrapLogLevel.String(),
NotificationController: defaults.NotificationController,
ManifestFile: defaults.ManifestFile,
Timeout: timeout,
Registry: bootstrapArgs.registry,
ImagePullSecret: bootstrapArgs.imagePullSecret,
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
NetworkPolicy: bootstrapArgs.networkPolicy,
LogLevel: bootstrapArgs.logLevel.String(),
NotificationController: rootArgs.defaults.NotificationController,
ManifestFile: rootArgs.defaults.ManifestFile,
Timeout: rootArgs.timeout,
TargetPath: targetPath,
ClusterDomain: bootstrapClusterDomain,
ClusterDomain: bootstrapArgs.clusterDomain,
TolerationKeys: bootstrapArgs.tolerationKeys,
}
if localManifests == "" {
opts.BaseURL = defaults.BaseURL
opts.BaseURL = rootArgs.defaults.BaseURL
}
output, err := install.Generate(opts)
output, err := install.Generate(opts, manifestsBase)
if err != nil {
return "", fmt.Errorf("generating install manifests failed: %w", err)
}
@@ -149,15 +176,24 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
kubectlArgs := []string{"apply", "-f", manifestPath}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed: %w", err)
}
for _, deployment := range components {
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
}
kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
componentRefs, err := buildComponentObjectRefs(components...)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
logger.Waitingf("verifying installation")
if err := statusChecker.Assess(componentRefs...); err != nil {
return fmt.Errorf("install failed")
}
return nil
}
@@ -169,6 +205,7 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
URL: url,
Branch: branch,
Interval: interval,
Secret: namespace,
TargetPath: targetPath,
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
}
@@ -183,28 +220,38 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
return "", err
}
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
}
if _, err = kustomization.WriteFile(tmpDir); err != nil {
return "", err
}
return outputDir, nil
}
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error {
kubectlArgs := []string{"apply", "-k", manifestsPath}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
return err
}
logger.Waitingf("waiting for cluster sync")
var gitRepository sourcev1.GitRepository
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil {
return err
}
var kustomization kustomizev1.Kustomization
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
return err
}
@@ -238,35 +285,6 @@ func shouldCreateDeployKey(ctx context.Context, kubeClient client.Client, namesp
return false
}
func generateDeployKey(ctx context.Context, kubeClient client.Client, url *url.URL, namespace string) (string, error) {
pair, err := generateKeyPair(ctx, sourceGitKeyAlgorithm, sourceGitRSABits, sourceGitECDSACurve)
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) {
namespacedName := types.NamespacedName{
Name: namespace,

View File

@@ -26,14 +26,14 @@ import (
"path/filepath"
"time"
"github.com/fluxcd/pkg/git"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/pkg/git"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
)
var bootstrapGitHubCmd = &cobra.Command{
@@ -71,36 +71,34 @@ the bootstrap command will perform an upgrade if needed.`,
RunE: bootstrapGitHubCmdRun,
}
var (
ghOwner string
ghRepository string
ghInterval time.Duration
ghPersonal bool
ghPrivate bool
ghHostname string
ghPath flags.SafeRelativePath
ghTeams []string
ghDelete bool
ghSSHHostname string
)
type githubFlags struct {
owner string
repository string
interval time.Duration
personal bool
private bool
hostname string
path flags.SafeRelativePath
teams []string
sshHostname string
}
const (
ghDefaultPermission = "maintain"
)
func init() {
bootstrapGitHubCmd.Flags().StringVar(&ghOwner, "owner", "", "GitHub user or organization name")
bootstrapGitHubCmd.Flags().StringVar(&ghRepository, "repository", "", "GitHub repository name")
bootstrapGitHubCmd.Flags().StringArrayVar(&ghTeams, "team", []string{}, "GitHub team to be given maintainer access")
bootstrapGitHubCmd.Flags().BoolVar(&ghPersonal, "personal", false, "is personal repository")
bootstrapGitHubCmd.Flags().BoolVar(&ghPrivate, "private", true, "is private repository")
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
bootstrapGitHubCmd.Flags().StringVar(&ghSSHHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitHubCmd.Flags().Var(&ghPath, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
var githubArgs githubFlags
bootstrapGitHubCmd.Flags().BoolVar(&ghDelete, "delete", false, "delete repository (used for testing only)")
bootstrapGitHubCmd.Flags().MarkHidden("delete")
func init() {
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository name")
bootstrapGitHubCmd.Flags().StringArrayVar(&githubArgs.teams, "team", []string{}, "GitHub team to be given maintainer access")
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "if true, the owner is assumed to be a GitHub user; otherwise an org")
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "if true, the repository is assumed to be private")
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitHubCmd.Flags().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
bootstrapCmd.AddCommand(bootstrapGitHubCmd)
}
@@ -115,50 +113,54 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, namespace, filepath.ToSlash(ghPath.String()))
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(
ctx,
kubeClient,
rootArgs.namespace,
filepath.ToSlash(githubArgs.path.String()),
)
if bootstrapPathDiffers {
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
}
repository, err := git.NewRepository(ghRepository, ghOwner, ghHostname, ghToken, "flux", ghOwner+"@users.noreply.github.com")
repository, err := git.NewRepository(
githubArgs.repository,
githubArgs.owner,
githubArgs.hostname,
ghToken,
"flux",
githubArgs.owner+"@users.noreply.github.com",
)
if err != nil {
return err
}
if ghSSHHostname != "" {
repository.SSHHost = ghSSHHostname
if githubArgs.sshHostname != "" {
repository.SSHHost = githubArgs.sshHostname
}
provider := &git.GithubProvider{
IsPrivate: ghPrivate,
IsPersonal: ghPersonal,
IsPrivate: githubArgs.private,
IsPersonal: githubArgs.personal,
}
tmpDir, err := ioutil.TempDir("", namespace)
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
if ghDelete {
if err := provider.DeleteRepository(ctx, repository); err != nil {
return err
}
logger.Successf("repository deleted")
return nil
}
// create GitHub repository if doesn't exists
logger.Actionf("connecting to %s", ghHostname)
logger.Actionf("connecting to %s", githubArgs.hostname)
changed, err := provider.CreateRepository(ctx, repository)
if err != nil {
return err
@@ -169,8 +171,8 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
withErrors := false
// add teams to org repository
if !ghPersonal {
for _, team := range ghTeams {
if !githubArgs.personal {
for _, team := range githubArgs.teams {
if changed, err := provider.AddTeam(ctx, repository, team, ghDefaultPermission); err != nil {
logger.Failuref(err.Error())
withErrors = true
@@ -181,20 +183,29 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
}
// clone repository and checkout the main branch
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil {
return err
}
logger.Successf("repository cloned")
// generate install manifests
logger.Generatef("generating manifests")
installManifest, err := generateInstallManifests(ghPath.String(), namespace, tmpDir, bootstrapManifestsPath)
installManifest, err := generateInstallManifests(
githubArgs.path.String(),
rootArgs.namespace,
tmpDir,
bootstrapArgs.manifestsPath,
)
if err != nil {
return err
}
// stage install manifests
changed, err = repository.Commit(ctx, path.Join(ghPath.String(), namespace), "Add manifests")
changed, err = repository.Commit(
ctx,
path.Join(githubArgs.path.String(), rootArgs.namespace),
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version),
)
if err != nil {
return err
}
@@ -210,55 +221,59 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
}
// determine if repo synchronization is working
isInstall := shouldInstallManifests(ctx, kubeClient, namespace)
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace)
if isInstall {
// apply install manifests
logger.Actionf("installing components in %s namespace", namespace)
logger.Actionf("installing components in %s namespace", rootArgs.namespace)
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
return err
}
logger.Successf("install completed")
}
repoURL := repository.GetURL()
if bootstrapTokenAuth {
// setup HTTPS token auth
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
Namespace: namespace,
},
StringData: map[string]string{
"username": "git",
"password": ghToken,
},
repoURL := repository.GetSSH()
secretOpts := sourcesecret.Options{
Name: rootArgs.namespace,
Namespace: rootArgs.namespace,
}
if bootstrapArgs.tokenAuth {
// Setup HTTPS token auth
repoURL = repository.GetURL()
secretOpts.Username = "git"
secretOpts.Password = ghToken
} else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
// Setup SSH auth
u, err := url.Parse(repoURL)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
if 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
}
} else {
// setup SSH deploy key
repoURL = repository.GetSSH()
if shouldCreateDeployKey(ctx, kubeClient, 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, namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
}
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
keyName := "flux"
if ghPath != "" {
keyName = fmt.Sprintf("flux-%s", ghPath)
if 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
} else if changed {
logger.Successf("deploy key configured")
@@ -268,13 +283,25 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
// configure repo synchronization
logger.Actionf("generating sync manifests")
syncManifests, err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, filepath.ToSlash(ghPath.String()), tmpDir, ghInterval)
syncManifests, err := generateSyncManifests(
repoURL,
bootstrapArgs.branch,
rootArgs.namespace,
rootArgs.namespace,
filepath.ToSlash(githubArgs.path.String()),
tmpDir,
githubArgs.interval,
)
if err != nil {
return err
}
// commit and push manifests
if changed, err = repository.Commit(ctx, path.Join(ghPath.String(), namespace), "Add manifests"); err != nil {
if changed, err = repository.Commit(
ctx,
path.Join(githubArgs.path.String(), rootArgs.namespace),
fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version),
); err != nil {
return err
} else if changed {
if err := repository.Push(ctx); err != nil {
@@ -285,7 +312,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
// apply manifests and waiting for sync
logger.Actionf("applying sync manifests")
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
return err
}

View File

@@ -29,12 +29,13 @@ import (
"github.com/spf13/cobra"
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/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
)
var bootstrapGitLabCmd = &cobra.Command{
@@ -73,26 +74,28 @@ const (
gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z`
)
var (
glOwner string
glRepository string
glInterval time.Duration
glPersonal bool
glPrivate bool
glHostname string
glSSHHostname string
glPath flags.SafeRelativePath
)
type gitlabFlags struct {
owner string
repository string
interval time.Duration
personal bool
private bool
hostname string
sshHostname string
path flags.SafeRelativePath
}
var gitlabArgs gitlabFlags
func init() {
bootstrapGitLabCmd.Flags().StringVar(&glOwner, "owner", "", "GitLab user or group name")
bootstrapGitLabCmd.Flags().StringVar(&glRepository, "repository", "", "GitLab repository name")
bootstrapGitLabCmd.Flags().BoolVar(&glPersonal, "personal", false, "is personal repository")
bootstrapGitLabCmd.Flags().BoolVar(&glPrivate, "private", true, "is private repository")
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitLabCmd.Flags().Var(&glPath, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name")
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.personal, "personal", false, "if true, the owner is assumed to be a GitLab user; otherwise a group")
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.private, "private", true, "if true, the repository is assumed to be private")
bootstrapGitLabCmd.Flags().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.sshHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
}
@@ -103,54 +106,61 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("%s environment variable not found", git.GitLabTokenName)
}
projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, glRepository)
projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository)
if err != nil {
return err
}
if !projectNameIsValid {
return fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", glRepository)
return fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", gitlabArgs.repository)
}
if err := bootstrapValidate(); err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, namespace, filepath.ToSlash(glPath.String()))
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String()))
if bootstrapPathDiffers {
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
}
repository, err := git.NewRepository(glRepository, glOwner, glHostname, glToken, "flux", glOwner+"@users.noreply.gitlab.com")
repository, err := git.NewRepository(
gitlabArgs.repository,
gitlabArgs.owner,
gitlabArgs.hostname,
glToken,
"flux",
gitlabArgs.owner+"@users.noreply.gitlab.com",
)
if err != nil {
return err
}
if glSSHHostname != "" {
repository.SSHHost = glSSHHostname
if gitlabArgs.sshHostname != "" {
repository.SSHHost = gitlabArgs.sshHostname
}
tmpDir, err := ioutil.TempDir("", namespace)
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
provider := &git.GitLabProvider{
IsPrivate: glPrivate,
IsPersonal: glPersonal,
IsPrivate: gitlabArgs.private,
IsPersonal: gitlabArgs.personal,
}
// create GitLab project if doesn't exists
logger.Actionf("connecting to %s", glHostname)
logger.Actionf("connecting to %s", gitlabArgs.hostname)
changed, err := provider.CreateRepository(ctx, repository)
if err != nil {
return err
@@ -160,20 +170,29 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
}
// clone repository and checkout the master branch
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil {
return err
}
logger.Successf("repository cloned")
// generate install manifests
logger.Generatef("generating manifests")
installManifest, err := generateInstallManifests(glPath.String(), namespace, tmpDir, bootstrapManifestsPath)
installManifest, err := generateInstallManifests(
gitlabArgs.path.String(),
rootArgs.namespace,
tmpDir,
bootstrapArgs.manifestsPath,
)
if err != nil {
return err
}
// stage install manifests
changed, err = repository.Commit(ctx, path.Join(glPath.String(), namespace), "Add manifests")
changed, err = repository.Commit(
ctx,
path.Join(gitlabArgs.path.String(), rootArgs.namespace),
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version),
)
if err != nil {
return err
}
@@ -189,55 +208,59 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
}
// determine if repo synchronization is working
isInstall := shouldInstallManifests(ctx, kubeClient, namespace)
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace)
if isInstall {
// apply install manifests
logger.Actionf("installing components in %s namespace", namespace)
logger.Actionf("installing components in %s namespace", rootArgs.namespace)
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
return err
}
logger.Successf("install completed")
}
repoURL := repository.GetURL()
if bootstrapTokenAuth {
// setup HTTPS token auth
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
Namespace: namespace,
},
StringData: map[string]string{
"username": "git",
"password": glToken,
},
repoURL := repository.GetSSH()
secretOpts := sourcesecret.Options{
Name: rootArgs.namespace,
Namespace: rootArgs.namespace,
}
if bootstrapArgs.tokenAuth {
// Setup HTTPS token auth
repoURL = repository.GetURL()
secretOpts.Username = "git"
secretOpts.Password = glToken
} else if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
// Setup SSH auth
u, err := url.Parse(repoURL)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
if 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
}
} else {
// setup SSH deploy key
repoURL = repository.GetSSH()
if shouldCreateDeployKey(ctx, kubeClient, 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, namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
}
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
keyName := "flux"
if glPath != "" {
keyName = fmt.Sprintf("flux-%s", glPath)
if 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
} else if changed {
logger.Successf("deploy key configured")
@@ -247,13 +270,25 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
// configure repo synchronization
logger.Actionf("generating sync manifests")
syncManifests, err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, filepath.ToSlash(glPath.String()), tmpDir, glInterval)
syncManifests, err := generateSyncManifests(
repoURL,
bootstrapArgs.branch,
rootArgs.namespace,
rootArgs.namespace,
filepath.ToSlash(gitlabArgs.path.String()),
tmpDir,
gitlabArgs.interval,
)
if err != nil {
return err
}
// commit and push manifests
if changed, err = repository.Commit(ctx, path.Join(glPath.String(), namespace), "Add manifests"); err != nil {
if changed, err = repository.Commit(
ctx,
path.Join(gitlabArgs.path.String(), rootArgs.namespace),
fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version),
); err != nil {
return err
} else if changed {
if err := repository.Push(ctx); err != nil {
@@ -264,7 +299,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
// apply manifests and waiting for sync
logger.Actionf("applying sync manifests")
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
return err
}

View File

@@ -21,13 +21,20 @@ import (
"encoding/json"
"os"
"os/exec"
"strings"
"time"
"github.com/blang/semver/v4"
"github.com/fluxcd/flux2/internal/utils"
"github.com/Masterminds/semver/v3"
"github.com/spf13/cobra"
v1 "k8s.io/api/apps/v1"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/pkg/version"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/pkg/status"
)
var checkCmd = &cobra.Command{
@@ -44,39 +51,46 @@ the local environment is configured correctly and if the installed components ar
RunE: runCheckCmd,
}
var (
checkPre bool
checkComponents []string
)
type checkFlags struct {
pre bool
components []string
extraComponents []string
}
type kubectlVersion struct {
ClientVersion *apimachineryversion.Info `json:"clientVersion"`
}
var checkArgs checkFlags
func init() {
checkCmd.Flags().BoolVarP(&checkPre, "pre", "", false,
checkCmd.Flags().BoolVarP(&checkArgs.pre, "pre", "", false,
"only run pre-installation checks")
checkCmd.Flags().StringSliceVar(&checkComponents, "components", defaults.Components,
checkCmd.Flags().StringSliceVar(&checkArgs.components, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values")
checkCmd.Flags().StringSliceVar(&checkArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
rootCmd.AddCommand(checkCmd)
}
func runCheckCmd(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
logger.Actionf("checking prerequisites")
checkFailed := false
if !kubectlCheck(ctx, ">=1.18.0") {
fluxCheck()
if !kubectlCheck(ctx, ">=1.18.0-0") {
checkFailed = true
}
if !kubernetesCheck(">=1.16.0") {
if !kubernetesCheck(">=1.16.0-0") {
checkFailed = true
}
if checkPre {
if checkArgs.pre {
if checkFailed {
os.Exit(1)
}
@@ -95,7 +109,29 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
return nil
}
func kubectlCheck(ctx context.Context, version string) bool {
func fluxCheck() {
curSv, err := version.ParseVersion(VERSION)
if err != nil {
return
}
// Exclude development builds.
if curSv.Prerelease() != "" {
return
}
latest, err := install.GetLatestVersion()
if err != nil {
return
}
latestSv, err := version.ParseVersion(latest)
if err != nil {
return
}
if latestSv.GreaterThan(curSv) {
logger.Failuref("flux %s <%s (new version is available, please upgrade)", curSv, latestSv)
}
}
func kubectlCheck(ctx context.Context, constraint string) bool {
_, err := exec.LookPath("kubectl")
if err != nil {
logger.Failuref("kubectl not found")
@@ -103,7 +139,7 @@ func kubectlCheck(ctx context.Context, version string) bool {
}
kubectlArgs := []string{"version", "--client", "--output", "json"}
output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...)
output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
if err != nil {
logger.Failuref("kubectl version can't be determined")
return false
@@ -111,77 +147,93 @@ func kubectlCheck(ctx context.Context, version string) bool {
kv := &kubectlVersion{}
if err = json.Unmarshal([]byte(output), kv); err != nil {
logger.Failuref("kubectl version output can't be unmarshaled")
logger.Failuref("kubectl version output can't be unmarshalled")
return false
}
v, err := semver.ParseTolerant(kv.ClientVersion.GitVersion)
v, err := version.ParseVersion(kv.ClientVersion.GitVersion)
if err != nil {
logger.Failuref("kubectl version can't be parsed")
return false
}
rng, _ := semver.ParseRange(version)
if !rng(v) {
logger.Failuref("kubectl version must be %s", version)
c, _ := semver.NewConstraint(constraint)
if !c.Check(v) {
logger.Failuref("kubectl version %s < %s", v.Original(), constraint)
return false
}
logger.Successf("kubectl %s %s", v.String(), version)
logger.Successf("kubectl %s %s", v.String(), constraint)
return true
}
func kubernetesCheck(version string) bool {
cfg, err := utils.KubeConfig(kubeconfig, kubecontext)
func kubernetesCheck(constraint string) bool {
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
return false
}
client, err := kubernetes.NewForConfig(cfg)
clientSet, err := kubernetes.NewForConfig(cfg)
if err != nil {
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
return false
}
ver, err := client.Discovery().ServerVersion()
kv, err := clientSet.Discovery().ServerVersion()
if err != nil {
logger.Failuref("Kubernetes API call failed: %s", err.Error())
return false
}
v, err := semver.ParseTolerant(ver.String())
v, err := version.ParseVersion(kv.String())
if err != nil {
logger.Failuref("Kubernetes version can't be determined")
return false
}
rng, _ := semver.ParseRange(version)
if !rng(v) {
logger.Failuref("Kubernetes version must be %s", version)
c, _ := semver.NewConstraint(constraint)
if !c.Check(v) {
logger.Failuref("Kubernetes version %s < %s", v.Original(), constraint)
return false
}
logger.Successf("Kubernetes %s %s", v.String(), version)
logger.Successf("Kubernetes %s %s", v.String(), constraint)
return true
}
func componentsCheck() bool {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return false
}
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 {
return false
}
ok := true
for _, deployment := range checkComponents {
kubectlArgs := []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...); err != nil {
logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
ok = false
} else {
logger.Successf("%s is healthy", deployment)
}
kubectlArgs = []string{"-n", namespace, "get", "deployment", deployment, "-o", "jsonpath=\"{..image}\""}
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...); err == nil {
logger.Actionf(strings.TrimPrefix(strings.TrimSuffix(output, "\""), "\""))
selector := client.MatchingLabels{"app.kubernetes.io/instance": rootArgs.namespace}
var list v1.DeploymentList
if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace), selector); err == nil {
for _, d := range list.Items {
if ref, err := buildComponentObjectRefs(d.Name); err == nil {
if err := statusChecker.Assess(ref...); err != nil {
ok = false
}
}
for _, c := range d.Spec.Template.Spec.Containers {
logger.Actionf(c.Image)
}
}
}
return ok

View File

@@ -25,13 +25,9 @@ import (
var completionFishCmd = &cobra.Command{
Use: "fish",
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)
To configure your fish shell to load completions for each session write this script to your completions dir:
flux completion fish > ~/.config/fish/completions/flux
flux completion fish > ~/.config/fish/completions/flux.fish
See http://fishshell.com/docs/current/index.html#completion-own for more details
`,

View File

@@ -38,16 +38,18 @@ var createCmd = &cobra.Command{
Long: "The create sub-commands generate sources and resources.",
}
var (
type createFlags struct {
interval time.Duration
export bool
labels []string
)
}
var createArgs createFlags
func init() {
createCmd.PersistentFlags().DurationVarP(&interval, "interval", "", time.Minute, "source sync interval")
createCmd.PersistentFlags().BoolVar(&export, "export", false, "export in YAML format to stdout")
createCmd.PersistentFlags().StringSliceVar(&labels, "label", nil,
createCmd.PersistentFlags().DurationVarP(&createArgs.interval, "interval", "", time.Minute, "source sync interval")
createCmd.PersistentFlags().BoolVar(&createArgs.export, "export", false, "export in YAML format to stdout")
createCmd.PersistentFlags().StringSliceVar(&createArgs.labels, "label", nil,
"set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)")
rootCmd.AddCommand(createCmd)
}
@@ -99,10 +101,10 @@ type upsertWaitable interface {
// resource, then waiting for it to reconcile. See the note on
// `upsert` for how to work with the `mutate` argument.
func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) // NB globals
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) // NB globals
if err != nil {
return err
}
@@ -116,7 +118,7 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
}
logger.Waitingf("waiting for %s reconciliation", names.kind)
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isReady(ctx, kubeClient, namespacedName, object)); err != nil {
return err
}
@@ -126,7 +128,7 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
func parseLabels() (map[string]string, error) {
result := make(map[string]string)
for _, label := range labels {
for _, label := range createArgs.labels {
// validate key value pair
parts := strings.Split(label, "=")
if len(parts) != 2 {

View File

@@ -20,11 +20,7 @@ import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -33,6 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/internal/utils"
)
var createAlertCmd = &cobra.Command{
@@ -49,16 +48,18 @@ var createAlertCmd = &cobra.Command{
RunE: createAlertCmdRun,
}
var (
aProviderRef string
aEventSeverity string
aEventSources []string
)
type alertFlags struct {
providerRef string
eventSeverity string
eventSources []string
}
var alertArgs alertFlags
func init() {
createAlertCmd.Flags().StringVar(&aProviderRef, "provider-ref", "", "reference to provider")
createAlertCmd.Flags().StringVar(&aEventSeverity, "event-severity", "", "severity of events to send alerts for")
createAlertCmd.Flags().StringArrayVar(&aEventSources, "event-source", []string{}, "sources that should generate alerts (<kind>/<name>)")
createAlertCmd.Flags().StringVar(&alertArgs.providerRef, "provider-ref", "", "reference to provider")
createAlertCmd.Flags().StringVar(&alertArgs.eventSeverity, "event-severity", "", "severity of events to send alerts for")
createAlertCmd.Flags().StringArrayVar(&alertArgs.eventSources, "event-source", []string{}, "sources that should generate alerts (<kind>/<name>)")
createCmd.AddCommand(createAlertCmd)
}
@@ -68,12 +69,12 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
if aProviderRef == "" {
if alertArgs.providerRef == "" {
return fmt.Errorf("provider ref is required")
}
eventSources := []notificationv1.CrossNamespaceObjectReference{}
for _, eventSource := range aEventSources {
for _, eventSource := range alertArgs.eventSources {
kind, name := utils.ParseObjectKindName(eventSource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
@@ -94,34 +95,34 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if !export {
if !createArgs.export {
logger.Generatef("generating Alert")
}
alert := notificationv1.Alert{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
Spec: notificationv1.AlertSpec{
ProviderRef: corev1.LocalObjectReference{
Name: aProviderRef,
ProviderRef: meta.LocalObjectReference{
Name: alertArgs.providerRef,
},
EventSeverity: aEventSeverity,
EventSeverity: alertArgs.eventSeverity,
EventSources: eventSources,
Suspend: false,
},
}
if export {
if createArgs.export {
return exportAlert(alert)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
@@ -133,7 +134,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}

View File

@@ -21,7 +21,6 @@ import (
"fmt"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -29,9 +28,10 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/internal/utils"
)
var createAlertProviderCmd = &cobra.Command{
@@ -54,20 +54,22 @@ var createAlertProviderCmd = &cobra.Command{
RunE: createAlertProviderCmdRun,
}
var (
apType string
apChannel string
apUsername string
apAddress string
apSecretRef string
)
type alertProviderFlags struct {
alertType string
channel string
username string
address string
secretRef string
}
var alertProviderArgs alertProviderFlags
func init() {
createAlertProviderCmd.Flags().StringVar(&apType, "type", "", "type of provider")
createAlertProviderCmd.Flags().StringVar(&apChannel, "channel", "", "channel to send messages to in the case of a chat provider")
createAlertProviderCmd.Flags().StringVar(&apUsername, "username", "", "bot username used by the provider")
createAlertProviderCmd.Flags().StringVar(&apAddress, "address", "", "path to either the git repository, chat provider or webhook")
createAlertProviderCmd.Flags().StringVar(&apSecretRef, "secret-ref", "", "name of secret containing authentication token")
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.alertType, "type", "", "type of provider")
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.channel, "channel", "", "channel to send messages to in the case of a chat provider")
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.username, "username", "", "bot username used by the provider")
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.address, "address", "", "path to either the git repository, chat provider or webhook")
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.secretRef, "secret-ref", "", "name of secret containing authentication token")
createCmd.AddCommand(createAlertProviderCmd)
}
@@ -77,7 +79,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
if apType == "" {
if alertProviderArgs.alertType == "" {
return fmt.Errorf("Provider type is required")
}
@@ -86,38 +88,38 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if !export {
if !createArgs.export {
logger.Generatef("generating Provider")
}
provider := notificationv1.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
Spec: notificationv1.ProviderSpec{
Type: apType,
Channel: apChannel,
Username: apUsername,
Address: apAddress,
Type: alertProviderArgs.alertType,
Channel: alertProviderArgs.channel,
Username: alertProviderArgs.username,
Address: alertProviderArgs.address,
},
}
if apSecretRef != "" {
provider.Spec.SecretRef = &corev1.LocalObjectReference{
Name: apSecretRef,
if alertProviderArgs.secretRef != "" {
provider.Spec.SecretRef = &meta.LocalObjectReference{
Name: alertProviderArgs.secretRef,
}
}
if export {
if createArgs.export {
return exportAlertProvider(provider)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
@@ -129,7 +131,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for Provider reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
return err
}

View File

@@ -18,12 +18,14 @@ package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/transform"
"github.com/spf13/cobra"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -62,11 +64,12 @@ var createHelmReleaseCmd = &cobra.Command{
--source=Bucket/podinfo \
--chart=./charts/podinfo
# Create a HelmRelease with values from a local YAML file
# Create a HelmRelease with values from local YAML files
flux create hr podinfo \
--source=HelmRepository/podinfo \
--chart=podinfo \
--values=./my-values.yaml
--values=./my-values1.yaml \
--values=./my-values2.yaml
# Create a HelmRelease with values from a Kubernetes secret
kubectl -n app create secret generic my-secret-values \
@@ -98,28 +101,30 @@ var createHelmReleaseCmd = &cobra.Command{
RunE: createHelmReleaseCmdRun,
}
var (
hrName string
hrSource flags.HelmChartSource
hrDependsOn []string
hrChart string
hrChartVersion string
hrTargetNamespace string
hrValuesFile string
hrValuesFrom flags.HelmReleaseValuesFrom
hrSAName string
)
type helmReleaseFlags struct {
name string
source flags.HelmChartSource
dependsOn []string
chart string
chartVersion string
targetNamespace string
valuesFile []string
valuesFrom flags.HelmReleaseValuesFrom
saName string
}
var helmReleaseArgs helmReleaseFlags
func init() {
createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
createHelmReleaseCmd.Flags().Var(&hrSource, "source", hrSource.Description())
createHelmReleaseCmd.Flags().StringVar(&hrChart, "chart", "", "Helm chart name or path")
createHelmReleaseCmd.Flags().StringVar(&hrChartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
createHelmReleaseCmd.Flags().StringArrayVar(&hrDependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
createHelmReleaseCmd.Flags().StringVar(&hrTargetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
createHelmReleaseCmd.Flags().StringVar(&hrSAName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
createHelmReleaseCmd.Flags().StringVar(&hrValuesFile, "values", "", "local path to the values.yaml file")
createHelmReleaseCmd.Flags().Var(&hrValuesFrom, "values-from", hrValuesFrom.Description())
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.name, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.source, "source", helmReleaseArgs.source.Description())
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chart, "chart", "", "Helm chart name or path")
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
createHelmReleaseCmd.Flags().StringArrayVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
createHelmReleaseCmd.Flags().StringArrayVar(&helmReleaseArgs.valuesFile, "values", nil, "local path to values.yaml files")
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
createCmd.AddCommand(createHelmReleaseCmd)
}
@@ -129,7 +134,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
if hrChart == "" {
if helmReleaseArgs.chart == "" {
return fmt.Errorf("chart name or path is required")
}
@@ -138,30 +143,30 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if !export {
if !createArgs.export {
logger.Generatef("generating HelmRelease")
}
helmRelease := helmv2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
Spec: helmv2.HelmReleaseSpec{
ReleaseName: hrName,
DependsOn: utils.MakeDependsOn(hrDependsOn),
ReleaseName: helmReleaseArgs.name,
DependsOn: utils.MakeDependsOn(helmReleaseArgs.dependsOn),
Interval: metav1.Duration{
Duration: interval,
Duration: createArgs.interval,
},
TargetNamespace: hrTargetNamespace,
TargetNamespace: helmReleaseArgs.targetNamespace,
Chart: helmv2.HelmChartTemplate{
Spec: helmv2.HelmChartTemplateSpec{
Chart: hrChart,
Version: hrChartVersion,
Chart: helmReleaseArgs.chart,
Version: helmReleaseArgs.chartVersion,
SourceRef: helmv2.CrossNamespaceObjectReference{
Kind: hrSource.Kind,
Name: hrSource.Name,
Kind: helmReleaseArgs.source.Kind,
Name: helmReleaseArgs.source.Name,
},
},
},
@@ -169,39 +174,58 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
},
}
if hrSAName != "" {
helmRelease.Spec.ServiceAccountName = hrSAName
if helmReleaseArgs.saName != "" {
helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
}
if hrValuesFile != "" {
data, err := ioutil.ReadFile(hrValuesFile)
if err != nil {
return fmt.Errorf("reading values from %s failed: %w", hrValuesFile, err)
if len(helmReleaseArgs.valuesFile) > 0 {
var valuesMap map[string]interface{}
for _, v := range helmReleaseArgs.valuesFile {
data, err := ioutil.ReadFile(v)
if err != nil {
return fmt.Errorf("reading values from %s failed: %w", v, err)
}
jsonBytes, err := yaml.YAMLToJSON(data)
if err != nil {
return fmt.Errorf("converting values to JSON from %s failed: %w", v, err)
}
jsonMap := make(map[string]interface{})
if err := json.Unmarshal(jsonBytes, &jsonMap); err != nil {
return fmt.Errorf("unmarshaling values from %s failed: %w", v, err)
}
if valuesMap == nil {
valuesMap = jsonMap
} else {
valuesMap = transform.MergeMaps(valuesMap, jsonMap)
}
}
json, err := yaml.YAMLToJSON(data)
jsonRaw, err := json.Marshal(valuesMap)
if err != nil {
return fmt.Errorf("converting values to JSON from %s failed: %w", hrValuesFile, err)
return fmt.Errorf("marshaling values failed: %w", err)
}
helmRelease.Spec.Values = &apiextensionsv1.JSON{Raw: json}
helmRelease.Spec.Values = &apiextensionsv1.JSON{Raw: jsonRaw}
}
if hrValuesFrom.String() != "" {
if helmReleaseArgs.valuesFrom.String() != "" {
helmRelease.Spec.ValuesFrom = []helmv2.ValuesReference{{
Kind: hrValuesFrom.Kind,
Name: hrValuesFrom.Name,
Kind: helmReleaseArgs.valuesFrom.Kind,
Name: helmReleaseArgs.valuesFrom.Name,
}}
}
if export {
if createArgs.export {
return exportHelmRelease(helmRelease)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
@@ -213,7 +237,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return err
}

View File

@@ -18,16 +18,21 @@ package main
import (
"fmt"
"regexp/syntax"
"strings"
"unicode"
"unicode/utf8"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/pkg/apis/meta"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
)
var createImagePolicyCmd = &cobra.Command{
Use: "policy <name>",
Use: "policy [name]",
Short: "Create or update an ImagePolicy object",
Long: `The create image policy command generates an ImagePolicy resource.
An ImagePolicy object calculates a "latest image" given an image
@@ -35,12 +40,28 @@ repository and a policy, e.g., semver.
The image that sorts highest according to the policy is recorded in
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}
type imagePolicyFlags struct {
imageRef string
semver string
filterRegex string
imageRef string
semver string
alpha string
numeric string
filterRegex string
filterExtract string
filterNumerical string
}
var imagePolicyArgs = imagePolicyFlags{}
@@ -48,8 +69,11 @@ var imagePolicyArgs = imagePolicyFlags{}
func init() {
flags := createImagePolicyCmd.Flags()
flags.StringVar(&imagePolicyArgs.imageRef, "image-ref", "", "the name of an image repository object")
flags.StringVar(&imagePolicyArgs.semver, "semver", "", "a semver range to apply to tags; e.g., '1.x'")
flags.StringVar(&imagePolicyArgs.filterRegex, "filter-regex", "", " regular expression pattern used to filter the image tags")
flags.StringVar(&imagePolicyArgs.semver, "select-semver", "", "a semver range to apply to tags; e.g., '1.x'")
flags.StringVar(&imagePolicyArgs.alpha, "select-alpha", "", "use alphabetical sorting to select image; either \"asc\" meaning select the last, or \"desc\" meaning select the first")
flags.StringVar(&imagePolicyArgs.numeric, "select-numeric", "", "use numeric sorting to select image; either \"asc\" meaning select the last, or \"desc\" meaning select the first")
flags.StringVar(&imagePolicyArgs.filterRegex, "filter-regex", "", "regular expression pattern used to filter the image tags")
flags.StringVar(&imagePolicyArgs.filterExtract, "filter-extract", "", "replacement pattern (using capture groups from --filter-regex) to use for sorting")
createImageCmd.AddCommand(createImagePolicyCmd)
}
@@ -78,32 +102,63 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
var policy = imagev1.ImagePolicy{
ObjectMeta: metav1.ObjectMeta{
Name: objectName,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: labels,
},
Spec: imagev1.ImagePolicySpec{
ImageRepositoryRef: corev1.LocalObjectReference{
ImageRepositoryRef: meta.LocalObjectReference{
Name: imagePolicyArgs.imageRef,
},
},
}
switch {
case imagePolicyArgs.semver != "" && imagePolicyArgs.alpha != "":
case imagePolicyArgs.semver != "" && imagePolicyArgs.numeric != "":
case imagePolicyArgs.alpha != "" && imagePolicyArgs.numeric != "":
return fmt.Errorf("only one of --select-semver, --select-alpha or --select-numeric can be specified")
case imagePolicyArgs.semver != "":
policy.Spec.Policy.SemVer = &imagev1.SemVerPolicy{
Range: imagePolicyArgs.semver,
}
case imagePolicyArgs.alpha != "":
if imagePolicyArgs.alpha != "desc" && imagePolicyArgs.alpha != "asc" {
return fmt.Errorf("--select-alpha must be one of [\"asc\", \"desc\"]")
}
policy.Spec.Policy.Alphabetical = &imagev1.AlphabeticalPolicy{
Order: imagePolicyArgs.alpha,
}
case imagePolicyArgs.numeric != "":
if imagePolicyArgs.numeric != "desc" && imagePolicyArgs.numeric != "asc" {
return fmt.Errorf("--select-numeric must be one of [\"asc\", \"desc\"]")
}
policy.Spec.Policy.Numerical = &imagev1.NumericalPolicy{
Order: imagePolicyArgs.numeric,
}
default:
return fmt.Errorf("a policy must be provided with --semver")
return fmt.Errorf("a policy must be provided with either --select-semver or --select-alpha")
}
if imagePolicyArgs.filterRegex != "" {
exp, err := syntax.Parse(imagePolicyArgs.filterRegex, syntax.Perl)
if err != nil {
return fmt.Errorf("--filter-regex is an invalid regex pattern")
}
policy.Spec.FilterTags = &imagev1.TagFilter{
Pattern: imagePolicyArgs.filterRegex,
}
if imagePolicyArgs.filterExtract != "" {
if err := validateExtractStr(imagePolicyArgs.filterExtract, exp.CapNames()); err != nil {
return err
}
policy.Spec.FilterTags.Extract = imagePolicyArgs.filterExtract
}
} else if imagePolicyArgs.filterExtract != "" {
return fmt.Errorf("cannot specify --filter-extract without specifying --filter-regex")
}
if export {
if createArgs.export {
return printExport(exportImagePolicy(&policy))
}
@@ -116,3 +171,94 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
})
return err
}
// Performs a dry-run of the extract function in Regexp to validate the template
func validateExtractStr(template string, capNames []string) error {
for len(template) > 0 {
i := strings.Index(template, "$")
if i < 0 {
return nil
}
template = template[i:]
if len(template) > 1 && template[1] == '$' {
template = template[2:]
continue
}
name, num, rest, ok := extract(template)
if !ok {
// Malformed extract string, assume user didn't want this
template = template[1:]
return fmt.Errorf("--filter-extract is malformed")
}
template = rest
if num >= 0 {
// we won't worry about numbers as we can't validate these
continue
} else {
found := false
for _, capName := range capNames {
if name == capName {
found = true
}
}
if !found {
return fmt.Errorf("capture group $%s used in --filter-extract not found in --filter-regex", name)
}
}
}
return nil
}
// extract method from the regexp package
// returns the name or number of the value prepended by $
func extract(str string) (name string, num int, rest string, ok bool) {
if len(str) < 2 || str[0] != '$' {
return
}
brace := false
if str[1] == '{' {
brace = true
str = str[2:]
} else {
str = str[1:]
}
i := 0
for i < len(str) {
rune, size := utf8.DecodeRuneInString(str[i:])
if !unicode.IsLetter(rune) && !unicode.IsDigit(rune) && rune != '_' {
break
}
i += size
}
if i == 0 {
// empty name is not okay
return
}
name = str[:i]
if brace {
if i >= len(str) || str[i] != '}' {
// missing closing brace
return
}
i++
}
// Parse number.
num = 0
for i := 0; i < len(name); i++ {
if name[i] < '0' || '9' < name[i] || num >= 1e8 {
num = -1
break
}
num = num*10 + int(name[i]) - '0'
}
// Disallow leading zeros.
if name[0] == '0' && len(name) > 1 {
num = -1
}
rest = str[i:]
ok = true
return
}

View File

@@ -22,24 +22,51 @@ import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/pkg/apis/meta"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
)
var createImageRepositoryCmd = &cobra.Command{
Use: "repository <name>",
Use: "repository [name]",
Short: "Create or update an ImageRepository object",
Long: `The create image repository command generates an ImageRepository resource.
An ImageRepository object specifies an image repository to scan.`,
Example: ` # Create an ImageRepository object to scan the alpine image repository:
flux create image repository alpine-repo --image alpine --interval 20m
# Create an image repository that uses an image pull secret (assumed to
# have been created already):
flux create image repository myapp-repo \
--secret-ref image-pull \
--image ghcr.io/example.com/myapp --interval 5m
# Create a TLS secret for a local image registry using a self-signed
# host certificate, and use it to scan an image. ca.pem is a file
# containing the CA certificate used to sign the host certificate.
flux create secret tls local-registry-cert --ca-file ./ca.pem
flux create image repository app-repo \
--cert-secret-ref local-registry-cert \
--image local-registry:5000/app --interval 5m
# Create a TLS secret with a client certificate and key, and use it
# to scan a private image registry.
flux create secret tls client-cert \
--cert-file client.crt --key-file client.key
flux create image repository app-repo \
--cert-secret-ref client-cert \
--image registry.example.com/private/app --interval 5m
`,
RunE: createImageRepositoryRun,
}
type imageRepoFlags struct {
image string
secretRef string
timeout time.Duration
image string
secretRef string
certSecretRef string
timeout time.Duration
}
var imageRepoArgs = imageRepoFlags{}
@@ -48,6 +75,7 @@ func init() {
flags := createImageRepositoryCmd.Flags()
flags.StringVar(&imageRepoArgs.image, "image", "", "the image repository to scan; e.g., library/alpine")
flags.StringVar(&imageRepoArgs.secretRef, "secret-ref", "", "the name of a docker-registry secret to use for credentials")
flags.StringVar(&imageRepoArgs.certSecretRef, "cert-ref", "", "the name of a secret to use for TLS certificates")
// NB there is already a --timeout in the global flags, for
// controlling timeout on operations while e.g., creating objects.
flags.DurationVar(&imageRepoArgs.timeout, "scan-timeout", 0, "a timeout for scanning; this defaults to the interval if not set")
@@ -77,24 +105,29 @@ func createImageRepositoryRun(cmd *cobra.Command, args []string) error {
var repo = imagev1.ImageRepository{
ObjectMeta: metav1.ObjectMeta{
Name: objectName,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: labels,
},
Spec: imagev1.ImageRepositorySpec{
Image: imageRepoArgs.image,
Interval: metav1.Duration{Duration: interval},
Interval: metav1.Duration{Duration: createArgs.interval},
},
}
if imageRepoArgs.timeout != 0 {
repo.Spec.Timeout = &metav1.Duration{Duration: imageRepoArgs.timeout}
}
if imageRepoArgs.secretRef != "" {
repo.Spec.SecretRef = &corev1.LocalObjectReference{
repo.Spec.SecretRef = &meta.LocalObjectReference{
Name: imageRepoArgs.secretRef,
}
}
if imageRepoArgs.certSecretRef != "" {
repo.Spec.CertSecretRef = &meta.LocalObjectReference{
Name: imageRepoArgs.certSecretRef,
}
}
if export {
if createArgs.export {
return printExport(exportImageRepository(&repo))
}

View File

@@ -20,26 +20,46 @@ import (
"fmt"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/pkg/apis/meta"
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
)
var createImageUpdateCmd = &cobra.Command{
Use: "update <name>",
Use: "update [name]",
Short: "Create or update an ImageUpdateAutomation object",
Long: `The create image update command generates an ImageUpdateAutomation resource.
An ImageUpdateAutomation object specifies an automated update to images
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,
}
type imageUpdateFlags struct {
// git checkout spec
gitRepoRef string
branch string
// commit spec
gitRepoRef string
gitRepoPath string
checkoutBranch string
pushBranch string
commitTemplate string
authorName string
authorEmail string
@@ -49,8 +69,10 @@ var imageUpdateArgs = imageUpdateFlags{}
func init() {
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.branch, "branch", "", "the branch to checkout and push commits to")
flags.StringVar(&imageUpdateArgs.gitRepoRef, "git-repo-ref", "", "the name of a GitRepository resource with details of the upstream Git repository")
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.authorName, "author-name", "", "the name to use for commit author")
flags.StringVar(&imageUpdateArgs.authorEmail, "author-email", "", "the email to use for commit author")
@@ -68,8 +90,16 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("a reference to a GitRepository is required (--git-repo-ref)")
}
if imageUpdateArgs.branch == "" {
return fmt.Errorf("the Git repoistory branch is required (--branch)")
if imageUpdateArgs.checkoutBranch == "" {
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()
@@ -80,19 +110,18 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
var update = autov1.ImageUpdateAutomation{
ObjectMeta: metav1.ObjectMeta{
Name: objectName,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: labels,
},
Spec: autov1.ImageUpdateAutomationSpec{
Checkout: autov1.GitCheckoutSpec{
GitRepositoryRef: corev1.LocalObjectReference{
GitRepositoryRef: meta.LocalObjectReference{
Name: imageUpdateArgs.gitRepoRef,
},
Branch: imageUpdateArgs.branch,
Branch: imageUpdateArgs.checkoutBranch,
},
Interval: metav1.Duration{Duration: interval},
Update: autov1.UpdateStrategy{
Setters: &autov1.SettersStrategy{},
Interval: metav1.Duration{
Duration: createArgs.interval,
},
Commit: autov1.CommitSpec{
AuthorName: imageUpdateArgs.authorName,
@@ -102,7 +131,20 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
},
}
if export {
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 {
return printExport(exportImageUpdate(&update))
}

View File

@@ -19,11 +19,11 @@ package main
import (
"context"
"fmt"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -31,11 +31,12 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
)
var createKsCmd = &cobra.Command{
@@ -72,53 +73,61 @@ var createKsCmd = &cobra.Command{
RunE: createKsCmdRun,
}
var (
ksSource flags.KustomizationSource
ksPath flags.SafeRelativePath = "./"
ksPrune bool
ksDependsOn []string
ksValidation string
ksHealthCheck []string
ksHealthTimeout time.Duration
ksSAName string
ksDecryptionProvider flags.DecryptionProvider
ksDecryptionSecret string
ksTargetNamespace string
)
type kustomizationFlags struct {
source flags.KustomizationSource
path flags.SafeRelativePath
prune bool
dependsOn []string
validation string
healthCheck []string
healthTimeout time.Duration
saName string
decryptionProvider flags.DecryptionProvider
decryptionSecret string
targetNamespace string
}
var kustomizationArgs = NewKustomizationFlags()
func init() {
createKsCmd.Flags().Var(&ksSource, "source", ksSource.Description())
createKsCmd.Flags().Var(&ksPath, "path", "path to the directory containing a kustomization.yaml file")
createKsCmd.Flags().BoolVar(&ksPrune, "prune", false, "enable garbage collection")
createKsCmd.Flags().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
createKsCmd.Flags().DurationVar(&ksHealthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
createKsCmd.Flags().StringVar(&ksValidation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'")
createKsCmd.Flags().StringVar(&ksSAName, "service-account", "", "the name of the service account to impersonate when reconciling this Kustomization")
createKsCmd.Flags().Var(&ksDecryptionProvider, "decryption-provider", ksDecryptionProvider.Description())
createKsCmd.Flags().StringVar(&ksDecryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
createKsCmd.Flags().StringVar(&ksTargetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
createKsCmd.Flags().Var(&kustomizationArgs.source, "source", kustomizationArgs.source.Description())
createKsCmd.Flags().Var(&kustomizationArgs.path, "path", "path to the directory containing a kustomization.yaml file")
createKsCmd.Flags().BoolVar(&kustomizationArgs.prune, "prune", false, "enable garbage collection")
createKsCmd.Flags().StringArrayVar(&kustomizationArgs.healthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
createKsCmd.Flags().DurationVar(&kustomizationArgs.healthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
createKsCmd.Flags().StringVar(&kustomizationArgs.validation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
createKsCmd.Flags().StringArrayVar(&kustomizationArgs.dependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'")
createKsCmd.Flags().StringVar(&kustomizationArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this Kustomization")
createKsCmd.Flags().Var(&kustomizationArgs.decryptionProvider, "decryption-provider", kustomizationArgs.decryptionProvider.Description())
createKsCmd.Flags().StringVar(&kustomizationArgs.decryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
createKsCmd.Flags().StringVar(&kustomizationArgs.targetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
createCmd.AddCommand(createKsCmd)
}
func NewKustomizationFlags() kustomizationFlags {
return kustomizationFlags{
path: "./",
}
}
func createKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Kustomization name is required")
}
name := args[0]
if ksPath == "" {
if kustomizationArgs.path == "" {
return fmt.Errorf("path is required")
}
if !strings.HasPrefix(ksPath.String(), "./") {
if !strings.HasPrefix(kustomizationArgs.path.String(), "./") {
return fmt.Errorf("path must begin with ./")
}
if !export {
if !createArgs.export {
logger.Generatef("generating Kustomization")
}
ksLabels, err := parseLabels()
kslabels, err := parseLabels()
if err != nil {
return err
}
@@ -126,29 +135,29 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
kustomization := kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: ksLabels,
Namespace: rootArgs.namespace,
Labels: kslabels,
},
Spec: kustomizev1.KustomizationSpec{
DependsOn: utils.MakeDependsOn(ksDependsOn),
DependsOn: utils.MakeDependsOn(kustomizationArgs.dependsOn),
Interval: metav1.Duration{
Duration: interval,
Duration: createArgs.interval,
},
Path: ksPath.String(),
Prune: ksPrune,
Path: filepath.ToSlash(kustomizationArgs.path.String()),
Prune: kustomizationArgs.prune,
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: ksSource.Kind,
Name: ksSource.Name,
Kind: kustomizationArgs.source.Kind,
Name: kustomizationArgs.source.Name,
},
Suspend: false,
Validation: ksValidation,
TargetNamespace: ksTargetNamespace,
Validation: kustomizationArgs.validation,
TargetNamespace: kustomizationArgs.targetNamespace,
},
}
if len(ksHealthCheck) > 0 {
healthChecks := make([]kustomizev1.CrossNamespaceObjectReference, 0)
for _, w := range ksHealthCheck {
if len(kustomizationArgs.healthCheck) > 0 {
healthChecks := make([]meta.NamespacedObjectKindReference, 0)
for _, w := range kustomizationArgs.healthCheck {
kindObj := strings.Split(w, "/")
if len(kindObj) != 2 {
return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace' %v", w, kindObj)
@@ -170,7 +179,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace'", w)
}
check := kustomizev1.CrossNamespaceObjectReference{
check := meta.NamespacedObjectKindReference{
Kind: kind,
Name: nameNs[0],
Namespace: nameNs[1],
@@ -183,32 +192,32 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}
kustomization.Spec.HealthChecks = healthChecks
kustomization.Spec.Timeout = &metav1.Duration{
Duration: ksHealthTimeout,
Duration: kustomizationArgs.healthTimeout,
}
}
if ksSAName != "" {
kustomization.Spec.ServiceAccountName = ksSAName
if kustomizationArgs.saName != "" {
kustomization.Spec.ServiceAccountName = kustomizationArgs.saName
}
if ksDecryptionProvider != "" {
if kustomizationArgs.decryptionProvider != "" {
kustomization.Spec.Decryption = &kustomizev1.Decryption{
Provider: ksDecryptionProvider.String(),
Provider: kustomizationArgs.decryptionProvider.String(),
}
if ksDecryptionSecret != "" {
kustomization.Spec.Decryption.SecretRef = &corev1.LocalObjectReference{Name: ksDecryptionSecret}
if kustomizationArgs.decryptionSecret != "" {
kustomization.Spec.Decryption.SecretRef = &meta.LocalObjectReference{Name: kustomizationArgs.decryptionSecret}
}
}
if export {
if createArgs.export {
return exportKs(kustomization)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
@@ -220,7 +229,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err
}

View File

@@ -21,7 +21,6 @@ import (
"fmt"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -29,9 +28,10 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/internal/utils"
)
var createReceiverCmd = &cobra.Command{
@@ -50,18 +50,20 @@ var createReceiverCmd = &cobra.Command{
RunE: createReceiverCmdRun,
}
var (
rcvType string
rcvSecretRef string
rcvEvents []string
rcvResources []string
)
type receiverFlags struct {
receiverType string
secretRef string
events []string
resources []string
}
var receiverArgs receiverFlags
func init() {
createReceiverCmd.Flags().StringVar(&rcvType, "type", "", "")
createReceiverCmd.Flags().StringVar(&rcvSecretRef, "secret-ref", "", "")
createReceiverCmd.Flags().StringArrayVar(&rcvEvents, "event", []string{}, "")
createReceiverCmd.Flags().StringArrayVar(&rcvResources, "resource", []string{}, "")
createReceiverCmd.Flags().StringVar(&receiverArgs.receiverType, "type", "", "")
createReceiverCmd.Flags().StringVar(&receiverArgs.secretRef, "secret-ref", "", "")
createReceiverCmd.Flags().StringArrayVar(&receiverArgs.events, "event", []string{}, "")
createReceiverCmd.Flags().StringArrayVar(&receiverArgs.resources, "resource", []string{}, "")
createCmd.AddCommand(createReceiverCmd)
}
@@ -71,16 +73,16 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
if rcvType == "" {
if receiverArgs.receiverType == "" {
return fmt.Errorf("Receiver type is required")
}
if rcvSecretRef == "" {
if receiverArgs.secretRef == "" {
return fmt.Errorf("secret ref is required")
}
resources := []notificationv1.CrossNamespaceObjectReference{}
for _, resource := range rcvResources {
for _, resource := range receiverArgs.resources {
kind, name := utils.ParseObjectKindName(resource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", resource)
@@ -101,35 +103,35 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if !export {
if !createArgs.export {
logger.Generatef("generating Receiver")
}
receiver := notificationv1.Receiver{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
Spec: notificationv1.ReceiverSpec{
Type: rcvType,
Events: rcvEvents,
Type: receiverArgs.receiverType,
Events: receiverArgs.events,
Resources: resources,
SecretRef: corev1.LocalObjectReference{
Name: rcvSecretRef,
SecretRef: meta.LocalObjectReference{
Name: receiverArgs.secretRef,
},
Suspend: false,
},
}
if export {
if createArgs.export {
return exportReceiver(receiver)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
@@ -141,7 +143,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}

View File

@@ -18,15 +18,12 @@ package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
)
var createSecretCmd = &cobra.Command{
@@ -64,19 +61,3 @@ func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.S
}
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

@@ -21,15 +21,14 @@ import (
"crypto/elliptic"
"fmt"
"net/url"
"time"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/ssh"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
)
var createSecretGitCmd = &cobra.Command{
@@ -71,136 +70,109 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
RunE: createSecretGitCmdRun,
}
var (
secretGitURL string
secretGitUsername string
secretGitPassword string
secretGitKeyAlgorithm flags.PublicKeyAlgorithm = "rsa"
secretGitRSABits flags.RSAKeyBits = 2048
secretGitECDSACurve = flags.ECDSACurve{Curve: elliptic.P384()}
)
type secretGitFlags struct {
url string
username string
password string
keyAlgorithm flags.PublicKeyAlgorithm
rsaBits flags.RSAKeyBits
ecdsaCurve flags.ECDSACurve
caFile string
}
var secretGitArgs = NewSecretGitFlags()
func init() {
createSecretGitCmd.Flags().StringVar(&secretGitURL, "url", "", "git address, e.g. ssh://git@host/org/repository")
createSecretGitCmd.Flags().StringVarP(&secretGitUsername, "username", "u", "", "basic authentication username")
createSecretGitCmd.Flags().StringVarP(&secretGitPassword, "password", "p", "", "basic authentication password")
createSecretGitCmd.Flags().Var(&secretGitKeyAlgorithm, "ssh-key-algorithm", secretGitKeyAlgorithm.Description())
createSecretGitCmd.Flags().Var(&secretGitRSABits, "ssh-rsa-bits", secretGitRSABits.Description())
createSecretGitCmd.Flags().Var(&secretGitECDSACurve, "ssh-ecdsa-curve", secretGitECDSACurve.Description())
createSecretGitCmd.Flags().StringVar(&secretGitArgs.url, "url", "", "git address, e.g. ssh://git@host/org/repository")
createSecretGitCmd.Flags().StringVarP(&secretGitArgs.username, "username", "u", "", "basic authentication username")
createSecretGitCmd.Flags().StringVarP(&secretGitArgs.password, "password", "p", "", "basic authentication password")
createSecretGitCmd.Flags().Var(&secretGitArgs.keyAlgorithm, "ssh-key-algorithm", secretGitArgs.keyAlgorithm.Description())
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
createSecretCmd.AddCommand(createSecretGitCmd)
}
func NewSecretGitFlags() secretGitFlags {
return secretGitFlags{
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm),
rsaBits: 2048,
ecdsaCurve: flags.ECDSACurve{Curve: elliptic.P384()},
}
}
func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("secret name is required")
}
name := args[0]
if secretGitURL == "" {
if secretGitArgs.url == "" {
return fmt.Errorf("url is required")
}
u, err := url.Parse(secretGitURL)
u, err := url.Parse(secretGitArgs.url)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
secretLabels, err := parseLabels()
labels, err := parseLabels()
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: secretLabels,
},
opts := sourcesecret.Options{
Name: name,
Namespace: rootArgs.namespace,
Labels: labels,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
}
switch u.Scheme {
case "ssh":
pair, err := generateKeyPair(ctx, secretGitKeyAlgorithm, secretGitRSABits, secretGitECDSACurve)
if err != nil {
return err
}
hostKey, err := scanHostKey(ctx, u)
if err != nil {
return err
}
secret.Data = map[string][]byte{
"identity": pair.PrivateKey,
"identity.pub": pair.PublicKey,
"known_hosts": hostKey,
}
if !export {
logger.Generatef("deploy key: %s", string(pair.PublicKey))
}
opts.SSHHostname = u.Host
opts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(secretGitArgs.keyAlgorithm)
opts.RSAKeyBits = int(secretGitArgs.rsaBits)
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
case "http", "https":
if secretGitUsername == "" || secretGitPassword == "" {
if secretGitArgs.username == "" || secretGitArgs.password == "" {
return fmt.Errorf("for Git over HTTP/S the username and password are required")
}
// TODO: add cert data when it's implemented in source-controller
secret.Data = map[string][]byte{
"username": []byte(secretGitUsername),
"password": []byte(secretGitPassword),
}
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)
}
if export {
return exportSecret(secret)
}
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
secret, err := sourcesecret.Generate(opts)
if err != nil {
return err
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
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
}
logger.Actionf("secret '%s' created in '%s' namespace", name, namespace)
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
}
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
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

@@ -19,13 +19,13 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
)
var createSecretHelmCmd = &cobra.Command{
@@ -33,8 +33,8 @@ var createSecretHelmCmd = &cobra.Command{
Short: "Create or update a Kubernetes secret for Helm repository authentication",
Long: `
The create secret helm command generates a Kubernetes secret with basic authentication credentials.`,
Example: ` # Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
Example: `
# Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
flux create secret helm repo-auth \
--namespace=my-namespace \
--username=my-username \
@@ -55,21 +55,18 @@ The create secret helm command generates a Kubernetes secret with basic authenti
RunE: createSecretHelmCmdRun,
}
var (
secretHelmUsername string
secretHelmPassword string
secretHelmCertFile string
secretHelmKeyFile string
secretHelmCAFile string
)
type secretHelmFlags struct {
username string
password string
secretTLSFlags
}
var secretHelmArgs secretHelmFlags
func init() {
createSecretHelmCmd.Flags().StringVarP(&secretHelmUsername, "username", "u", "", "basic authentication username")
createSecretHelmCmd.Flags().StringVarP(&secretHelmPassword, "password", "p", "", "basic authentication password")
createSecretHelmCmd.Flags().StringVar(&secretHelmCertFile, "cert-file", "", "TLS authentication cert file path")
createSecretHelmCmd.Flags().StringVar(&secretHelmKeyFile, "key-file", "", "TLS authentication key file path")
createSecretHelmCmd.Flags().StringVar(&secretHelmCAFile, "ca-file", "", "TLS authentication CA file path")
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.username, "username", "u", "", "basic authentication username")
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.password, "password", "p", "", "basic authentication password")
initSecretTLSFlags(createSecretHelmCmd.Flags(), &secretHelmArgs.secretTLSFlags)
createSecretCmd.AddCommand(createSecretHelmCmd)
}
@@ -79,63 +76,44 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
secretLabels, err := parseLabels()
labels, err := parseLabels()
if err != nil {
return err
}
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: secretLabels,
},
StringData: map[string]string{},
opts := sourcesecret.Options{
Name: name,
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 != nil {
return err
}
if secretHelmUsername != "" && secretHelmPassword != "" {
secret.StringData["username"] = secretHelmUsername
secret.StringData["password"] = secretHelmPassword
if createArgs.export {
fmt.Println(secret.Content)
return nil
}
if secretHelmCertFile != "" && secretHelmKeyFile != "" {
cert, err := ioutil.ReadFile(secretHelmCertFile)
if err != nil {
return fmt.Errorf("failed to read repository cert file '%s': %w", secretHelmCertFile, err)
}
secret.StringData["certFile"] = string(cert)
key, err := ioutil.ReadFile(secretHelmKeyFile)
if err != nil {
return fmt.Errorf("failed to read repository key file '%s': %w", secretHelmKeyFile, err)
}
secret.StringData["keyFile"] = string(key)
}
if secretHelmCAFile != "" {
ca, err := ioutil.ReadFile(secretHelmCAFile)
if err != nil {
return fmt.Errorf("failed to read repository CA file '%s': %w", secretHelmCAFile, err)
}
secret.StringData["caFile"] = string(ca)
}
if export {
return exportSecret(secret)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
var s corev1.Secret
if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err
}
logger.Actionf("secret '%s' created in '%s' namespace", name, namespace)
return nil
}

View File

@@ -0,0 +1,116 @@
/*
Copyright 2020, 2021 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
)
var createSecretTLSCmd = &cobra.Command{
Use: "tls [name]",
Short: "Create or update a Kubernetes secret with TLS certificates",
Long: `
The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`,
Example: `
# Create a TLS secret on disk and encrypt it with Mozilla SOPS.
# Files are expected to be PEM-encoded.
flux create secret tls certs \
--namespace=my-namespace \
--cert-file=./client.crt \
--key-file=./client.key \
--export > certs.yaml
sops --encrypt --encrypted-regex '^(data|stringData)$' \
--in-place certs.yaml
`,
RunE: createSecretTLSCmdRun,
}
type secretTLSFlags struct {
certFile string
keyFile string
caFile string
}
var secretTLSArgs secretTLSFlags
func initSecretTLSFlags(flags *pflag.FlagSet, args *secretTLSFlags) {
flags.StringVar(&args.certFile, "cert-file", "", "TLS authentication cert file path")
flags.StringVar(&args.keyFile, "key-file", "", "TLS authentication key file path")
flags.StringVar(&args.caFile, "ca-file", "", "TLS authentication CA file path")
}
func init() {
flags := createSecretTLSCmd.Flags()
initSecretTLSFlags(flags, &secretTLSArgs)
createSecretCmd.AddCommand(createSecretTLSCmd)
}
func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("secret name is required")
}
name := args[0]
labels, err := parseLabels()
if err != nil {
return err
}
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
}
if createArgs.export {
fmt.Println(secret.Content)
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
var s corev1.Secret
if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err
}
return nil
}

View File

@@ -30,9 +30,11 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"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/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var createSourceBucketCmd = &cobra.Command{
@@ -61,41 +63,49 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
RunE: createSourceBucketCmdRun,
}
var (
sourceBucketName string
sourceBucketProvider = flags.SourceBucketProvider(sourcev1.GenericBucketProvider)
sourceBucketEndpoint string
sourceBucketAccessKey string
sourceBucketSecretKey string
sourceBucketRegion string
sourceBucketInsecure bool
sourceBucketSecretRef string
)
type sourceBucketFlags struct {
name string
provider flags.SourceBucketProvider
endpoint string
accessKey string
secretKey string
region string
insecure bool
secretRef string
}
var sourceBucketArgs = NewSourceBucketFlags()
func init() {
createSourceBucketCmd.Flags().Var(&sourceBucketProvider, "provider", sourceBucketProvider.Description())
createSourceBucketCmd.Flags().StringVar(&sourceBucketName, "bucket-name", "", "the bucket name")
createSourceBucketCmd.Flags().StringVar(&sourceBucketEndpoint, "endpoint", "", "the bucket endpoint address")
createSourceBucketCmd.Flags().StringVar(&sourceBucketAccessKey, "access-key", "", "the bucket access key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretKey, "secret-key", "", "the bucket secret key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketRegion, "region", "", "the bucket region")
createSourceBucketCmd.Flags().BoolVar(&sourceBucketInsecure, "insecure", false, "for when connecting to a non-TLS S3 HTTP endpoint")
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretRef, "secret-ref", "", "the name of an existing secret containing credentials")
createSourceBucketCmd.Flags().Var(&sourceBucketArgs.provider, "provider", sourceBucketArgs.provider.Description())
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.name, "bucket-name", "", "the bucket name")
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.endpoint, "endpoint", "", "the bucket endpoint address")
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.accessKey, "access-key", "", "the bucket access key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.secretKey, "secret-key", "", "the bucket secret key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.region, "region", "", "the bucket region")
createSourceBucketCmd.Flags().BoolVar(&sourceBucketArgs.insecure, "insecure", false, "for when connecting to a non-TLS S3 HTTP endpoint")
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.secretRef, "secret-ref", "", "the name of an existing secret containing credentials")
createSourceCmd.AddCommand(createSourceBucketCmd)
}
func NewSourceBucketFlags() sourceBucketFlags {
return sourceBucketFlags{
provider: flags.SourceBucketProvider(sourcev1.GenericBucketProvider),
}
}
func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Bucket source name is required")
}
name := args[0]
if sourceBucketName == "" {
if sourceBucketArgs.name == "" {
return fmt.Errorf("bucket-name is required")
}
if sourceBucketEndpoint == "" {
if sourceBucketArgs.endpoint == "" {
return fmt.Errorf("endpoint is required")
}
@@ -113,55 +123,55 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
bucket := &sourcev1.Bucket{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
Spec: sourcev1.BucketSpec{
BucketName: sourceBucketName,
Provider: sourceBucketProvider.String(),
Insecure: sourceBucketInsecure,
Endpoint: sourceBucketEndpoint,
Region: sourceBucketRegion,
BucketName: sourceBucketArgs.name,
Provider: sourceBucketArgs.provider.String(),
Insecure: sourceBucketArgs.insecure,
Endpoint: sourceBucketArgs.endpoint,
Region: sourceBucketArgs.region,
Interval: metav1.Duration{
Duration: interval,
Duration: createArgs.interval,
},
},
}
if sourceHelmSecretRef != "" {
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceBucketSecretRef,
if sourceHelmArgs.secretRef != "" {
bucket.Spec.SecretRef = &meta.LocalObjectReference{
Name: sourceBucketArgs.secretRef,
}
}
if export {
if createArgs.export {
return exportBucket(*bucket)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
logger.Generatef("generating Bucket source")
if sourceBucketSecretRef == "" {
if sourceBucketArgs.secretRef == "" {
secretName := fmt.Sprintf("bucket-%s", name)
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
StringData: map[string]string{},
}
if sourceBucketAccessKey != "" && sourceBucketSecretKey != "" {
secret.StringData["accesskey"] = sourceBucketAccessKey
secret.StringData["secretkey"] = sourceBucketSecretKey
if sourceBucketArgs.accessKey != "" && sourceBucketArgs.secretKey != "" {
secret.StringData["accesskey"] = sourceBucketArgs.accessKey
secret.StringData["secretkey"] = sourceBucketArgs.secretKey
}
if len(secret.StringData) > 0 {
@@ -169,7 +179,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
bucket.Spec.SecretRef = &meta.LocalObjectReference{
Name: secretName,
}
logger.Successf("authentication configured")
@@ -183,7 +193,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
return err
}

View File

@@ -24,6 +24,8 @@ import (
"net/url"
"os"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
@@ -33,13 +35,28 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
)
type sourceGitFlags struct {
url string
branch string
tag string
semver string
username string
password string
caFile string
keyAlgorithm flags.PublicKeyAlgorithm
keyRSABits flags.RSAKeyBits
keyECDSACurve flags.ECDSACurve
secretRef string
gitImplementation flags.GitImplementation
}
var createSourceGitCmd = &cobra.Command{
Use: "git [name]",
Short: "Create or update a GitRepository source",
@@ -84,57 +101,60 @@ For private Git repositories, the basic authentication credentials are stored in
RunE: createSourceGitCmdRun,
}
var (
sourceGitURL string
sourceGitBranch string
sourceGitTag string
sourceGitSemver string
sourceGitUsername string
sourceGitPassword string
sourceGitKeyAlgorithm flags.PublicKeyAlgorithm = "rsa"
sourceGitRSABits flags.RSAKeyBits = 2048
sourceGitECDSACurve = flags.ECDSACurve{Curve: elliptic.P384()}
sourceGitSecretRef string
sourceGitImplementation flags.GitImplementation
)
var sourceGitArgs = newSourceGitFlags()
func init() {
createSourceGitCmd.Flags().StringVar(&sourceGitURL, "url", "", "git address, e.g. ssh://git@host/org/repository")
createSourceGitCmd.Flags().StringVar(&sourceGitBranch, "branch", "master", "git branch")
createSourceGitCmd.Flags().StringVar(&sourceGitTag, "tag", "", "git tag")
createSourceGitCmd.Flags().StringVar(&sourceGitSemver, "tag-semver", "", "git tag semver range")
createSourceGitCmd.Flags().StringVarP(&sourceGitUsername, "username", "u", "", "basic authentication username")
createSourceGitCmd.Flags().StringVarP(&sourceGitPassword, "password", "p", "", "basic authentication password")
createSourceGitCmd.Flags().Var(&sourceGitKeyAlgorithm, "ssh-key-algorithm", sourceGitKeyAlgorithm.Description())
createSourceGitCmd.Flags().Var(&sourceGitRSABits, "ssh-rsa-bits", sourceGitRSABits.Description())
createSourceGitCmd.Flags().Var(&sourceGitECDSACurve, "ssh-ecdsa-curve", sourceGitECDSACurve.Description())
createSourceGitCmd.Flags().StringVarP(&sourceGitSecretRef, "secret-ref", "", "", "the name of an existing secret containing SSH or basic credentials")
createSourceGitCmd.Flags().Var(&sourceGitImplementation, "git-implementation", sourceGitImplementation.Description())
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.url, "url", "", "git address, e.g. ssh://git@host/org/repository")
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.branch, "branch", "master", "git branch")
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.tag, "tag", "", "git tag")
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.semver, "tag-semver", "", "git tag semver range")
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.username, "username", "u", "", "basic authentication username")
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.password, "password", "p", "", "basic authentication password")
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyAlgorithm, "ssh-key-algorithm", sourceGitArgs.keyAlgorithm.Description())
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyRSABits, "ssh-rsa-bits", sourceGitArgs.keyRSABits.Description())
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyECDSACurve, "ssh-ecdsa-curve", sourceGitArgs.keyECDSACurve.Description())
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials")
createSourceGitCmd.Flags().Var(&sourceGitArgs.gitImplementation, "git-implementation", sourceGitArgs.gitImplementation.Description())
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates, requires libgit2")
createSourceCmd.AddCommand(createSourceGitCmd)
}
func newSourceGitFlags() sourceGitFlags {
return sourceGitFlags{
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm),
keyRSABits: 2048,
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
}
}
func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("GitRepository source name is required")
}
name := args[0]
if sourceGitURL == "" {
if sourceGitArgs.url == "" {
return fmt.Errorf("url is required")
}
if sourceGitArgs.gitImplementation.String() != sourcev1.LibGit2Implementation && sourceGitArgs.caFile != "" {
return fmt.Errorf("specifing a CA file requires --git-implementation=%s", sourcev1.LibGit2Implementation)
}
tmpDir, err := ioutil.TempDir("", name)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
u, err := url.Parse(sourceGitURL)
u, err := url.Parse(sourceGitArgs.url)
if err != nil {
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()
if err != nil {
@@ -144,123 +164,96 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
gitRepository := sourcev1.GitRepository{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
Spec: sourcev1.GitRepositorySpec{
URL: sourceGitURL,
URL: sourceGitArgs.url,
Interval: metav1.Duration{
Duration: interval,
Duration: createArgs.interval,
},
Reference: &sourcev1.GitRepositoryRef{},
},
}
if sourceGitImplementation != "" {
gitRepository.Spec.GitImplementation = sourceGitImplementation.String()
if sourceGitArgs.gitImplementation != "" {
gitRepository.Spec.GitImplementation = sourceGitArgs.gitImplementation.String()
}
if sourceGitSemver != "" {
gitRepository.Spec.Reference.SemVer = sourceGitSemver
} else if sourceGitTag != "" {
gitRepository.Spec.Reference.Tag = sourceGitTag
if sourceGitArgs.semver != "" {
gitRepository.Spec.Reference.SemVer = sourceGitArgs.semver
} else if sourceGitArgs.tag != "" {
gitRepository.Spec.Reference.Tag = sourceGitArgs.tag
} else {
gitRepository.Spec.Reference.Branch = sourceGitBranch
gitRepository.Spec.Reference.Branch = sourceGitArgs.branch
}
if export {
if sourceGitSecretRef != "" {
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceGitSecretRef,
}
if sourceGitArgs.secretRef != "" {
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
Name: sourceGitArgs.secretRef,
}
}
if createArgs.export {
return exportGit(gitRepository)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
withAuth := false
// TODO(hidde): move all auth prep to separate func?
if sourceGitSecretRef != "" {
withAuth = true
} else if u.Scheme == "ssh" {
logger.Generatef("generating deploy key pair")
pair, err := generateKeyPair(ctx, sourceGitKeyAlgorithm, sourceGitRSABits, sourceGitECDSACurve)
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: 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 sourceGitUsername != "" && sourceGitPassword != "" {
logger.Actionf("applying secret with basic auth credentials")
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: sourceLabels,
},
StringData: map[string]string{
"username": sourceGitUsername,
"password": sourceGitPassword,
},
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
withAuth = true
}
if withAuth {
logger.Successf("authentication configured")
}
logger.Generatef("generating GitRepository source")
if withAuth {
secretName := name
if sourceGitSecretRef != "" {
secretName = sourceGitSecretRef
if sourceGitArgs.secretRef == "" {
secretOpts := sourcesecret.Options{
Name: name,
Namespace: rootArgs.namespace,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
}
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
switch u.Scheme {
case "ssh":
secretOpts.SSHHostname = u.Host
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")
}
}
@@ -271,7 +264,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
return err
}

View File

@@ -32,9 +32,12 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
)
var createSourceHelmCmd = &cobra.Command{
@@ -64,24 +67,26 @@ For private Helm repositories, the basic authentication credentials are stored i
RunE: createSourceHelmCmdRun,
}
var (
sourceHelmURL string
sourceHelmUsername string
sourceHelmPassword string
sourceHelmCertFile string
sourceHelmKeyFile string
sourceHelmCAFile string
sourceHelmSecretRef string
)
type sourceHelmFlags struct {
url string
username string
password string
certFile string
keyFile string
caFile string
secretRef string
}
var sourceHelmArgs sourceHelmFlags
func init() {
createSourceHelmCmd.Flags().StringVar(&sourceHelmURL, "url", "", "Helm repository address")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmUsername, "username", "u", "", "basic authentication username")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmPassword, "password", "p", "", "basic authentication password")
createSourceHelmCmd.Flags().StringVar(&sourceHelmCertFile, "cert-file", "", "TLS authentication cert file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmKeyFile, "key-file", "", "TLS authentication key file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmCAFile, "ca-file", "", "TLS authentication CA file path")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmSecretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.url, "url", "", "Helm repository address")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.username, "username", "u", "", "basic authentication username")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.password, "password", "p", "", "basic authentication password")
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.certFile, "cert-file", "", "TLS authentication cert file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
createSourceCmd.AddCommand(createSourceHelmCmd)
}
@@ -92,7 +97,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
if sourceHelmURL == "" {
if sourceHelmArgs.url == "" {
return fmt.Errorf("url is required")
}
@@ -107,88 +112,69 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}
defer os.RemoveAll(tmpDir)
if _, err := url.Parse(sourceHelmURL); err != nil {
if _, err := url.Parse(sourceHelmArgs.url); err != nil {
return fmt.Errorf("url parse failed: %w", err)
}
helmRepository := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Namespace: rootArgs.namespace,
Labels: sourceLabels,
},
Spec: sourcev1.HelmRepositorySpec{
URL: sourceHelmURL,
URL: sourceHelmArgs.url,
Interval: metav1.Duration{
Duration: interval,
Duration: createArgs.interval,
},
},
}
if sourceHelmSecretRef != "" {
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceHelmSecretRef,
if sourceHelmArgs.secretRef != "" {
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
Name: sourceHelmArgs.secretRef,
}
}
if export {
if createArgs.export {
return exportHelmRepository(*helmRepository)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
logger.Generatef("generating HelmRepository source")
if sourceHelmSecretRef == "" {
if sourceHelmArgs.secretRef == "" {
secretName := fmt.Sprintf("helm-%s", name)
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
Labels: sourceLabels,
},
StringData: map[string]string{},
secretOpts := sourcesecret.Options{
Name: secretName,
Namespace: rootArgs.namespace,
Username: sourceHelmArgs.username,
Password: sourceHelmArgs.password,
CertFilePath: sourceHelmArgs.certFile,
KeyFilePath: sourceHelmArgs.keyFile,
CAFilePath: sourceHelmArgs.caFile,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
}
if sourceHelmUsername != "" && sourceHelmPassword != "" {
secret.StringData["username"] = sourceHelmUsername
secret.StringData["password"] = sourceHelmPassword
secret, err := sourcesecret.Generate(secretOpts)
if err != nil {
return err
}
if sourceHelmCertFile != "" && sourceHelmKeyFile != "" {
cert, err := ioutil.ReadFile(sourceHelmCertFile)
if err != nil {
return fmt.Errorf("failed to read repository cert file '%s': %w", sourceHelmCertFile, err)
}
secret.StringData["certFile"] = string(cert)
key, err := ioutil.ReadFile(sourceHelmKeyFile)
if err != nil {
return fmt.Errorf("failed to read repository key file '%s': %w", sourceHelmKeyFile, err)
}
secret.StringData["keyFile"] = string(key)
var s corev1.Secret
if err = yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
return err
}
if sourceHelmCAFile != "" {
ca, err := ioutil.ReadFile(sourceHelmCAFile)
if err != nil {
return fmt.Errorf("failed to read repository CA file '%s': %w", sourceHelmCAFile, err)
}
secret.StringData["caFile"] = string(ca)
}
if len(secret.StringData) > 0 {
if len(s.StringData) > 0 {
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
}
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
Name: secretName,
}
logger.Successf("authentication configured")
@@ -202,7 +188,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
return err
}

View File

@@ -58,14 +58,16 @@ const (
tenantLabel = "toolkit.fluxcd.io/tenant"
)
var (
tenantNamespaces []string
tenantClusterRole string
)
type tenantFlags struct {
namespaces []string
clusterRole string
}
var tenantArgs tenantFlags
func init() {
createTenantCmd.Flags().StringSliceVar(&tenantNamespaces, "with-namespace", nil, "namespace belonging to this tenant")
createTenantCmd.Flags().StringVar(&tenantClusterRole, "cluster-role", "cluster-admin", "cluster role of the tenant role binding")
createTenantCmd.Flags().StringSliceVar(&tenantArgs.namespaces, "with-namespace", nil, "namespace belonging to this tenant")
createTenantCmd.Flags().StringVar(&tenantArgs.clusterRole, "cluster-role", "cluster-admin", "cluster role of the tenant role binding")
createCmd.AddCommand(createTenantCmd)
}
@@ -78,11 +80,11 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid tenant name '%s': %v", tenant, err)
}
if tenantClusterRole == "" {
if tenantArgs.clusterRole == "" {
return fmt.Errorf("cluster-role is required")
}
if tenantNamespaces == nil {
if tenantArgs.namespaces == nil {
return fmt.Errorf("with-namespace is required")
}
@@ -90,7 +92,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
var accounts []corev1.ServiceAccount
var roleBindings []rbacv1.RoleBinding
for _, ns := range tenantNamespaces {
for _, ns := range tenantArgs.namespaces {
if err := validation.IsQualifiedName(ns); len(err) > 0 {
return fmt.Errorf("invalid namespace '%s': %v", ns, err)
}
@@ -141,14 +143,14 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: tenantClusterRole,
Name: tenantArgs.clusterRole,
},
}
roleBindings = append(roleBindings, roleBinding)
}
if export {
for i, _ := range tenantNamespaces {
if createArgs.export {
for i, _ := range tenantArgs.namespaces {
if err := exportTenant(namespaces[i], accounts[i], roleBindings[i]); err != nil {
return err
}
@@ -156,15 +158,15 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
for i, _ := range tenantNamespaces {
for i, _ := range tenantArgs.namespaces {
logger.Actionf("applying namespace %s", namespaces[i].Name)
if err := upsertNamespace(ctx, kubeClient, namespaces[i]); err != nil {
return err

View File

@@ -33,12 +33,14 @@ var deleteCmd = &cobra.Command{
Long: "The delete sub-commands delete sources and resources.",
}
var (
deleteSilent bool
)
type deleteFlags struct {
silent bool
}
var deleteArgs deleteFlags
func init() {
deleteCmd.PersistentFlags().BoolVarP(&deleteSilent, "silent", "s", false,
deleteCmd.PersistentFlags().BoolVarP(&deleteArgs.silent, "silent", "s", false,
"delete resource without asking for confirmation")
rootCmd.AddCommand(deleteCmd)
@@ -55,16 +57,16 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -73,7 +75,7 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
return err
}
if !deleteSilent {
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this " + del.humanKind,
IsConfirm: true,
@@ -83,7 +85,7 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
}
}
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, namespace)
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, rootArgs.namespace)
err = kubeClient.Delete(ctx, del.object.asClientObject())
if err != nil {
return err

View File

@@ -48,16 +48,16 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -67,7 +67,7 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if !deleteSilent {
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Alert",
IsConfirm: true,
@@ -77,7 +77,7 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
}
}
logger.Actionf("deleting alert %s in %s namespace", name, namespace)
logger.Actionf("deleting alert %s in %s namespace", name, rootArgs.namespace)
err = kubeClient.Delete(ctx, &alert)
if err != nil {
return err

View File

@@ -48,16 +48,16 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -67,7 +67,7 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if !deleteSilent {
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Provider",
IsConfirm: true,
@@ -77,7 +77,7 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
}
logger.Actionf("deleting provider %s in %s namespace", name, namespace)
logger.Actionf("deleting provider %s in %s namespace", name, rootArgs.namespace)
err = kubeClient.Delete(ctx, &alertProvider)
if err != nil {
return err

View File

@@ -17,15 +17,8 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"github.com/spf13/cobra"
)
var deleteHelmReleaseCmd = &cobra.Command{
@@ -36,57 +29,12 @@ var deleteHelmReleaseCmd = &cobra.Command{
Example: ` # Delete a Helm release and the Kubernetes resources created by it
flux delete hr podinfo
`,
RunE: deleteHelmReleaseCmdRun,
RunE: deleteCommand{
apiType: helmReleaseType,
object: universalAdapter{&helmv2.HelmRelease{}},
}.run,
}
func init() {
deleteCmd.AddCommand(deleteHelmReleaseCmd)
}
func deleteHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("release name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var helmRelease helmv2.HelmRelease
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
if !deleteSilent {
if !helmRelease.Spec.Suspend {
logger.Waitingf("This action will remove the Kubernetes objects previously applied by the %s Helm release!", name)
}
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Helm release",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting release %s in %s namespace", name, namespace)
err = kubeClient.Delete(ctx, &helmRelease)
if err != nil {
return err
}
logger.Successf("release deleted")
return nil
}

View File

@@ -20,12 +20,12 @@ import (
"github.com/spf13/cobra"
)
var deleteAutoCmd = &cobra.Command{
Use: "auto",
Short: "Delete automation objects",
Long: "The delete auto sub-commands delete automation objects.",
var deleteImageCmd = &cobra.Command{
Use: "image",
Short: "Delete image automation objects",
Long: "The delete image sub-commands delete image automation objects.",
}
func init() {
deleteCmd.AddCommand(deleteAutoCmd)
deleteCmd.AddCommand(deleteImageCmd)
}

View File

@@ -23,11 +23,11 @@ import (
)
var deleteImagePolicyCmd = &cobra.Command{
Use: "image-policy [name]",
Use: "policy [name]",
Short: "Delete an ImagePolicy object",
Long: "The delete auto image-policy command deletes the given ImagePolicy from the cluster.",
Long: "The delete image policy command deletes the given ImagePolicy from the cluster.",
Example: ` # Delete an image policy
flux delete auto image-policy alpine3.x
flux delete image policy alpine3.x
`,
RunE: deleteCommand{
apiType: imagePolicyType,
@@ -36,5 +36,5 @@ var deleteImagePolicyCmd = &cobra.Command{
}
func init() {
deleteAutoCmd.AddCommand(deleteImagePolicyCmd)
deleteImageCmd.AddCommand(deleteImagePolicyCmd)
}

View File

@@ -23,11 +23,11 @@ import (
)
var deleteImageRepositoryCmd = &cobra.Command{
Use: "image-repository [name]",
Use: "repository [name]",
Short: "Delete an ImageRepository object",
Long: "The delete auto image-repository command deletes the given ImageRepository from the cluster.",
Long: "The delete image repository command deletes the given ImageRepository from the cluster.",
Example: ` # Delete an image repository
flux delete auto image-repository alpine
flux delete image repository alpine
`,
RunE: deleteCommand{
apiType: imageRepositoryType,
@@ -36,5 +36,5 @@ var deleteImageRepositoryCmd = &cobra.Command{
}
func init() {
deleteAutoCmd.AddCommand(deleteImageRepositoryCmd)
deleteImageCmd.AddCommand(deleteImageRepositoryCmd)
}

View File

@@ -23,11 +23,11 @@ import (
)
var deleteImageUpdateCmd = &cobra.Command{
Use: "image-update [name]",
Use: "update [name]",
Short: "Delete an ImageUpdateAutomation object",
Long: "The delete auto image-update command deletes the given ImageUpdateAutomation from the cluster.",
Long: "The delete image update command deletes the given ImageUpdateAutomation from the cluster.",
Example: ` # Delete an image update automation
flux delete auto image-update latest-images
flux delete image update latest-images
`,
RunE: deleteCommand{
apiType: imageUpdateAutomationType,
@@ -36,5 +36,5 @@ var deleteImageUpdateCmd = &cobra.Command{
}
func init() {
deleteAutoCmd.AddCommand(deleteImageUpdateCmd)
deleteImageCmd.AddCommand(deleteImageUpdateCmd)
}

View File

@@ -17,14 +17,8 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
)
var deleteKsCmd = &cobra.Command{
@@ -35,57 +29,12 @@ var deleteKsCmd = &cobra.Command{
Example: ` # Delete a kustomization and the Kubernetes resources created by it
flux delete kustomization podinfo
`,
RunE: deleteKsCmdRun,
RunE: deleteCommand{
apiType: kustomizationType,
object: universalAdapter{&kustomizev1.Kustomization{}},
}.run,
}
func init() {
deleteCmd.AddCommand(deleteKsCmd)
}
func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("kustomization name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var kustomization kustomizev1.Kustomization
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if !deleteSilent {
if !kustomization.Spec.Suspend {
logger.Waitingf("This action will remove the Kubernetes objects previously applied by the %s kustomization!", name)
}
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this kustomization",
IsConfirm: true,
}
if _, err := prompt.Run(); err != nil {
return fmt.Errorf("aborting")
}
}
logger.Actionf("deleting kustomization %s in %s namespace", name, namespace)
err = kubeClient.Delete(ctx, &kustomization)
if err != nil {
return err
}
logger.Successf("kustomization deleted")
return nil
}

View File

@@ -48,16 +48,16 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -67,7 +67,7 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if !deleteSilent {
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Receiver",
IsConfirm: true,
@@ -77,7 +77,7 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
}
logger.Actionf("deleting receiver %s in %s namespace", name, namespace)
logger.Actionf("deleting receiver %s in %s namespace", name, rootArgs.namespace)
err = kubeClient.Delete(ctx, &receiver)
if err != nil {
return err

View File

@@ -17,14 +17,8 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
)
var deleteSourceBucketCmd = &cobra.Command{
@@ -34,54 +28,12 @@ var deleteSourceBucketCmd = &cobra.Command{
Example: ` # Delete a Bucket source
flux delete source bucket podinfo
`,
RunE: deleteSourceBucketCmdRun,
RunE: deleteCommand{
apiType: bucketType,
object: universalAdapter{&sourcev1.Bucket{}},
}.run,
}
func init() {
deleteSourceCmd.AddCommand(deleteSourceBucketCmd)
}
func deleteSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var bucket sourcev1.Bucket
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return err
}
if !deleteSilent {
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, namespace)
err = kubeClient.Delete(ctx, &bucket)
if err != nil {
return err
}
logger.Successf("source deleted")
return nil
}

View File

@@ -17,14 +17,8 @@ limitations under the License.
package main
import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
)
var deleteSourceGitCmd = &cobra.Command{
@@ -34,54 +28,12 @@ var deleteSourceGitCmd = &cobra.Command{
Example: ` # Delete a Git repository
flux delete source git podinfo
`,
RunE: deleteSourceGitCmdRun,
RunE: deleteCommand{
apiType: gitRepositoryType,
object: universalAdapter{&sourcev1.GitRepository{}},
}.run,
}
func init() {
deleteSourceCmd.AddCommand(deleteSourceGitCmd)
}
func deleteSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("git name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var git sourcev1.GitRepository
err = kubeClient.Get(ctx, namespacedName, &git)
if err != nil {
return err
}
if !deleteSilent {
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, namespace)
err = kubeClient.Delete(ctx, &git)
if err != nil {
return err
}
logger.Successf("source deleted")
return nil
}

View File

@@ -34,7 +34,10 @@ var deleteSourceHelmCmd = &cobra.Command{
Example: ` # Delete a Helm repository
flux delete source helm podinfo
`,
RunE: deleteSourceHelmCmdRun,
RunE: deleteCommand{
apiType: helmRepositoryType,
object: universalAdapter{&sourcev1.HelmRepository{}},
}.run,
}
func init() {
@@ -47,16 +50,16 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -66,7 +69,7 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if !deleteSilent {
if !deleteArgs.silent {
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this source",
IsConfirm: true,
@@ -76,7 +79,7 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}
}
logger.Actionf("deleting source %s in %s namespace", name, namespace)
logger.Actionf("deleting source %s in %s namespace", name, rootArgs.namespace)
err = kubeClient.Delete(ctx, &helmRepository)
if err != nil {
return err

View File

@@ -35,12 +35,14 @@ var exportCmd = &cobra.Command{
Long: "The export sub-commands export resources in YAML format.",
}
var (
exportAll bool
)
type exportFlags struct {
all bool
}
var exportArgs exportFlags
func init() {
exportCmd.PersistentFlags().BoolVar(&exportAll, "all", false, "select all resources")
exportCmd.PersistentFlags().BoolVar(&exportArgs.all, "all", false, "select all resources")
rootCmd.AddCommand(exportCmd)
}
@@ -65,26 +67,26 @@ type exportCommand struct {
}
func (export exportCommand) run(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(namespace))
if exportArgs.all {
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(rootArgs.namespace))
if err != nil {
return err
}
if export.list.len() == 0 {
logger.Failuref("no objects found in %s namespace", namespace)
logger.Failuref("no objects found in %s namespace", rootArgs.namespace)
return nil
}
@@ -96,7 +98,7 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())

View File

@@ -48,27 +48,27 @@ func init() {
}
func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
if exportArgs.all {
var list notificationv1.AlertList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
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", namespace)
logger.Failuref("no alerts found in %s namespace", rootArgs.namespace)
return nil
}
@@ -80,7 +80,7 @@ func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var alert notificationv1.Alert

View File

@@ -48,27 +48,27 @@ func init() {
}
func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
if exportArgs.all {
var list notificationv1.ProviderList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
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", namespace)
logger.Failuref("no alertproviders found in %s namespace", rootArgs.namespace)
return nil
}
@@ -80,7 +80,7 @@ func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var alertProvider notificationv1.Provider

View File

@@ -49,27 +49,27 @@ func init() {
}
func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
if exportArgs.all {
var list helmv2.HelmReleaseList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
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", namespace)
logger.Failuref("no helmrelease found in %s namespace", rootArgs.namespace)
return nil
}
@@ -81,7 +81,7 @@ func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var helmRelease helmv2.HelmRelease

View File

@@ -49,27 +49,27 @@ func init() {
}
func exportKsCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("kustomization name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
if exportArgs.all {
var list kustomizev1.KustomizationList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
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", namespace)
logger.Failuref("no kustomizations found in %s namespace", rootArgs.namespace)
return nil
}
@@ -81,7 +81,7 @@ func exportKsCmdRun(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var kustomization kustomizev1.Kustomization

View File

@@ -48,27 +48,27 @@ func init() {
}
func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
if exportArgs.all {
var list notificationv1.ReceiverList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
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", namespace)
logger.Failuref("no receivers found in %s namespace", rootArgs.namespace)
return nil
}
@@ -80,7 +80,7 @@ func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var receiver notificationv1.Receiver

View File

@@ -34,7 +34,7 @@ import (
var exportSourceBucketCmd = &cobra.Command{
Use: "bucket [name]",
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
flux export source bucket --all > sources.yaml
@@ -49,27 +49,27 @@ func init() {
}
func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
if exportArgs.all {
var list sourcev1.BucketList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
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", namespace)
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
return nil
}
@@ -86,7 +86,7 @@ func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var bucket sourcev1.Bucket

View File

@@ -34,7 +34,7 @@ import (
var exportSourceGitCmd = &cobra.Command{
Use: "git [name]",
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
flux export source git --all > sources.yaml
@@ -49,27 +49,27 @@ func init() {
}
func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
if exportArgs.all {
var list sourcev1.GitRepositoryList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
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", namespace)
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
return nil
}
@@ -86,7 +86,7 @@ func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var repository sourcev1.GitRepository

View File

@@ -34,7 +34,7 @@ import (
var exportSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
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
flux export source helm --all > sources.yaml
@@ -49,27 +49,27 @@ func init() {
}
func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
if !exportArgs.all && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
if exportAll {
if exportArgs.all {
var list sourcev1.HelmRepositoryList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
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", namespace)
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
return nil
}
@@ -86,7 +86,7 @@ func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var repository sourcev1.HelmRepository

View File

@@ -18,7 +18,9 @@ package main
import (
"context"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -32,21 +34,25 @@ import (
var getCmd = &cobra.Command{
Use: "get",
Short: "Get sources and resources",
Long: "The get sub-commands print the statuses of sources and resources.",
Short: "Get the resources and their status",
Long: "The get sub-commands print the statuses of Flux resources.",
}
var allNamespaces bool
type GetFlags struct {
allNamespaces bool
}
var getArgs GetFlags
func init() {
getCmd.PersistentFlags().BoolVarP(&allNamespaces, "all-namespaces", "A", false,
getCmd.PersistentFlags().BoolVarP(&getArgs.allNamespaces, "all-namespaces", "A", false,
"list the requested object(s) across all namespaces")
rootCmd.AddCommand(getCmd)
}
type summarisable interface {
listAdapter
summariseItem(i int, includeNamespace bool) []string
summariseItem(i int, includeNamespace bool, includeKind bool) []string
headers(includeNamespace bool) []string
}
@@ -59,11 +65,17 @@ func statusAndMessage(conditions []metav1.Condition) (string, string) {
return string(metav1.ConditionFalse), "waiting to be reconciled"
}
func nameColumns(item named, includeNamespace bool) []string {
if includeNamespace {
return []string{item.GetNamespace(), item.GetName()}
func nameColumns(item named, includeNamespace bool, includeKind bool) []string {
name := 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"}
@@ -74,34 +86,47 @@ type getCommand struct {
}
func (get getCommand) run(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
if !getArgs.allNamespaces {
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
}
if len(args) > 0 {
listOpts = append(listOpts, client.MatchingFields{"metadata.name": args[0]})
}
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
if err != nil {
return err
}
getAll := cmd.Use == "all"
if get.list.len() == 0 {
logger.Failuref("no %s objects found in %s namespace", get.kind, namespace)
if !getAll {
logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace)
}
return nil
}
header := get.list.headers(allNamespaces)
header := get.list.headers(getArgs.allNamespaces)
var rows [][]string
for i := 0; i < get.list.len(); i++ {
row := get.list.summariseItem(i, allNamespaces)
row := get.list.summariseItem(i, getArgs.allNamespaces, getAll)
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
if getAll {
fmt.Println()
}
return nil
}

View File

@@ -33,9 +33,10 @@ import (
)
var getAlertCmd = &cobra.Command{
Use: "alerts",
Short: "Get Alert statuses",
Long: "The get alert command prints the statuses of the resources.",
Use: "alerts",
Aliases: []string{"alert"},
Short: "Get Alert statuses",
Long: "The get alert command prints the statuses of the resources.",
Example: ` # List all Alerts and their status
flux get alerts
`,
@@ -47,17 +48,17 @@ func init() {
}
func getAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
if !getArgs.allNamespaces {
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
}
var list notificationv1.AlertList
err = kubeClient.List(ctx, &list, listOpts...)
@@ -66,12 +67,12 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
}
if len(list.Items) == 0 {
logger.Failuref("no alerts found in %s namespace", namespace)
logger.Failuref("no alerts found in %s namespace", rootArgs.namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Suspended"}
if allNamespaces {
if getArgs.allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
@@ -92,7 +93,7 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
}
}
if allNamespaces {
if getArgs.allNamespaces {
row = append([]string{alert.Namespace}, row...)
}
rows = append(rows, row)

View File

@@ -31,9 +31,10 @@ import (
)
var getAlertProviderCmd = &cobra.Command{
Use: "alert-providers",
Short: "Get Provider statuses",
Long: "The get alert-provider command prints the statuses of the resources.",
Use: "alert-providers",
Aliases: []string{"alert-provider"},
Short: "Get Provider statuses",
Long: "The get alert-provider command prints the statuses of the resources.",
Example: ` # List all Providers and their status
flux get alert-providers
`,
@@ -45,17 +46,17 @@ func init() {
}
func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
if !getArgs.allNamespaces {
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
}
var list notificationv1.ProviderList
err = kubeClient.List(ctx, &list, listOpts...)
@@ -64,12 +65,12 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
if len(list.Items) == 0 {
logger.Failuref("no providers found in %s namespace", namespace)
logger.Failuref("no providers found in %s namespace", rootArgs.namespace)
return nil
}
header := []string{"Name", "Ready", "Message"}
if allNamespaces {
if getArgs.allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
@@ -88,7 +89,7 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
"waiting to be reconciled",
}
}
if allNamespaces {
if getArgs.allNamespaces {
row = append([]string{provider.Namespace}, row...)
}
rows = append(rows, row)

View File

@@ -17,90 +17,43 @@ limitations under the License.
package main
import (
"context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"github.com/spf13/cobra"
)
var getHelmReleaseCmd = &cobra.Command{
Use: "helmreleases",
Aliases: []string{"hr"},
Aliases: []string{"hr", "helmrelease"},
Short: "Get HelmRelease statuses",
Long: "The get helmreleases command prints the statuses of the resources.",
Example: ` # List all Helm releases and their status
flux get helmreleases
`,
RunE: getHelmReleaseCmdRun,
RunE: getCommand{
apiType: helmReleaseType,
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
}.run,
}
func init() {
getCmd.AddCommand(getHelmReleaseCmd)
}
func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list helmv2.HelmReleaseList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no releases found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, helmRelease := range list.Items {
row := []string{}
if c := apimeta.FindStatusCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
helmRelease.GetName(),
string(c.Status),
c.Message,
helmRelease.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(helmRelease.Spec.Suspend)),
}
} else {
row = []string{
helmRelease.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
helmRelease.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(helmRelease.Spec.Suspend)),
}
}
if allNamespaces {
row = append([]string{helmRelease.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
func (a helmReleaseListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i]
revision := item.Status.LastAppliedRevision
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
}
func (a helmReleaseListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace {
headers = append([]string{"Namespace"}, headers...)
}
return headers
}

View File

@@ -21,9 +21,10 @@ import (
)
var getImageCmd = &cobra.Command{
Use: "image",
Short: "Get image automation object status",
Long: "The get image sub-commands print the status of image automation objects.",
Use: "images",
Aliases: []string{"image"},
Short: "Get image automation object status",
Long: "The get image sub-commands print the status of image automation objects.",
}
func init() {

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

@@ -0,0 +1,66 @@
/*
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

@@ -42,10 +42,10 @@ func init() {
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]
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 {

View File

@@ -46,14 +46,14 @@ func init() {
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]
status, msg := statusAndMessage(item.Status.Conditions)
var lastScan string
if item.Status.LastScanResult != nil {
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)))
}

View File

@@ -46,14 +46,14 @@ func init() {
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]
status, msg := statusAndMessage(item.Status.Conditions)
var lastRun string
if item.Status.LastAutomationRunTime != nil {
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 {

View File

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

View File

@@ -33,9 +33,10 @@ import (
)
var getReceiverCmd = &cobra.Command{
Use: "receivers",
Short: "Get Receiver statuses",
Long: "The get receiver command prints the statuses of the resources.",
Use: "receivers",
Aliases: []string{"receiver"},
Short: "Get Receiver statuses",
Long: "The get receiver command prints the statuses of the resources.",
Example: ` # List all Receiver and their status
flux get receivers
`,
@@ -47,17 +48,17 @@ func init() {
}
func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
if !getArgs.allNamespaces {
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
}
var list notificationv1.ReceiverList
err = kubeClient.List(ctx, &list, listOpts...)
@@ -66,17 +67,17 @@ func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
if len(list.Items) == 0 {
logger.Failuref("no receivers found in %s namespace", namespace)
logger.Failuref("no receivers found in %s namespace", rootArgs.namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Suspended"}
if allNamespaces {
if getArgs.allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, receiver := range list.Items {
row := []string{}
var row []string
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
receiver.GetName(),

View File

@@ -21,9 +21,10 @@ import (
)
var getSourceCmd = &cobra.Command{
Use: "sources",
Short: "Get source statuses",
Long: "The get source sub-commands print the statuses of the sources.",
Use: "sources",
Aliases: []string{"source"},
Short: "Get source statuses",
Long: "The get source sub-commands print the statuses of the sources.",
}
func init() {

View File

@@ -0,0 +1,73 @@
/*
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

@@ -17,19 +17,11 @@ limitations under the License.
package main
import (
"context"
"os"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"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"
)
var getSourceBucketCmd = &cobra.Command{
@@ -42,70 +34,31 @@ var getSourceBucketCmd = &cobra.Command{
# List buckets from all namespaces
flux get sources helm --all-namespaces
`,
RunE: getSourceBucketCmdRun,
RunE: getCommand{
apiType: bucketType,
list: &bucketListAdapter{&sourcev1.BucketList{}},
}.run,
}
func init() {
getSourceCmd.AddCommand(getSourceBucketCmd)
}
func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
func (a *bucketListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i]
var revision string
if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list sourcev1.BucketList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no bucket sources found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, source := range list.Items {
var row []string
var revision string
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
string(c.Status),
c.Message,
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
} else {
row = []string{
source.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
}
if allNamespaces {
row = append([]string{source.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
}
func (a bucketListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace {
headers = append([]string{"Namespace"}, headers...)
}
return headers
}

View File

@@ -17,19 +17,11 @@ limitations under the License.
package main
import (
"context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"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"
)
var getSourceHelmChartCmd = &cobra.Command{
@@ -42,70 +34,31 @@ var getSourceHelmChartCmd = &cobra.Command{
# List Helm charts from all namespaces
flux get sources chart --all-namespaces
`,
RunE: getSourceHelmChartCmdRun,
RunE: getCommand{
apiType: helmChartType,
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
}.run,
}
func init() {
getSourceCmd.AddCommand(getSourceHelmChartCmd)
}
func getSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
func (a *helmChartListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i]
var revision string
if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list sourcev1.HelmChartList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no chart sources found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, source := range list.Items {
var row []string
var revision string
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
string(c.Status),
c.Message,
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
} else {
row = []string{
source.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
}
if allNamespaces {
row = append([]string{source.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
}
func (a helmChartListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace {
headers = append([]string{"Namespace"}, headers...)
}
return headers
}

View File

@@ -17,19 +17,11 @@ limitations under the License.
package main
import (
"context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"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"
)
var getSourceGitCmd = &cobra.Command{
@@ -42,70 +34,31 @@ var getSourceGitCmd = &cobra.Command{
# List Git repositories from all namespaces
flux get sources git --all-namespaces
`,
RunE: getSourceGitCmdRun,
RunE: getCommand{
apiType: gitRepositoryType,
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
}.run,
}
func init() {
getSourceCmd.AddCommand(getSourceGitCmd)
}
func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
func (a *gitRepositoryListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i]
var revision string
if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list sourcev1.GitRepositoryList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no git sources found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, source := range list.Items {
var row []string
var revision string
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
string(c.Status),
c.Message,
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
} else {
row = []string{
source.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
}
if allNamespaces {
row = append([]string{source.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
}
func (a gitRepositoryListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace {
headers = append([]string{"Namespace"}, headers...)
}
return headers
}

View File

@@ -17,19 +17,11 @@ limitations under the License.
package main
import (
"context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"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"
)
var getSourceHelmCmd = &cobra.Command{
@@ -42,70 +34,31 @@ var getSourceHelmCmd = &cobra.Command{
# List Helm repositories from all namespaces
flux get sources helm --all-namespaces
`,
RunE: getSourceHelmCmdRun,
RunE: getCommand{
apiType: helmRepositoryType,
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
}.run,
}
func init() {
getSourceCmd.AddCommand(getSourceHelmCmd)
}
func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
func (a *helmRepositoryListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := a.Items[i]
var revision string
if item.GetArtifact() != nil {
revision = item.GetArtifact().Revision
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list sourcev1.HelmRepositoryList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no helm sources found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, source := range list.Items {
var row []string
var revision string
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
string(c.Status),
c.Message,
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
} else {
row = []string{
source.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
}
if allNamespaces {
row = append([]string{source.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind),
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
}
func (a helmRepositoryListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace {
headers = append([]string{"Namespace"}, headers...)
}
return headers
}

51
cmd/flux/helmrelease.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 (
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// helmv2.HelmRelease
var helmReleaseType = apiType{
kind: helmv2.HelmReleaseKind,
humanKind: "helmreleases",
}
type helmReleaseAdapter struct {
*helmv2.HelmRelease
}
func (h helmReleaseAdapter) asClientObject() client.Object {
return h.HelmRelease
}
// helmv2.HelmReleaseList
type helmReleaseListAdapter struct {
*helmv2.HelmReleaseList
}
func (h helmReleaseListAdapter) asClientList() client.ObjectList {
return h.HelmReleaseList
}
func (h helmReleaseListAdapter) len() int {
return len(h.HelmReleaseList.Items)
}

View File

@@ -23,25 +23,30 @@ import (
"os"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/pkg/status"
)
var installCmd = &cobra.Command{
Use: "install",
Short: "Install the toolkit components",
Long: `The install command deploys the toolkit components in the specified namespace.
Short: "Install or upgrade Flux",
Long: `The install command deploys Flux in the specified namespace.
If a previous version is installed, then an in-place upgrade will be performed.`,
Example: ` # Install the latest version in the flux-system namespace
flux install --version=latest --namespace=flux-system
# Dry-run install for a specific version and a series of components
# Install a specific version and a series of components
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
# Install Flux onto tainted Kubernetes nodes
flux install --toleration-keys=node.kubernetes.io/dedicated-to-flux
# Dry-run install with manifests preview
flux install --dry-run --verbose
@@ -51,91 +56,119 @@ If a previous version is installed, then an in-place upgrade will be performed.`
RunE: installCmdRun,
}
var (
installExport bool
installDryRun bool
installManifestsPath string
installVersion string
installDefaultComponents []string
installExtraComponents []string
installRegistry string
installImagePullSecret string
installWatchAllNamespaces bool
installNetworkPolicy bool
installArch flags.Arch
installLogLevel = flags.LogLevel(defaults.LogLevel)
installClusterDomain string
)
type installFlags struct {
export bool
dryRun bool
version string
defaultComponents []string
extraComponents []string
registry string
imagePullSecret string
branch string
watchAllNamespaces bool
networkPolicy bool
manifestsPath string
arch flags.Arch
logLevel flags.LogLevel
tokenAuth bool
clusterDomain string
tolerationKeys []string
}
var installArgs = NewInstallFlags()
func init() {
installCmd.Flags().BoolVar(&installExport, "export", false,
installCmd.Flags().BoolVar(&installArgs.export, "export", false,
"write the install manifests to stdout and exit")
installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false,
installCmd.Flags().BoolVarP(&installArgs.dryRun, "dry-run", "", false,
"only print the object that would be applied")
installCmd.Flags().StringVarP(&installVersion, "version", "v", defaults.Version,
"toolkit version")
installCmd.Flags().StringSliceVar(&installDefaultComponents, "components", defaults.Components,
installCmd.Flags().StringVarP(&installArgs.version, "version", "v", "",
"toolkit version, when specified the manifests are downloaded from https://github.com/fluxcd/flux2/releases")
installCmd.Flags().StringSliceVar(&installArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values")
installCmd.Flags().StringSliceVar(&installExtraComponents, "components-extra", nil,
installCmd.Flags().StringSliceVar(&installArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory")
installCmd.Flags().StringVar(&installRegistry, "registry", defaults.Registry,
installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory")
installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry,
"container registry where the toolkit images are published")
installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "",
installCmd.Flags().StringVar(&installArgs.imagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry")
installCmd.Flags().Var(&installArch, "arch", installArch.Description())
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", defaults.WatchAllNamespaces,
installCmd.Flags().Var(&installArgs.arch, "arch", installArgs.arch.Description())
installCmd.Flags().BoolVar(&installArgs.watchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces,
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description())
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", defaults.NetworkPolicy,
installCmd.Flags().Var(&installArgs.logLevel, "log-level", installArgs.logLevel.Description())
installCmd.Flags().BoolVar(&installArgs.networkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy,
"deny ingress access to the toolkit controllers from other namespaces using network policies")
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", defaults.ClusterDomain, "internal cluster domain")
installCmd.Flags().StringVar(&installArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
installCmd.Flags().StringSliceVar(&installArgs.tolerationKeys, "toleration-keys", nil,
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
installCmd.Flags().MarkHidden("manifests")
installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
rootCmd.AddCommand(installCmd)
}
func NewInstallFlags() installFlags {
return installFlags{
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
}
}
func installCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
tmpDir, err := ioutil.TempDir("", namespace)
components := append(installArgs.defaultComponents, installArgs.extraComponents...)
err := utils.ValidateComponents(components)
if err != nil {
return err
}
if ver, err := getVersion(installArgs.version); err != nil {
return err
} else {
installArgs.version = ver
}
if !installArgs.export {
logger.Generatef("generating manifests")
}
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
if !installExport {
logger.Generatef("generating manifests")
}
components := append(installDefaultComponents, installExtraComponents...)
if err := utils.ValidateComponents(components); err != nil {
return err
manifestsBase := ""
if isEmbeddedVersion(installArgs.version) {
if err := writeEmbeddedManifests(tmpDir); err != nil {
return err
}
manifestsBase = tmpDir
}
opts := install.Options{
BaseURL: installManifestsPath,
Version: installVersion,
Namespace: namespace,
BaseURL: installArgs.manifestsPath,
Version: installArgs.version,
Namespace: rootArgs.namespace,
Components: components,
Registry: installRegistry,
ImagePullSecret: installImagePullSecret,
WatchAllNamespaces: installWatchAllNamespaces,
NetworkPolicy: installNetworkPolicy,
LogLevel: installLogLevel.String(),
NotificationController: defaults.NotificationController,
ManifestFile: fmt.Sprintf("%s.yaml", namespace),
Timeout: timeout,
ClusterDomain: installClusterDomain,
Registry: installArgs.registry,
ImagePullSecret: installArgs.imagePullSecret,
WatchAllNamespaces: installArgs.watchAllNamespaces,
NetworkPolicy: installArgs.networkPolicy,
LogLevel: installArgs.logLevel.String(),
NotificationController: rootArgs.defaults.NotificationController,
ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace),
Timeout: rootArgs.timeout,
ClusterDomain: installArgs.clusterDomain,
TolerationKeys: installArgs.tolerationKeys,
}
if installManifestsPath == "" {
if installArgs.manifestsPath == "" {
opts.BaseURL = install.MakeDefaultOptions().BaseURL
}
manifest, err := install.Generate(opts)
manifest, err := install.Generate(opts, manifestsBase)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
@@ -144,11 +177,11 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("install failed: %w", err)
}
if verbose {
if rootArgs.verbose {
fmt.Print(manifest.Content)
} else if installExport {
} else if installArgs.export {
fmt.Println("---")
fmt.Println("# GitOps Toolkit revision", installVersion)
fmt.Println("# Flux version:", installArgs.version)
fmt.Println("# Components:", strings.Join(components, ","))
fmt.Print(manifest.Content)
fmt.Println("---")
@@ -156,36 +189,41 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
}
logger.Successf("manifests build completed")
logger.Actionf("installing components in %s namespace", namespace)
logger.Actionf("installing components in %s namespace", rootArgs.namespace)
applyOutput := utils.ModeStderrOS
if verbose {
if rootArgs.verbose {
applyOutput = utils.ModeOS
}
kubectlArgs := []string{"apply", "-f", filepath.Join(tmpDir, manifest.Path)}
if installDryRun {
if installArgs.dryRun {
kubectlArgs = append(kubectlArgs, "--dry-run=client")
applyOutput = utils.ModeOS
}
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed: %w", err)
}
if installDryRun {
if installArgs.dryRun {
logger.Successf("install dry-run finished")
return nil
} else {
logger.Successf("install completed")
}
kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
componentRefs, err := buildComponentObjectRefs(components...)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
logger.Waitingf("verifying installation")
for _, deployment := range components {
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
} else {
logger.Successf("%s ready", deployment)
}
if err := statusChecker.Assess(componentRefs...); err != nil {
return fmt.Errorf("install failed")
}
logger.Successf("install finished")

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

@@ -0,0 +1,51 @@
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// kustomizev1.Kustomization
var kustomizationType = apiType{
kind: kustomizev1.KustomizationKind,
humanKind: "kustomizations",
}
type kustomizationAdapter struct {
*kustomizev1.Kustomization
}
func (a kustomizationAdapter) asClientObject() client.Object {
return a.Kustomization
}
// kustomizev1.KustomizationList
type kustomizationListAdapter struct {
*kustomizev1.KustomizationList
}
func (a kustomizationListAdapter) asClientList() client.ObjectList {
return a.KustomizationList
}
func (a kustomizationListAdapter) len() int {
return len(a.KustomizationList.Items)
}

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

@@ -26,7 +26,6 @@ import (
"github.com/spf13/cobra/doc"
_ "k8s.io/client-go/plugin/pkg/client/auth"
fluxlog "github.com/fluxcd/flux2/pkg/log"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
)
@@ -42,7 +41,7 @@ var rootCmd = &cobra.Command{
Example: ` # Check prerequisites
flux check --pre
# Install the latest version of the toolkit
# Install the latest version of Flux
flux install --version=master
# Create a source from a public Git repository
@@ -89,27 +88,39 @@ var rootCmd = &cobra.Command{
# Delete a GitRepository source
flux delete source git webapp-latest
# Uninstall the toolkit and delete CRDs
flux uninstall --crds
# Uninstall Flux and delete CRDs
flux uninstall
`,
}
var (
var logger = stderrLogger{stderr: os.Stderr}
type rootFlags struct {
kubeconfig string
kubecontext string
namespace string
timeout time.Duration
verbose bool
pollInterval = 2 * time.Second
logger fluxlog.Logger = stderrLogger{stderr: os.Stderr}
defaults = install.MakeDefaultOptions()
)
pollInterval time.Duration
defaults install.Options
}
var rootArgs = NewRootFlags()
func init() {
rootCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", defaults.Namespace, "the namespace scope for this operation")
rootCmd.PersistentFlags().DurationVar(&timeout, "timeout", 5*time.Minute, "timeout for this operation")
rootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "print generated objects")
rootCmd.PersistentFlags().StringVarP(&kubecontext, "context", "", "", "kubernetes context to use")
rootCmd.PersistentFlags().StringVarP(&rootArgs.namespace, "namespace", "n", rootArgs.defaults.Namespace, "the namespace scope for this operation")
rootCmd.PersistentFlags().DurationVar(&rootArgs.timeout, "timeout", 5*time.Minute, "timeout for this operation")
rootCmd.PersistentFlags().BoolVar(&rootArgs.verbose, "verbose", false, "print generated objects")
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubecontext, "context", "", "", "kubernetes context to use")
}
func NewRootFlags() rootFlags {
rf := rootFlags{
pollInterval: 2 * time.Second,
defaults: install.MakeDefaultOptions(),
}
rf.defaults.Version = "v" + VERSION
return rf
}
func main() {
@@ -124,22 +135,22 @@ func main() {
func kubeconfigFlag() {
if home := homeDir(); home != "" {
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "", filepath.Join(home, ".kube", "config"),
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", filepath.Join(home, ".kube", "config"),
"path to the kubeconfig file")
} else {
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "", "",
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "",
"absolute path to the kubeconfig file")
}
if len(os.Getenv("KUBECONFIG")) > 0 {
kubeconfig = os.Getenv("KUBECONFIG")
rootArgs.kubeconfig = os.Getenv("KUBECONFIG")
}
}
func generateDocs() {
args := os.Args[1:]
if len(args) > 0 && args[0] == "docgen" {
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "", "~/.kube/config",
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "~/.kube/config",
"path to the kubeconfig file")
rootCmd.DisableAutoGenTag = true
err := doc.GenMarkdownTree(rootCmd, "./docs/cmd")

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
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -62,6 +63,7 @@ func (c universalAdapter) asClientObject() client.Object {
type named interface {
GetName() string
GetNamespace() string
GetObjectKind() schema.ObjectKind
SetName(string)
SetNamespace(string)
}

View File

@@ -69,16 +69,16 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -91,7 +91,7 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, namespace)
logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, rootArgs.namespace)
if err := requestReconciliation(ctx, kubeClient, namespacedName, reconcile.object); err != nil {
return err
}
@@ -99,7 +99,7 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
return err
}

View File

@@ -51,16 +51,16 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -74,7 +74,7 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating Alert %s in %s namespace", name, namespace)
logger.Actionf("annotating Alert %s in %s namespace", name, rootArgs.namespace)
if alert.Annotations == nil {
alert.Annotations = map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
@@ -89,7 +89,7 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("Alert annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}

View File

@@ -51,20 +51,20 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
logger.Actionf("annotating Provider %s in %s namespace", name, namespace)
logger.Actionf("annotating Provider %s in %s namespace", name, rootArgs.namespace)
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
@@ -84,7 +84,7 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("Provider annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isAlertProviderReady(ctx, kubeClient, namespacedName, &alertProvider)); err != nil {
return err
}

View File

@@ -51,12 +51,14 @@ The reconcile kustomization command triggers a reconciliation of a HelmRelease r
RunE: reconcileHrCmdRun,
}
var (
type reconcileHelmReleaseFlags struct {
syncHrWithSource bool
)
}
var rhrArgs reconcileHelmReleaseFlags
func init() {
reconcileHrCmd.Flags().BoolVar(&syncHrWithSource, "with-source", false, "reconcile HelmRelease source")
reconcileHrCmd.Flags().BoolVar(&rhrArgs.syncHrWithSource, "with-source", false, "reconcile HelmRelease source")
reconcileCmd.AddCommand(reconcileHrCmd)
}
@@ -67,16 +69,16 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -90,29 +92,43 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("resource is suspended")
}
if syncHrWithSource {
if rhrArgs.syncHrWithSource {
nsCopy := rootArgs.namespace
if helmRelease.Spec.Chart.Spec.SourceRef.Namespace != "" {
rootArgs.namespace = helmRelease.Spec.Chart.Spec.SourceRef.Namespace
}
switch helmRelease.Spec.Chart.Spec.SourceRef.Kind {
case sourcev1.HelmRepositoryKind:
err = reconcileSourceHelmCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
err = reconcileCommand{
apiType: helmRepositoryType,
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
case sourcev1.GitRepositoryKind:
err = reconcileSourceGitCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
err = reconcileCommand{
apiType: gitRepositoryType,
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
case sourcev1.BucketKind:
err = reconcileSourceBucketCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
err = reconcileCommand{
apiType: bucketType,
object: bucketAdapter{&sourcev1.Bucket{}},
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
}
if err != nil {
return err
}
rootArgs.namespace = nsCopy
}
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt
logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace)
logger.Actionf("annotating HelmRelease %s in %s namespace", name, rootArgs.namespace)
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
return err
}
logger.Successf("HelmRelease annotated")
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
); err != nil {
return err

View File

@@ -50,12 +50,14 @@ The reconcile kustomization command triggers a reconciliation of a Kustomization
RunE: reconcileKsCmdRun,
}
var (
type reconcileKsFlags struct {
syncKsWithSource bool
)
}
var rksArgs reconcileKsFlags
func init() {
reconcileKsCmd.Flags().BoolVar(&syncKsWithSource, "with-source", false, "reconcile Kustomization source")
reconcileKsCmd.Flags().BoolVar(&rksArgs.syncKsWithSource, "with-source", false, "reconcile Kustomization source")
reconcileCmd.AddCommand(reconcileKsCmd)
}
@@ -66,16 +68,16 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
var kustomization kustomizev1.Kustomization
@@ -88,20 +90,31 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("resource is suspended")
}
if syncKsWithSource {
if rksArgs.syncKsWithSource {
nsCopy := rootArgs.namespace
if kustomization.Spec.SourceRef.Namespace != "" {
rootArgs.namespace = kustomization.Spec.SourceRef.Namespace
}
switch kustomization.Spec.SourceRef.Kind {
case sourcev1.GitRepositoryKind:
err = reconcileSourceGitCmdRun(nil, []string{kustomization.Spec.SourceRef.Name})
err = reconcileCommand{
apiType: gitRepositoryType,
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
case sourcev1.BucketKind:
err = reconcileSourceBucketCmdRun(nil, []string{kustomization.Spec.SourceRef.Name})
err = reconcileCommand{
apiType: bucketType,
object: bucketAdapter{&sourcev1.Bucket{}},
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
}
if err != nil {
return err
}
rootArgs.namespace = nsCopy
}
lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
logger.Actionf("annotating Kustomization %s in %s namespace", name, namespace)
logger.Actionf("annotating Kustomization %s in %s namespace", name, rootArgs.namespace)
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
return err
}
@@ -109,7 +122,7 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(
pollInterval, timeout,
rootArgs.pollInterval, rootArgs.timeout,
kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
); err != nil {
return err

View File

@@ -51,16 +51,16 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Namespace: rootArgs.namespace,
Name: name,
}
@@ -74,7 +74,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating Receiver %s in %s namespace", name, namespace)
logger.Actionf("annotating Receiver %s in %s namespace", name, rootArgs.namespace)
if receiver.Annotations == nil {
receiver.Annotations = map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
@@ -88,7 +88,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("Receiver annotated")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}

View File

@@ -19,13 +19,7 @@ package main
import (
"context"
"fmt"
"time"
"github.com/fluxcd/pkg/apis/meta"
"k8s.io/client-go/util/retry"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -43,64 +37,16 @@ var reconcileSourceBucketCmd = &cobra.Command{
Example: ` # Trigger a reconciliation for an existing source
flux reconcile source bucket podinfo
`,
RunE: reconcileSourceBucketCmdRun,
RunE: reconcileCommand{
apiType: bucketType,
object: bucketAdapter{&sourcev1.Bucket{}},
}.run,
}
func init() {
reconcileSourceCmd.AddCommand(reconcileSourceBucketCmd)
}
func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var bucket sourcev1.Bucket
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return err
}
if bucket.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
lastHandledReconcileAt := bucket.Status.LastHandledReconcileAt
logger.Actionf("annotating Bucket source %s in %s namespace", name, namespace)
if err := requestBucketReconciliation(ctx, kubeClient, namespacedName, &bucket); err != nil {
return err
}
logger.Successf("Bucket source annotated")
logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(
pollInterval, timeout,
bucketReconciliationHandled(ctx, kubeClient, namespacedName, &bucket, lastHandledReconcileAt),
); err != nil {
return err
}
logger.Successf("Bucket source reconciliation completed")
if apimeta.IsStatusConditionFalse(bucket.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("Bucket source reconciliation failed")
}
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
return nil
}
func isBucketReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
return func() (bool, error) {
@@ -126,30 +72,10 @@ func isBucketReady(ctx context.Context, kubeClient client.Client,
}
}
func bucketReconciliationHandled(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, bucket)
if err != nil {
return false, err
}
return bucket.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
func (obj bucketAdapter) lastHandledReconcileRequest() string {
return obj.Status.GetLastHandledReconcileRequest()
}
func requestBucketReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, bucket); err != nil {
return err
}
if bucket.Annotations == nil {
bucket.Annotations = map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
bucket.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
}
return kubeClient.Update(ctx, bucket)
})
func (obj bucketAdapter) successMessage() string {
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
}

View File

@@ -17,21 +17,9 @@ limitations under the License.
package main
import (
"context"
"fmt"
"time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
)
var reconcileSourceGitCmd = &cobra.Command{
@@ -41,86 +29,20 @@ var reconcileSourceGitCmd = &cobra.Command{
Example: ` # Trigger a git pull for an existing source
flux reconcile source git podinfo
`,
RunE: reconcileSourceGitCmdRun,
RunE: reconcileCommand{
apiType: gitRepositoryType,
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
}.run,
}
func init() {
reconcileSourceCmd.AddCommand(reconcileSourceGitCmd)
}
func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var repository sourcev1.GitRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
if repository.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating GitRepository source %s in %s namespace", name, namespace)
if err := requestGitRepositoryReconciliation(ctx, kubeClient, namespacedName, &repository); err != nil {
return err
}
logger.Successf("GitRepository source annotated")
lastHandledReconcileAt := repository.Status.LastHandledReconcileAt
logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
gitRepositoryReconciliationHandled(ctx, kubeClient, namespacedName, &repository, lastHandledReconcileAt)); err != nil {
return err
}
logger.Successf("GitRepository source reconciliation completed")
if apimeta.IsStatusConditionFalse(repository.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("GitRepository source reconciliation failed")
}
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
return nil
func (obj gitRepositoryAdapter) lastHandledReconcileRequest() string {
return obj.Status.GetLastHandledReconcileRequest()
}
func gitRepositoryReconciliationHandled(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, repository *sourcev1.GitRepository, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, repository)
if err != nil {
return false, err
}
return repository.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestGitRepositoryReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, repository *sourcev1.GitRepository) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, repository); err != nil {
return err
}
if repository.Annotations == nil {
repository.Annotations = map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
repository.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
}
return kubeClient.Update(ctx, repository)
})
func (obj gitRepositoryAdapter) successMessage() string {
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
}

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