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

Compare commits

..

154 Commits

Author SHA1 Message Date
Hidde Beydals
1cab68e385 Merge pull request #404 from fluxcd/update-components
Update source-controller component to v0.2.1
2020-10-30 20:53:57 +01:00
fluxcdbot
07e4fa8abd Update toolkit components 2020-10-30 19:45:48 +00:00
Stefan Prodan
77b86c78a4 Merge pull request #403 from fluxcd/install-docs-v0.2.0
Update bootstrap docs to match v0.2
2020-10-30 18:19:52 +02:00
Stefan Prodan
e4c701864a Update bootstrap docs to match v0.2
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-30 18:10:59 +02:00
Stefan Prodan
0275f28490 Merge pull request #402 from fluxcd/kubecon-na-talk
Update talks section
2020-10-30 17:11:36 +02:00
Stefan Prodan
300c3dd578 Update talks section
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-30 17:01:06 +02:00
Stefan Prodan
fb81870803 Merge pull request #401 from fluxcd/docs-v1-migration
Add migration guide for Flux v1 users
2020-10-30 16:10:49 +02:00
Stefan Prodan
bb6a61a313 Add migration guide for Flux v1 users
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-30 16:02:21 +02:00
Stefan Prodan
976d917a57 Merge pull request #400 from fluxcd/fix-install
Fix install script
2020-10-29 21:48:19 +02:00
Stefan Prodan
7e13cc2ca8 Fix install script
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-29 21:20:31 +02:00
Stefan Prodan
704296df4e Merge pull request #365 from mewzherder/patch-3
Community section: add link to GH Discussions
2020-10-29 21:19:46 +02:00
mewzherder
65244e3b8d Community section: add link to GH Discussions
Signed-off-by: mewzherder <tamao@weave.works>
2020-10-29 11:49:05 -07:00
Stefan Prodan
3cf54a4cce Merge pull request #397 from fluxcd/dangling-gotk-link
Add GOTK section to index
2020-10-29 20:17:58 +02:00
Michael Bridgen
031c09d8dc Link to specific guide
In the absence of a dev-guides index or other GitOps Toolkit specific
home page, link to the dev guide that exists, for now.

Signed-off-by: Michael Bridgen <michael@weave.works>
2020-10-29 18:08:55 +00:00
Michael Bridgen
5209d9cf75 Add GOTK section to index
Signed-off-by: Michael Bridgen <michael@weave.works>
2020-10-29 17:57:16 +00:00
Michael Bridgen
815dad18f4 Merge pull request #395 from fluxcd/replace-in-docs
Replace GitOps Toolkit in docs
2020-10-29 17:21:38 +00:00
Michael Bridgen
4319382da3 Change title and author of generated website
I changed the author "The Flux project", since it seemed oddly
redundant to effectively say "the author is the Flux authors".

Signed-off-by: Michael Bridgen <michael@weave.works>
2020-10-29 17:11:23 +00:00
Michael Bridgen
5e96cec95b Replace GitOps Toolkit in docs
This replaces most mentions of "GitOps Toolkit" and "toolkit" with
"Flux". I have adopted the style of using "Flux v2" in the first
mention, and thereafter just "Flux".

Signed-off-by: Michael Bridgen <michael@weave.works>
2020-10-29 17:11:23 +00:00
Hidde Beydals
071982097a Merge pull request #396 from fluxcd/make-docs-target
Delete generated docs before regenerating them
2020-10-29 18:10:33 +01:00
Hidde Beydals
56024de6cc Delete generated docs before regenerating them
This ensures no stray files are kept when for example a command name
changes.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-10-29 17:58:48 +01:00
Hidde Beydals
845d4acd8e Merge pull request #393 from fluxcd/rename-cli-ns 2020-10-29 16:47:01 +01:00
Hidde Beydals
b0029e9255 Remove Flux CLI from Toolkit component overview
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-10-29 16:31:37 +01:00
Hidde Beydals
23bc44a350 Change Homebrew tap instructions
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-10-29 16:31:37 +01:00
Hidde Beydals
0bf52f4746 Change fluxcd/toolkit to fluxcd/flux2
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-10-29 16:31:32 +01:00
Hidde Beydals
345707e6cc Incorporate name and metric changes in Grafana cfg
Signed-off-by: Hidde Beydals <hello@hidde.co>

Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-10-29 16:25:16 +01:00
Hidde Beydals
e2752e4508 Rename gotk binary to flux
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-10-29 16:25:16 +01:00
Hidde Beydals
9916a53761 Rename gotk-system namespace to flux-system
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-10-29 16:25:16 +01:00
Stefan Prodan
4581c99a81 Merge pull request #389 from fluxcd/docs/helmreleases
Update HelmRelease documentation
2020-10-29 16:53:48 +02:00
Hidde Beydals
c3fdc41a91 Update HelmRelease documentation
* Remove mention of dependency download limitation
* Include bucket sources in HelmRelease guide

Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-10-29 16:46:37 +02:00
Stefan Prodan
edf2f51aa1 Merge pull request #385 from fluxcd/readme-flux2
Minimal rewrite of README to explain Flux v2
2020-10-29 16:45:26 +02:00
Michael Bridgen
de944d0095 Minimal rewrite of README to explain Flux v2
- Adjust the description so it talks about the whole system (Flux) as
   one thing

 - Describe what GitOps Toolkit is, and how it relates to Flux v2.

Signed-off-by: Michael Bridgen <michael@weave.works>
2020-10-29 16:38:05 +02:00
Stefan Prodan
49ffc972f8 Merge pull request #392 from fluxcd/update-components
Update toolkit components
2020-10-29 15:22:42 +02:00
fluxcdbot
2d8e5b2420 Update toolkit components 2020-10-29 13:14:48 +00:00
Stefan Prodan
7c48975198 Merge pull request #387 from fluxcd/bootstrap-refac
Refactor manifests generation
2020-10-29 12:40:32 +02:00
Stefan Prodan
9bc250d027 Refactor manifests generation
- introduce manifestgen pkg, to be consumed by the CLI and Terraform provider
- consolidate defaults in manifestgen/install pkg
- introduce Manifest as the returning type of manifest generation
- add helper function to Manifest for writing multi-doc YAMLs on disk

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-29 10:15:53 +02:00
Stefan Prodan
41d4e7e15b Merge pull request #376 from phillebaba/refactor-manifest-generation
Refactor manifest generation into a separate package
2020-10-28 18:15:38 +02:00
Philip Laine
b0d2a38ff6 Refactor manifest generation
Signed-off-by: Philip Laine <philip.laine@xenit.se>
2020-10-28 16:01:43 +01:00
Stefan Prodan
bd4d4d927e Merge pull request #384 from fluxcd/update-stable-repo
Update the stable Helm repository in docs
2020-10-28 15:51:40 +02:00
Stefan Prodan
db17cacbf4 Update the stable Helm repository in docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-28 15:40:26 +02:00
Stefan Prodan
0c5961df98 Merge pull request #381 from fluxcd/copyright
Change copyright to Flux authors
2020-10-27 19:09:46 +02:00
Stefan Prodan
0e67b76b57 Change copyright to Flux authors
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-27 18:59:01 +02:00
Stefan Prodan
f72b323ab5 Merge pull request #378 from chanwit/windows-zip
change the windows binary release format to .zip
2020-10-27 18:54:22 +02:00
Chanwit Kaewkasi
927c21a60f change the windows binary release format to .zip
Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
2020-10-27 23:24:28 +07:00
Hidde Beydals
624a9ba8eb Merge pull request #364 from staceypotter/patch-4
Update featured talks and add upcoming meeting
2020-10-22 17:09:08 +02:00
Stacey Potter
8509283ac2 Update featured talks and add upcoming meeting 2020-10-22 16:44:15 +02:00
Stefan Prodan
aa9ba27f40 Merge pull request #369 from fluxcd/mon-guide
Add monitoring guide
2020-10-21 10:48:40 +03:00
Stefan Prodan
06434464bb Add monitoring guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-21 10:09:28 +03:00
Stefan Prodan
3e7bfc72a2 Merge pull request #361 from fluxcd/uninstall-ignore-missing-crds
Ignore missing kinds when uninstalling
2020-10-20 21:57:10 +03:00
Stefan Prodan
8dfbc35e60 Ignore missing kinds when uninstalling
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-20 00:38:58 +03:00
Stefan Prodan
503e5ec950 Merge pull request #358 from fluxcd/update-components
Update toolkit components
2020-10-19 17:56:48 +03:00
fluxcdbot
414aeb0ac3 Update toolkit components 2020-10-19 14:23:20 +00:00
Stefan Prodan
55b8544d32 Merge pull request #350 from allymparker/source-git-secret-ref
Add secret-ref flag to create source git
2020-10-19 17:21:14 +03:00
Ally Parker
2d67ea5f7f Add secret-ref flag to git source
Add secret-ref flag to Helm source

Add secret-ref to bucket source
2020-10-19 14:49:14 +01:00
Stefan Prodan
4eaf72fa3e Merge pull request #356 from StupidScience/custom-flags
Implement custom flags for options with validation rules
2020-10-19 13:41:47 +03:00
“Anton
95ef3c1782 Update docs 2020-10-19 13:09:53 +03:00
“Anton
b3ef410fb7 Add source bucket provider flag 2020-10-19 13:05:56 +03:00
“Anton
0c55bca218 Add helm chart source flag 2020-10-19 12:55:34 +03:00
“Anton
5fd28439dc Add kustomization source and decryption provider flags 2020-10-19 12:46:10 +03:00
“Anton
a58c18e992 Refresh bootstrap and install docs 2020-10-17 23:47:20 +03:00
“Anton
058dfdfcd6 Move flags and utils to internal packages 2020-10-17 23:35:09 +03:00
Stefan Prodan
637fdac28a Merge pull request #354 from fluxcd/windows-exe
Add Windows OS to CLI install docs
2020-10-17 15:43:54 +03:00
Stefan Prodan
a8b667780a Add Windows OS to CLI install docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-17 14:05:17 +03:00
Stefan Prodan
b4f13e10d3 Merge pull request #351 from fluxcd/gitlab-https-auth
Add GitLab HTTPS auth to bootstrap options
2020-10-17 13:15:26 +03:00
Stefan Prodan
d0eb55fb86 Merge branch 'gitlab-https-auth' of https://github.com/fluxcd/toolkit into gitlab-https-auth 2020-10-17 13:08:38 +03:00
Stefan Prodan
b041dbd14f Add GitLab HTTPS auth to bootstrap options
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-17 09:27:47 +00:00
Stefan Prodan
0ab814fbc4 Merge pull request #348 from fluxcd/update-components
Update toolkit components
2020-10-17 12:26:46 +03:00
fluxcdbot
a2144b1759 Update toolkit components 2020-10-17 07:21:01 +00:00
Stefan Prodan
82eb3b5ccf Merge pull request #353 from fluxcd/build/remove-docs-hotfix
Remove documentation version hotfix
2020-10-17 10:05:00 +03:00
Hidde Beydals
7515080c63 Remove documentation version hotfix
And re-use the same logic to properly compare current and next release
versions in update workflow.
2020-10-16 22:19:58 +02:00
Hidde Beydals
07015c5172 Merge pull request #352 from fluxcd/refactor-create-reconcile-resume
Refactor create, reconcile and resume commands
2020-10-16 20:47:39 +02:00
Hidde Beydals
19918cd342 Refactor create, reconcile and resume cmds
* Take ObservedGeneration into account in readiness checks where
  applicable
* Reduce amount of code (and duplicate GETs) by working with pointers
  where possible
* Improve logged messages to properly take resource names into account
  and better describe processes
2020-10-16 20:17:39 +02:00
Stefan Prodan
16f0ed548e Add GitLab HTTPS auth to bootstrap options
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-16 20:57:21 +03:00
Stefan Prodan
0d1600275d Merge pull request #349 from fluxcd/windows-os-cli
Publish CLI binary for Windows
2020-10-16 17:30:59 +03:00
Stefan Prodan
768f32bd2d Publish CLI binary for Windows
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-16 16:32:11 +03:00
Stefan Prodan
d86798a7c6 Merge pull request #345 from staceypotter/patch-2
updated featured talks + upcoming meetups
2020-10-16 10:50:49 +03:00
Stefan Prodan
7718c6a2a5 Merge pull request #340 from fluxcd/bootstrap-validate-manifests
Enable client side validation on bootstrap
2020-10-16 10:41:47 +03:00
Stefan Prodan
a0616ac2cb Merge pull request #334 from StupidScience/windows-support
Get rid of shell-out and invoke command directly via exec
2020-10-16 10:41:31 +03:00
Stefan Prodan
23bbe0d52b Merge pull request #346 from staceypotter/patch-3
updated with featured talks + upcoming meetups
2020-10-16 10:41:07 +03:00
Stefan Prodan
6d9e7758a2 Merge pull request #347 from fluxcd/rebase-action
Add rebase action
2020-10-16 10:39:48 +03:00
Stefan Prodan
f81a80c2da Add rebase action
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-16 10:31:50 +03:00
Stefan Prodan
ab64619a41 Merge pull request #344 from smorimoto/fossa-action
Use fossa-contrib/fossa-action instead
2020-10-16 10:23:33 +03:00
Stacey Potter
18bdd26be8 updated with featured talks + upcoming meetups 2020-10-15 19:57:21 -07:00
Stacey Potter
c5f3a674d6 updated featured talks + upcoming meetups 2020-10-15 19:52:07 -07:00
Sora Morimoto
f95e17b144 Use fossa-contrib/fossa-action instead
Signed-off-by: Sora Morimoto <sora@morimoto.io>
2020-10-16 08:16:19 +09:00
“Anton
4989826124 Get rid of shell-out and invoke kubetcl directly via exec 2020-10-15 20:38:44 +03:00
Stefan Prodan
f23a551ae3 Enable client side validation on bootstrap
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-15 18:04:35 +03:00
Stefan Prodan
bd2df01b12 Merge pull request #343 from fluxcd/alert-provider-fix
Set alert provider secretRef only if specified
2020-10-15 18:03:23 +03:00
Stefan Prodan
880ef303e8 Set alert provider secretRef only if specified
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-15 17:51:43 +03:00
Michael Bridgen
b4ed1a0bdc Merge pull request #341 from fluxcd/update-image-auto-progress
Mark the design tasks for image automation as done
2020-10-15 12:23:33 +01:00
Michael Bridgen
7a2eae0699 Mark the design tasks for image automation as done
This leaves the implementation tasks as not done -- a bit of a
simplification, since there's some implementation done, and some
design left to do, but it's close enough at this level.

I estimated that having the design basics figured out is worth 30%
overall.
2020-10-15 12:12:04 +01:00
Stefan Prodan
4464252cae Merge pull request #337 from fluxcd/monitoring
Add cluster stats dashboard to monitoring stack
2020-10-14 17:12:30 +03:00
Stefan Prodan
ca8cf44200 Add cluster stats dashboard to docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-14 16:34:46 +03:00
Stefan Prodan
4565165579 Add cluster stats dashboard
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-14 16:16:56 +03:00
Stefan Prodan
a37be432a9 Merge pull request #333 from fluxcd/docs-update-v0.1.6
Update docs website
2020-10-14 12:11:30 +03:00
Stefan Prodan
90591e852d Update docs website
- add notification-controller/api commands to index
- move diagrams to docs website
- update CRDs docs

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-14 10:35:29 +03:00
Stefan Prodan
13f7d44a06 Merge pull request #327 from nanikjava/f-fix-306
Add ARM instruction to get started guide
2020-10-13 23:55:58 +03:00
Nanik
7dc8aa66a6 Add ARM instruction under staging and production bootstrap 2020-10-14 07:44:16 +11:00
Stefan Prodan
6cf28ab718 Merge pull request #331 from fluxcd/update-components
Update toolkit components
2020-10-13 23:41:39 +03:00
fluxcdbot
f461c5e8b7 Update toolkit components 2020-10-13 17:47:46 +00:00
Stefan Prodan
9433bdf4ad Merge pull request #332 from fluxcd/uninstall-fix
Uninstall improvements
2020-10-13 20:43:48 +03:00
Stefan Prodan
d2d494e079 Uninstall improvements
- ignore not found errors when deleting objects
- remove the CR/CRDs before deleting the cluster role binding
- capture kubectl exist code

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-13 20:30:17 +03:00
Stefan Prodan
e64b35cde8 Merge pull request #302 from phillebaba/feature/notification-controller
Add notification controller CLI commands
2020-10-13 18:31:16 +03:00
Philip Laine
d32e8c6e98 Remove aliases 2020-10-13 12:21:45 +02:00
Philip Laine
55cee488bc Update docs 2020-10-13 11:11:55 +02:00
Philip Laine
65b8942416 Fix minor issues 2020-10-13 11:11:55 +02:00
Philip Laine
94cf7c329c Update docs 2020-10-13 11:11:55 +02:00
Philip Laine
8eac7d6b4d Implement table output 2020-10-13 11:11:55 +02:00
Philip Laine
7ebb34de80 Add receiver command 2020-10-13 11:11:55 +02:00
Philip Laine
6ea84906ac Add alert commands 2020-10-13 11:11:54 +02:00
Philip Laine
f7971a871a Add alert provider commands 2020-10-13 11:11:54 +02:00
Stefan Prodan
54b35b7c2b Merge pull request #323 from fluxcd/monitoring
docs: Install the monitoring stack with gotk
2020-10-12 13:04:43 +03:00
Stefan Prodan
ca970b4ffb docs: Install the monitoring stack with gotk
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-12 09:50:43 +03:00
Stefan Prodan
8a96e32679 Update Prometheus and Grafana
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-11 15:13:16 +03:00
Stefan Prodan
fc4d01b3e5 Allow scraping and webhooks
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-11 13:48:06 +03:00
Stefan Prodan
b6afc8f6ce Merge pull request #317 from staceypotter/patch-1
Add featured talks and meetups section
2020-10-09 09:03:14 +03:00
Stefan Prodan
c481a431be Merge branch 'main' into patch-1 2020-10-09 08:54:45 +03:00
Stacey Potter
0a7b82793e removed header link
Removed "(Check out our [Upcoming Meetups](#upcoming-meetups)!)" from the top of the page.
2020-10-08 22:54:04 -07:00
Stacey Potter
3653236bcb added "featured talks" section
+ upcoming meetups & header link
2020-10-08 15:31:10 -07:00
Stefan Prodan
a2eee72015 Merge pull request #318 from fluxcd/install-pkg
Introduce install package
2020-10-08 14:01:25 +03:00
Stefan Prodan
5672646278 Use install pkg in CLI
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-08 12:47:25 +03:00
Stefan Prodan
c4d3fa7a48 init install pkg
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-08 02:31:33 +03:00
Stacey Potter
32b0267b9f Added GOTK guide walk-through
Added invite for Leigh's GOTK guide walk-through on Oct 19 (10am PT/18:00 BST) to the Community Section. LMK if this should be somewhere else - or condensed (wasn't sure how much info to include). Thanks! :)
2020-10-07 11:43:14 -07:00
Stefan Prodan
a7b41a4b8c Merge pull request #315 from rieger-jared/bug/update-notification-api-in-docs
Update api versions in guides
2020-10-07 18:10:38 +03:00
Jared Rieger
bfd6d14bf3 Update api versions in guides 2020-10-07 17:00:43 +02:00
Daniel Holbach
469de31218 Merge pull request #313 from dholbach/update-frontpage
close parentheses, make link to guide more obvious
2020-10-06 14:51:18 +02:00
Daniel Holbach
2ce9823b3a close parentheses, make link to guide more obvious 2020-10-06 14:34:21 +02:00
Stefan Prodan
e8b3d09ddf Merge pull request #310 from fluxcd/mod-cleanup
Use GitHub actions from fluxcd/pkg@main
2020-10-06 11:09:46 +03:00
stefanprodan
f613c01803 Update blang/semver to v4
Signed-off-by: stefanprodan <stefan.prodan@gmail.com>
2020-10-06 10:50:32 +03:00
stefanprodan
a5a5908fb5 Use GitHub actions from fluxcd/pkg@main 2020-10-06 10:50:02 +03:00
Hidde Beydals
5313a0ed47 Merge pull request #305 from fluxcd/docs/get-cmds-columns
Update guide to include column output commands
2020-10-05 18:26:51 +02:00
Hidde Beydals
1c15eebd7c Merge pull request #307 from fluxcd/bugfix-get-source
Display proper revision for sources
2020-10-05 18:26:28 +02:00
Hidde Beydals
f3cab6e177 Display proper revision for sources
Includes a change to an empty revision string if the reconciler has not
produced an artifact yet, as this will otherwise result in a nil
pointer dereference.
2020-10-05 18:16:22 +02:00
Hidde Beydals
c0623334ee Update guide to include column output commands 2020-10-05 18:11:01 +02:00
Hidde Beydals
d41bd6b6b1 Merge pull request #299 from circa10a/main
Switch get commands to use tables for output
2020-10-05 12:34:49 +02:00
Hidde Beydals
c9b4a8eef5 Merge pull request #301 from fluxcd/multi-context-kubeconfig
Support multi-path KUBECONFIG
2020-10-05 08:45:20 +02:00
circa10a
3619cb8bd1 Switch get commands to use tables for output
Signed-off-by: circa10a <caleblemoine@gmail.com>
2020-10-04 16:34:04 -05:00
Hidde Beydals
8e2316ba62 Support multi-path KUBECONFIG
Ref:
https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#the-kubeconfig-environment-variable
2020-10-04 18:22:03 +02:00
Stefan Prodan
495abf42ef Merge pull request #298 from yiannistri/sealed-secrets-docs
Update api versions in docs
2020-10-03 21:54:30 +03:00
Yiannis
5372dd633e Update api versions in docs 2020-10-03 19:08:16 +01:00
Stefan Prodan
5efa1ebe88 Merge pull request #297 from fluxcd/opt-out-network-policy
Add option to disable the network policy at install time
2020-10-03 19:15:00 +03:00
stefanprodan
07677ed4a7 Add option to disable the network policy at install time 2020-10-03 17:35:55 +03:00
Hidde Beydals
73e5640109 Merge pull request #295 from fluxcd/update-components
Update toolkit components
2020-10-02 20:39:49 +02:00
fluxcdbot
bdbded8588 Update toolkit components 2020-10-02 18:20:24 +00:00
Hidde Beydals
e0fbf8920d Merge pull request #290 from fluxcd/bug-get-break
Remove faulty `break` from get commands
2020-10-02 14:01:19 +02:00
Hidde Beydals
7b2227bfac Remove faulty break from get commands 2020-10-02 13:34:48 +02:00
Hidde Beydals
12866ca7ba Merge pull request #289 from fluxcd/fix-kustomization-depends-on
Fix Kusomization depends-on mapping
2020-10-02 13:01:47 +02:00
stefanprodan
1427b1537e Fix Kusomization depends-on mapping 2020-10-02 13:43:05 +03:00
Stefan Prodan
6ceb133bb5 Merge pull request #287 from fluxcd/go-1.15
Update Go to v1.15
2020-10-01 23:45:55 +03:00
stefanprodan
4ab67aaf90 Update Go to v1.15 2020-10-01 23:38:25 +03:00
Stefan Prodan
6cce0a3901 Merge pull request #286 from fluxcd/main-branch
Change default branch to main
2020-10-01 22:55:48 +03:00
stefanprodan
fa67789350 Change default branch to main 2020-10-01 22:45:45 +03:00
Stefan Prodan
16adeb1373 Merge pull request #285 from fluxcd/bootstrap-default-branch
Change the bootstrap default branch to main
2020-10-01 22:31:20 +03:00
stefanprodan
29c1cf1237 Retry setting annotation on conflict 2020-10-01 22:23:59 +03:00
stefanprodan
0e52065893 Change the bootstrap default branch to main 2020-10-01 21:47:33 +03:00
238 changed files with 8200 additions and 2719 deletions

View File

@@ -28,43 +28,43 @@ jobs:
id: vars id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Build - name: Build
run: sudo go build -o ./bin/gotk ./cmd/gotk run: sudo go build -o ./bin/flux ./cmd/flux
- name: bootstrap init - name: bootstrap init
run: | run: |
./bin/gotk bootstrap github --manifests ./manifests/install/ \ ./bin/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \ --owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \ --repository=flux-test-${{ steps.vars.outputs.sha_short }} \
--branch=main \ --branch=main \
--path=test-cluster --path=test-cluster
env: env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: bootstrap no-op - name: bootstrap no-op
run: | run: |
./bin/gotk bootstrap github --manifests ./manifests/install/ \ ./bin/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \ --owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \ --repository=flux-test-${{ steps.vars.outputs.sha_short }} \
--branch=main \ --branch=main \
--path=test-cluster --path=test-cluster
env: env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: uninstall - name: uninstall
run: | run: |
./bin/gotk suspend kustomization gotk-system ./bin/flux suspend kustomization flux-system
./bin/gotk uninstall --resources --crds -s ./bin/flux uninstall --resources --crds -s
- name: bootstrap reinstall - name: bootstrap reinstall
run: | run: |
./bin/gotk bootstrap github --manifests ./manifests/install/ \ ./bin/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \ --owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \ --repository=flux-test-${{ steps.vars.outputs.sha_short }} \
--branch=main \ --branch=main \
--path=test-cluster --path=test-cluster
env: env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: delete repository - name: delete repository
run: | run: |
./bin/gotk bootstrap github --manifests ./manifests/install/ \ ./bin/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \ --owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \ --repository=flux-test-${{ steps.vars.outputs.sha_short }} \
--branch=main \ --branch=main \
--path=test-cluster \ --path=test-cluster \
--delete --delete
@@ -73,6 +73,6 @@ jobs:
- name: Debug failure - name: Debug failure
if: failure() if: failure()
run: | run: |
kubectl -n gotk-system get all kubectl -n flux-system get all
kubectl -n gotk-system logs deploy/source-controller kubectl -n flux-system logs deploy/source-controller
kubectl -n gotk-system logs deploy/kustomize-controller kubectl -n flux-system logs deploy/kustomize-controller

View File

@@ -3,10 +3,7 @@ on:
push: push:
branches: branches:
- docs* - docs*
- roadmap* - main
- master
tags:
- '*'
jobs: jobs:
build: build:
@@ -18,18 +15,14 @@ jobs:
- name: Copy assets - name: Copy assets
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SOURCE_VER: ${{ 'v0.1.0' }}
KUSTOMIZE_VER: ${{ 'v0.1.0' }}
HELM_VER: ${{ 'v0.1.0' }}
NOTIFICATION_VER: ${{ 'v0.1.0' }}
run: | run: |
controller_version() { controller_version() {
sed -n "s/\(.*$1\/.*?ref=\)//p;n" "manifests/bases/$1/kustomization.yaml" sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml
} }
{ {
# source-controller CRDs # source-controller CRDs
# SOURCE_VER=$(controller_version source-controller) SOURCE_VER=$(controller_version source-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/api/source.md" > docs/components/source/api.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/api/source.md" > docs/components/source/api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/gitrepositories.md" > docs/components/source/gitrepositories.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/gitrepositories.md" > docs/components/source/gitrepositories.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/helmrepositories.md" > docs/components/source/helmrepositories.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/helmrepositories.md" > docs/components/source/helmrepositories.md
@@ -39,21 +32,21 @@ jobs:
{ {
# kustomize-controller CRDs # kustomize-controller CRDs
# KUSTOMIZE_VER=$(controller_version kustomize-controller) KUSTOMIZE_VER=$(controller_version kustomize-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/kustomize-controller/$KUSTOMIZE_VER/docs/api/kustomize.md" > docs/components/kustomize/api.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/kustomize-controller/$KUSTOMIZE_VER/docs/api/kustomize.md" > docs/components/kustomize/api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/kustomize-controller/$KUSTOMIZE_VER/docs/spec/v1beta1/kustomization.md" > docs/components/kustomize/kustomization.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/kustomize-controller/$KUSTOMIZE_VER/docs/spec/v1beta1/kustomization.md" > docs/components/kustomize/kustomization.md
} }
{ {
# helm-controller CRDs # helm-controller CRDs
# HELM_VER=$(controller_version helm-controller) HELM_VER=$(controller_version helm-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/helm-controller/$HELM_VER/docs/api/helmrelease.md" > docs/components/helm/api.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/helm-controller/$HELM_VER/docs/api/helmrelease.md" > docs/components/helm/api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/helm-controller/$HELM_VER/docs/spec/v2beta1/helmreleases.md" > docs/components/helm/helmreleases.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/helm-controller/$HELM_VER/docs/spec/v2beta1/helmreleases.md" > docs/components/helm/helmreleases.md
} }
{ {
# notification-controller CRDs # notification-controller CRDs
# NOTIFICATION_VER=$(controller_version notification-controller) NOTIFICATION_VER=$(controller_version notification-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/api/notification.md" > docs/components/notification/api.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/api/notification.md" > docs/components/notification/api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/event.md" > docs/components/notification/event.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/event.md" > docs/components/notification/event.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/alert.md" > docs/components/notification/alert.md curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/alert.md" > docs/components/notification/alert.md
@@ -63,7 +56,7 @@ jobs:
{ {
# install script # install script
cp install/gotk.sh docs/install.sh cp install/flux.sh docs/install.sh
} }
- name: Deploy docs - name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master uses: mhausenblas/mkdocs-deploy-gh-pages@master

View File

@@ -4,7 +4,7 @@ on:
pull_request: pull_request:
push: push:
branches: branches:
- master - main
jobs: jobs:
kind: kind:
@@ -20,9 +20,9 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-go- ${{ runner.os }}-go-
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2-beta uses: actions/setup-go@v2
with: with:
go-version: 1.14.x go-version: 1.15.x
- name: Setup Kubernetes - name: Setup Kubernetes
uses: engineerd/setup-kind@v0.4.0 uses: engineerd/setup-kind@v0.4.0
with: with:
@@ -37,31 +37,34 @@ jobs:
exit 1 exit 1
fi fi
- name: Build - name: Build
run: sudo go build -o ./bin/gotk ./cmd/gotk run: sudo go build -o ./bin/flux ./cmd/flux
- name: gotk check --pre - name: flux check --pre
run: | run: |
./bin/gotk check --pre ./bin/flux check --pre
- name: gotk install --manifests - name: flux install --manifests
run: | run: |
./bin/gotk install --manifests ./manifests/install/ ./bin/flux install --manifests ./manifests/install/
- name: gotk create source git - name: flux create source git
run: | run: |
./bin/gotk create source git podinfo \ ./bin/flux create source git podinfo \
--url https://github.com/stefanprodan/podinfo \ --url https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.3" --tag-semver=">=3.2.3"
- name: gotk create source git export apply - name: flux create source git export apply
run: | run: |
./bin/gotk create source git podinfo-export \ ./bin/flux create source git podinfo-export \
--url https://github.com/stefanprodan/podinfo \ --url https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.3" \ --tag-semver=">=3.2.3" \
--export | kubectl apply -f - --export | kubectl apply -f -
./bin/gotk delete source git podinfo-export --silent ./bin/flux delete source git podinfo-export --silent
- name: gotk get sources git - name: flux get sources git
run: | run: |
./bin/gotk get sources git ./bin/flux get sources git
- name: gotk create kustomization - name: flux get sources git --all-namespaces
run: | run: |
./bin/gotk create kustomization podinfo \ ./bin/flux get sources git --all-namespaces
- name: flux create kustomization
run: |
./bin/flux create kustomization podinfo \
--source=podinfo \ --source=podinfo \
--path="./deploy/overlays/dev" \ --path="./deploy/overlays/dev" \
--prune=true \ --prune=true \
@@ -70,74 +73,80 @@ jobs:
--health-check="Deployment/frontend.dev" \ --health-check="Deployment/frontend.dev" \
--health-check="Deployment/backend.dev" \ --health-check="Deployment/backend.dev" \
--health-check-timeout=3m --health-check-timeout=3m
- name: gotk reconcile kustomization --with-source - name: flux reconcile kustomization --with-source
run: | run: |
./bin/gotk reconcile kustomization podinfo --with-source ./bin/flux reconcile kustomization podinfo --with-source
- name: gotk get kustomizations - name: flux get kustomizations
run: | run: |
./bin/gotk get kustomizations ./bin/flux get kustomizations
- name: gotk suspend kustomization - name: flux get kustomizations --all-namespaces
run: | run: |
./bin/gotk suspend kustomization podinfo ./bin/flux get kustomizations --all-namespaces
- name: gotk resume kustomization - name: flux suspend kustomization
run: | run: |
./bin/gotk resume kustomization podinfo ./bin/flux suspend kustomization podinfo
- name: gotk export - name: flux resume kustomization
run: | run: |
./bin/gotk export source git --all ./bin/flux resume kustomization podinfo
./bin/gotk export kustomization --all - name: flux export
- name: gotk delete kustomization
run: | run: |
./bin/gotk delete kustomization podinfo --silent ./bin/flux export source git --all
- name: gotk create source helm ./bin/flux export kustomization --all
- name: flux delete kustomization
run: | run: |
./bin/gotk create source helm podinfo \ ./bin/flux delete kustomization podinfo --silent
- name: flux create source helm
run: |
./bin/flux create source helm podinfo \
--url https://stefanprodan.github.io/podinfo --url https://stefanprodan.github.io/podinfo
- name: gotk create helmrelease --source=HelmRepository/podinfo - name: flux create helmrelease --source=HelmRepository/podinfo
run: | run: |
./bin/gotk create hr podinfo-helm \ ./bin/flux create hr podinfo-helm \
--target-namespace=default \ --target-namespace=default \
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo \ --chart=podinfo \
--chart-version=">4.0.0 <5.0.0" --chart-version=">4.0.0 <5.0.0"
- name: gotk create helmrelease --source=GitRepository/podinfo - name: flux create helmrelease --source=GitRepository/podinfo
run: | run: |
./bin/gotk create hr podinfo-git \ ./bin/flux create hr podinfo-git \
--target-namespace=default \ --target-namespace=default \
--source=GitRepository/podinfo \ --source=GitRepository/podinfo \
--chart=./charts/podinfo --chart=./charts/podinfo
- name: gotk reconcile helmrelease --with-source - name: flux reconcile helmrelease --with-source
run: | run: |
./bin/gotk reconcile helmrelease podinfo-git --with-source ./bin/flux reconcile helmrelease podinfo-git --with-source
- name: gotk get helmreleases - name: flux get helmreleases
run: | run: |
./bin/gotk get helmreleases ./bin/flux get helmreleases
- name: gotk export helmrelease - name: flux get helmreleases --all-namespaces
run: | run: |
./bin/gotk export hr --all ./bin/flux get helmreleases --all-namespaces
- name: gotk delete helmrelease podinfo-helm - name: flux export helmrelease
run: | run: |
./bin/gotk delete hr podinfo-helm --silent ./bin/flux export hr --all
- name: gotk delete helmrelease podinfo-git - name: flux delete helmrelease podinfo-helm
run: | run: |
./bin/gotk delete hr podinfo-git --silent ./bin/flux delete hr podinfo-helm --silent
- name: gotk delete source helm - name: flux delete helmrelease podinfo-git
run: | run: |
./bin/gotk delete source helm podinfo --silent ./bin/flux delete hr podinfo-git --silent
- name: gotk delete source git - name: flux delete source helm
run: | run: |
./bin/gotk delete source git podinfo --silent ./bin/flux delete source helm podinfo --silent
- name: gotk check - name: flux delete source git
run: | run: |
./bin/gotk check ./bin/flux delete source git podinfo --silent
- name: gotk uninstall - name: flux check
run: | run: |
./bin/gotk uninstall --crds --silent ./bin/flux check
- name: flux uninstall
run: |
./bin/flux uninstall --crds --silent
- name: Debug failure - name: Debug failure
if: failure() if: failure()
run: | run: |
kubectl version --client --short kubectl version --client --short
kubectl -n gotk-system get all kubectl -n flux-system get all
kubectl -n gotk-system get kustomizations -oyaml kubectl -n flux-system get kustomizations -oyaml
kubectl -n gotk-system logs deploy/source-controller kubectl -n flux-system logs deploy/source-controller
kubectl -n gotk-system logs deploy/kustomize-controller kubectl -n flux-system logs deploy/kustomize-controller

View File

@@ -1,9 +1,9 @@
name: FOSSA name: FOSSA
on: on:
push: push:
branches: [master] branches: [main]
pull_request: pull_request:
branches: [master] branches: [main]
jobs: jobs:
build: build:
@@ -13,15 +13,13 @@ jobs:
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
with: with:
go-version: "^1.14.x" go-version: "^1.14.x"
- run: go version - name: Add GOPATH to GITHUB_ENV
# Runs a set of commands to initialize and analyze with FOSSA run: echo "GOPATH=$(go env GOPATH)" >>"$GITHUB_ENV"
- name: run FOSSA analysis - name: Add GOPATH to GITHUB_PATH
env: 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 Push-Only API Token
FOSSA_API_KEY: '5ee8bf422db1471e0bcf2bcb289185de' fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
run: | github-token: ${{ github.token }}
export GOPATH=$HOME/go
export PATH=$PATH:$(go env GOPATH)/bin
curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash
fossa init
fossa analyze

21
.github/workflows/rebase.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: rebase
on:
pull_request:
types: [opened]
issue_comment:
types: [created]
jobs:
rebase:
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') && (github.event.comment.author_association == 'CONTRIBUTOR' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER')
runs-on: ubuntu-latest
steps:
- name: Checkout the latest code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.3.1
env:
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.14.x go-version: 1.15.x
- name: Download release notes utility - name: Download release notes utility
env: env:
GH_REL_URL: https://github.com/buchanae/github-release-notes/releases/download/0.2.0/github-release-notes-linux-amd64-0.2.0.tar.gz GH_REL_URL: https://github.com/buchanae/github-release-notes/releases/download/0.2.0/github-release-notes-linux-amd64-0.2.0.tar.gz
@@ -25,8 +25,10 @@ jobs:
run: | run: |
echo 'CHANGELOG' > /tmp/release.txt echo 'CHANGELOG' > /tmp/release.txt
github-release-notes -org fluxcd -repo toolkit -since-latest-release -include-author >> /tmp/release.txt github-release-notes -org fluxcd -repo toolkit -since-latest-release -include-author >> /tmp/release.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@master uses: fluxcd/pkg//actions/kustomize@main
- name: Generate manifests tarball - name: Generate manifests tarball
run: | run: |
mkdir -p ./output mkdir -p ./output

View File

@@ -19,7 +19,7 @@ jobs:
bump_version() { bump_version() {
local RELEASE_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name') local RELEASE_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
local CURRENT_VERSION=$(sed -n "s/\(.*$1\/.*?ref=\)//p;n" "manifests/bases/$1/kustomization.yaml") local CURRENT_VERSION=$(sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml)
if [[ "${RELEASE_VERSION}" != "${CURRENT_VERSION}" ]]; then if [[ "${RELEASE_VERSION}" != "${CURRENT_VERSION}" ]]; then
# bump kustomize # bump kustomize

View File

@@ -1,10 +1,13 @@
builds: builds:
- main: ./cmd/gotk - <<: &build_defaults
ldflags: binary: flux
- -s -w -X main.VERSION={{ .Version }} main: ./cmd/flux
binary: gotk ldflags:
- -s -w -X main.VERSION={{ .Version }}
env:
- CGO_ENABLED=0
id: linux
goos: goos:
- darwin
- linux - linux
goarch: goarch:
- amd64 - amd64
@@ -12,23 +15,38 @@ builds:
- arm - arm
goarm: goarm:
- 7 - 7
env: - <<: *build_defaults
- CGO_ENABLED=0 id: darwin
goos:
- darwin
- <<: *build_defaults
id: windows
goos:
- windows
archives: archives:
- name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" - name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
id: nix
builds: [linux, darwin]
format: tar.gz
files:
- none*
- name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
id: windows
builds: [windows]
format: zip
files: files:
- none* - none*
brews: brews:
- name: gotk - name: flux
tap: tap:
owner: fluxcd owner: fluxcd
name: homebrew-tap name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
folder: Formula folder: Formula
homepage: "https://toolkit.fluxcd.io/" homepage: "https://toolkit.fluxcd.io/"
description: "GitOps Toolkit CLI" description: "Flux CLI"
dependencies: dependencies:
- name: kubectl - name: kubectl
type: optional type: optional
test: | test: |
system "#{bin}/gotk --version" system "#{bin}/flux --version"

View File

@@ -1,8 +1,10 @@
# Contributing # Contributing
The GitOps Toolkit is [Apache 2.0 licensed](https://github.com/fluxcd/toolkit/blob/master/LICENSE) Flux is [Apache 2.0
and accepts contributions via GitHub pull requests. This document outlines licensed](https://github.com/fluxcd/flux2/blob/main/LICENSE) and
some of the conventions on to make it easier to get your contribution accepted. accepts contributions via GitHub pull requests. This document outlines
some of the conventions on to make it easier to get your contribution
accepted.
We gratefully welcome improvements to issues and documentation as well as to We gratefully welcome improvements to issues and documentation as well as to
code. code.
@@ -23,7 +25,7 @@ join the [CNCF](https://slack.cncf.io/) Slack workspace and use the
[#flux-dev](https://cloud-native.slack.com/messages/flux-dev/) channel. [#flux-dev](https://cloud-native.slack.com/messages/flux-dev/) channel.
To discuss ideas and specifications we use [Github To discuss ideas and specifications we use [Github
Discussions](https://github.com/fluxcd/toolkit/discussions). Discussions](https://github.com/fluxcd/flux2/discussions).
For announcements we use a mailing list as well. Simply subscribe to For announcements we use a mailing list as well. Simply subscribe to
[flux-dev on cncf.io](https://lists.cncf.io/g/cncf-flux-dev) [flux-dev on cncf.io](https://lists.cncf.io/g/cncf-flux-dev)
@@ -31,14 +33,14 @@ to join the conversation (there you can also add calendar invites
to your Google calendar for our [Flux to your Google calendar for our [Flux
meeting](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view)). meeting](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view)).
## Understanding the GitOps Toolkit ## Understanding Flux and the GitOps Toolkit
If you are entirely new to the GitOps Toolkit, If you are entirely new to Flux and the GitOps Toolkit,
you might want to take a look at the [introductory talk and demo](https://www.youtube.com/watch?v=qQBtSkgl7tI). you might want to take a look at the [introductory talk and demo](https://www.youtube.com/watch?v=qQBtSkgl7tI).
This project is composed of: This project is composed of:
- [/f/toolkit](https://github.com/fluxcd/toolkit): The GitOps Toolkit CLI - [/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/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/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/helm-controller](https://github.com/fluxcd/helm-controller): Kubernetes operator for building GitOps pipelines with Helm

View File

@@ -1,4 +1,4 @@
VERSION?=$(shell grep 'VERSION' cmd/gotk/main.go | awk '{ print $$4 }' | tr -d '"') VERSION?=$(shell grep 'VERSION' cmd/flux/main.go | awk '{ print $$4 }' | tr -d '"')
all: test build all: test build
@@ -15,14 +15,15 @@ test: tidy fmt vet docs
go test ./... -coverprofile cover.out go test ./... -coverprofile cover.out
build: build:
CGO_ENABLED=0 go build -o ./bin/gotk ./cmd/gotk CGO_ENABLED=0 go build -o ./bin/flux ./cmd/flux
install: install:
go install cmd/gotk go install cmd/flux
.PHONY: docs .PHONY: docs
docs: docs:
mkdir -p ./docs/cmd && go run ./cmd/gotk/ docgen rm docs/cmd/*
mkdir -p ./docs/cmd && go run ./cmd/flux/ docgen
install-dev: install-dev:
CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/gotk CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/flux

View File

@@ -1,28 +1,31 @@
# GitOps Toolkit # Flux version 2
[![e2e](https://github.com/fluxcd/toolkit/workflows/e2e/badge.svg)](https://github.com/fluxcd/toolkit/actions) [![e2e](https://github.com/fluxcd/flux2/workflows/e2e/badge.svg)](https://github.com/fluxcd/flux2/actions)
[![report](https://goreportcard.com/badge/github.com/fluxcd/toolkit)](https://goreportcard.com/report/github.com/fluxcd/toolkit) [![report](https://goreportcard.com/badge/github.com/fluxcd/flux2)](https://goreportcard.com/report/github.com/fluxcd/flux2)
[![license](https://img.shields.io/github/license/fluxcd/toolkit.svg)](https://github.com/fluxcd/toolkit/blob/master/LICENSE) [![license](https://img.shields.io/github/license/fluxcd/flux2.svg)](https://github.com/fluxcd/flux2/blob/main/LICENSE)
[![release](https://img.shields.io/github/release/fluxcd/toolkit/all.svg)](https://github.com/fluxcd/toolkit/releases) [![release](https://img.shields.io/github/release/fluxcd/flux2/all.svg)](https://github.com/fluxcd/flux2/releases)
![overview](docs/diagrams/gitops-toolkit.png)
![overview](docs/diagrams/gotk-feature.png) Flux is a tool for keeping Kubernetes clusters in sync with sources of
configuration (like Git repositories), and automating updates to
configuration when there is new code to deploy.
The GitOps Toolkit is a set of composable APIs and specialized tools Flux version 2 ("v2") is built from the ground up to use Kubernetes'
that can be used to build a Continuous Delivery platform on top of Kubernetes. API extension system, and to integrate with Prometheus and other core
components of the Kubernetes ecosystem. In version 2, Flux supports
multi-tenancy and support for syncing an arbitrary number of Git
repositories, among other long-requested features.
These tools are build with Kubernetes controller-runtime libraries, and they Flux v2 is constructed with the [GitOps Toolkit](#gitops-toolkit), a
can be dynamically configured with Kubernetes custom resources either by set of composable APIs and specialized tools for building Continuous
cluster admins or by other automated tools. Delivery on top of Kubernetes.
The GitOps Toolkit components interact with each other via Kubernetes
events and are responsible for the reconciliation of their designated API objects.
## `gotk` installation ## Flux installation
With Homebrew: With Homebrew:
```sh ```sh
brew tap fluxcd/tap brew install fluxcd/tap/flux
brew install gotk
``` ```
With Bash: With Bash:
@@ -31,36 +34,48 @@ With Bash:
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
# enable completions in ~/.bash_profile # enable completions in ~/.bash_profile
. <(gotk completion) . <(flux completion bash)
``` ```
Binaries for macOS and Linux AMD64/ARM64 are available to download on the Binaries for macOS, Windows and Linux AMD64/ARM are available to download on the
[release page](https://github.com/fluxcd/toolkit/releases). [release page](https://github.com/fluxcd/flux2/releases).
Verify that your cluster satisfies the prerequisites with: Verify that your cluster satisfies the prerequisites with:
```sh ```sh
gotk check --pre flux check --pre
``` ```
## Get started ## Get started
To get started with the GitOps Toolkit, start [browsing the documentation](https://toolkit.fluxcd.io) To get started with Flux, start [browsing the
or get started with one of the following guides: documentation](https://toolkit.fluxcd.io) or get started with one of
the following guides:
- [Get started with GitOps Toolkit (deep dive)](https://toolkit.fluxcd.io/get-started/) - [Get started with Flux (deep dive)](https://toolkit.fluxcd.io/get-started/)
- [Installation](https://toolkit.fluxcd.io/guides/installation/) - [Installation](https://toolkit.fluxcd.io/guides/installation/)
- [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/) - [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/)
- [Setup Notifications](https://toolkit.fluxcd.io/guides/notifications/) - [Setup Notifications](https://toolkit.fluxcd.io/guides/notifications/)
- [Setup Webhook Receivers](https://toolkit.fluxcd.io/guides/webhook-receivers/) - [Setup Webhook Receivers](https://toolkit.fluxcd.io/guides/webhook-receivers/)
## Components ## GitOps Toolkit
The GitOps Toolkit is the set of APIs and controllers that make up the
runtime for Flux v2. The APIs comprise Kubernetes custom resources,
which can be created and updated by a cluster user, or by other
automation tooling.
You can use the toolkit to extend Flux, or to build your own systems
for continuous delivery -- see [the developer
guides](https://toolkit.fluxcd.io/dev-guides/source-watcher/).
### Components
- [Toolkit CLI](https://toolkit.fluxcd.io/cmd/gotk/)
- [Source Controller](https://toolkit.fluxcd.io/components/source/controller/) - [Source Controller](https://toolkit.fluxcd.io/components/source/controller/)
- [GitRepository CRD](https://toolkit.fluxcd.io/components/source/gitrepositories/) - [GitRepository CRD](https://toolkit.fluxcd.io/components/source/gitrepositories/)
- [HelmRepository CRD](https://toolkit.fluxcd.io/components/source/helmrepositories/) - [HelmRepository CRD](https://toolkit.fluxcd.io/components/source/helmrepositories/)
- [HelmChart CRD](https://toolkit.fluxcd.io/components/source/helmcharts/) - [HelmChart CRD](https://toolkit.fluxcd.io/components/source/helmcharts/)
- [Bucket CRD](https://toolkit.fluxcd.io/components/source/buckets/)
- [Kustomize Controller](https://toolkit.fluxcd.io/components/kustomize/controller/) - [Kustomize Controller](https://toolkit.fluxcd.io/components/kustomize/controller/)
- [Kustomization CRD](https://toolkit.fluxcd.io/components/kustomize/kustomization/) - [Kustomization CRD](https://toolkit.fluxcd.io/components/kustomize/kustomization/)
- [Helm Controller](https://toolkit.fluxcd.io/components/helm/controller/) - [Helm Controller](https://toolkit.fluxcd.io/components/helm/controller/)
@@ -72,13 +87,29 @@ or get started with one of the following guides:
## Community ## Community
The GitOps Toolkit 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: 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:
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view)) - 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/) - Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
- Join the [planning discussions](https://github.com/fluxcd/toolkit/discussions) - Join the [planning discussions](https://github.com/fluxcd/flux2/discussions)
- And if you are completely new to the GitOps Toolkit, take a look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback - 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). - 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/index.md) to the project - Check out [how to contribute](CONTRIBUTING.md) to the project
### Featured Talks
- 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)
- 4 Sep 2020 - [KubeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
- 25 June 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
- 7 May 2020 - [GitOps Days - Community Special: GitOps Toolkit Experimentation with Stefan Prodan](https://youtu.be/WHzxunv4DKk?t=6521)
### Upcoming Events
- 2 Nov 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 2 with Leigh Capili](https://www.meetup.com/GitOps-Community/events/273934676/)
- 12-13 Nov 2020 - [GitOps Days EMEA](https://www.gitopsdays.com/) with talks and workshops on migrating to Flux v2 and Helm Controller
- 19 Nov 2020 - [KubeCon NA: Progressive Delivery Techniques with Flagger and Flux v2 with Stefan Prodan](https://kccncna20.sched.com/event/1b04f8408b49976b843a5d0019cb8112)
We are looking forward to seeing you with us! We are looking forward to seeing you with us!

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,10 +20,7 @@ import (
"context" "context"
"fmt" "fmt"
"net/url" "net/url"
"os"
"path"
"path/filepath" "path/filepath"
"strings"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -32,10 +29,14 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
) )
var bootstrapCmd = &cobra.Command{ var bootstrapCmd = &cobra.Command{
@@ -49,53 +50,44 @@ var (
bootstrapComponents []string bootstrapComponents []string
bootstrapRegistry string bootstrapRegistry string
bootstrapImagePullSecret string bootstrapImagePullSecret string
bootstrapArch string
bootstrapBranch string bootstrapBranch string
bootstrapWatchAllNamespaces bool bootstrapWatchAllNamespaces bool
bootstrapLogLevel string bootstrapNetworkPolicy bool
bootstrapManifestsPath string bootstrapManifestsPath string
bootstrapArch = flags.Arch(defaults.Arch)
bootstrapLogLevel = flags.LogLevel(defaults.LogLevel)
bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"} bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"}
) )
const ( const (
bootstrapDefaultBranch = "master" bootstrapDefaultBranch = "main"
bootstrapInstallManifest = "toolkit-components.yaml"
bootstrapSourceManifest = "toolkit-source.yaml"
bootstrapKustomizationManifest = "toolkit-kustomization.yaml"
) )
func init() { func init() {
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapVersion, "version", "v", defaultVersion, bootstrapCmd.PersistentFlags().StringVarP(&bootstrapVersion, "version", "v", defaults.Version,
"toolkit version") "toolkit version")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapComponents, "components", defaultComponents, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapComponents, "components", defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapRegistry, "registry", "ghcr.io/fluxcd", bootstrapCmd.PersistentFlags().StringVar(&bootstrapRegistry, "registry", "ghcr.io/fluxcd",
"container registry where the toolkit images are published") "container registry where the toolkit images are published")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapImagePullSecret, "image-pull-secret", "", bootstrapCmd.PersistentFlags().StringVar(&bootstrapImagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry") "Kubernetes secret name used for pulling the toolkit images from a private registry")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArch, "arch", "amd64", bootstrapCmd.PersistentFlags().Var(&bootstrapArch, "arch", bootstrapArch.Description())
"arch can be amd64 or arm64")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapBranch, "branch", bootstrapDefaultBranch, bootstrapCmd.PersistentFlags().StringVar(&bootstrapBranch, "branch", bootstrapDefaultBranch,
"default branch (for GitHub this must match the default branch setting for the organization)") "default branch (for GitHub this must match the default branch setting for the organization)")
rootCmd.AddCommand(bootstrapCmd) rootCmd.AddCommand(bootstrapCmd)
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapWatchAllNamespaces, "watch-all-namespaces", true, bootstrapCmd.PersistentFlags().BoolVar(&bootstrapWatchAllNamespaces, "watch-all-namespaces", true,
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed") "watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapLogLevel, "log-level", "info", "set the controllers log level") bootstrapCmd.PersistentFlags().BoolVar(&bootstrapNetworkPolicy, "network-policy", true,
"deny ingress access to the toolkit controllers from other namespaces using network policies")
bootstrapCmd.PersistentFlags().Var(&bootstrapLogLevel, "log-level", bootstrapLogLevel.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory") bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory")
bootstrapCmd.PersistentFlags().MarkHidden("manifests") bootstrapCmd.PersistentFlags().MarkHidden("manifests")
} }
func bootstrapValidate() error { func bootstrapValidate() error {
if !utils.containsItemString(supportedArch, bootstrapArch) {
return fmt.Errorf("arch %s is not supported, can be %v", bootstrapArch, supportedArch)
}
if !utils.containsItemString(supportedLogLevels, bootstrapLogLevel) {
return fmt.Errorf("log level %s is not supported, can be %v", bootstrapLogLevel, supportedLogLevels)
}
for _, component := range bootstrapRequiredComponents { for _, component := range bootstrapRequiredComponents {
if !utils.containsItemString(bootstrapComponents, component) { if !utils.ContainsItemString(bootstrapComponents, component) {
return fmt.Errorf("component %s is required", component) return fmt.Errorf("component %s is required", component)
} }
} }
@@ -104,50 +96,49 @@ func bootstrapValidate() error {
} }
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) { func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
manifestsDir := path.Join(tmpDir, targetPath, namespace) opts := install.Options{
if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil { BaseURL: localManifests,
return "", fmt.Errorf("creating manifests dir failed: %w", err) Version: bootstrapVersion,
} Namespace: namespace,
manifest := path.Join(manifestsDir, bootstrapInstallManifest) Components: bootstrapComponents,
Registry: bootstrapRegistry,
if localManifests != "" { ImagePullSecret: bootstrapImagePullSecret,
if err := buildKustomization(localManifests, manifest); err != nil { Arch: bootstrapArch.String(),
return "", fmt.Errorf("build kustomization failed: %w", err) WatchAllNamespaces: bootstrapWatchAllNamespaces,
} NetworkPolicy: bootstrapNetworkPolicy,
LogLevel: bootstrapLogLevel.String(),
return manifest, nil NotificationController: defaults.NotificationController,
ManifestFile: defaults.ManifestFile,
Timeout: timeout,
TargetPath: targetPath,
} }
gotkDir := path.Join(tmpDir, ".gotk") if localManifests == "" {
defer os.RemoveAll(gotkDir) opts.BaseURL = defaults.BaseURL
if err := os.MkdirAll(gotkDir, os.ModePerm); err != nil {
return "", fmt.Errorf("generating manifests failed: %w", err)
} }
if err := genInstallManifests(bootstrapVersion, namespace, bootstrapComponents, output, err := install.Generate(opts)
bootstrapWatchAllNamespaces, bootstrapRegistry, bootstrapImagePullSecret, if err != nil {
bootstrapArch, bootstrapLogLevel, gotkDir); err != nil { return "", fmt.Errorf("generating install manifests failed: %w", err)
return "", fmt.Errorf("generating manifests failed: %w", err)
} }
if err := buildKustomization(gotkDir, manifest); err != nil { if filePath, err := output.WriteFile(tmpDir); err != nil {
return "", fmt.Errorf("build kustomization failed: %w", err) return "", fmt.Errorf("generating install manifests failed: %w", err)
} else {
return filePath, nil
} }
return manifest, nil
} }
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error { func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
command := fmt.Sprintf("kubectl apply -f %s", manifestPath) kubectlArgs := []string{"apply", "-f", manifestPath}
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil { if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
return fmt.Errorf("install failed") return fmt.Errorf("install failed")
} }
for _, deployment := range components { for _, deployment := range components {
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s", kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
namespace, deployment, timeout.String()) if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
return fmt.Errorf("install failed") return fmt.Errorf("install failed")
} }
} }
@@ -155,72 +146,26 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
} }
func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) error { func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind) opts := sync.Options{
gitRepository := sourcev1.GitRepository{ Name: name,
TypeMeta: metav1.TypeMeta{ Namespace: namespace,
Kind: gvk.Kind, URL: url,
APIVersion: gvk.GroupVersion().String(), Branch: branch,
}, Interval: interval,
ObjectMeta: metav1.ObjectMeta{ TargetPath: targetPath,
Name: name, ManifestFile: sync.MakeDefaultOptions().ManifestFile,
Namespace: namespace,
},
Spec: sourcev1.GitRepositorySpec{
URL: url,
Interval: metav1.Duration{
Duration: interval,
},
Reference: &sourcev1.GitRepositoryRef{
Branch: branch,
},
SecretRef: &corev1.LocalObjectReference{
Name: name,
},
},
} }
gitData, err := yaml.Marshal(gitRepository) manifest, err := sync.Generate(opts)
if err != nil { if err != nil {
return fmt.Errorf("generating install manifests failed: %w", err)
}
if _, err := manifest.WriteFile(tmpDir); err != nil {
return err return err
} }
if err := utils.writeFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, bootstrapSourceManifest)); err != nil { if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
return err
}
gvk = kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)
kustomization := kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{
Duration: 10 * time.Minute,
},
Path: fmt.Sprintf("./%s", strings.TrimPrefix(targetPath, "./")),
Prune: true,
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourcev1.GitRepositoryKind,
Name: name,
},
},
}
ksData, err := yaml.Marshal(kustomization)
if err != nil {
return err
}
if err := utils.writeFile(string(ksData), filepath.Join(tmpDir, targetPath, namespace, bootstrapKustomizationManifest)); err != nil {
return err
}
if err := utils.generateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
return err return err
} }
@@ -228,20 +173,22 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
} }
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error { func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error {
command := fmt.Sprintf("kubectl apply -k %s", filepath.Join(tmpDir, targetPath, namespace)) kubectlArgs := []string{"apply", "-k", filepath.Join(tmpDir, targetPath, namespace)}
if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil { if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubectlArgs...); err != nil {
return err return err
} }
logger.Waitingf("waiting for cluster sync") logger.Waitingf("waiting for cluster sync")
var gitRepository sourcev1.GitRepository
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil {
return err return err
} }
var kustomization kustomizev1.Kustomization
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil { isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/git" "github.com/fluxcd/pkg/git"
) )
@@ -42,22 +43,22 @@ the bootstrap command will perform an upgrade if needed.`,
export GITHUB_TOKEN=<my-token> export GITHUB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitHub organization # Run bootstrap for a private repo owned by a GitHub organization
gotk bootstrap github --owner=<organization> --repository=<repo name> flux bootstrap github --owner=<organization> --repository=<repo name>
# Run bootstrap for a private repo and assign organization teams to it # Run bootstrap for a private repo and assign organization teams to it
gotk bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug> flux bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
# Run bootstrap for a repository path # Run bootstrap for a repository path
gotk bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster flux bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account # Run bootstrap for a public repository on a personal account
gotk bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true flux bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on GitHub Enterprise # Run bootstrap for a private repo hosted on GitHub Enterprise
gotk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a an existing repository with a branch named main # Run bootstrap for a an existing repository with a branch named main
gotk bootstrap github --owner=<organization> --repository=<repo name> --branch=main flux bootstrap github --owner=<organization> --repository=<repo name> --branch=main
`, `,
RunE: bootstrapGitHubCmdRun, RunE: bootstrapGitHubCmdRun,
} }
@@ -104,7 +105,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
repository, err := git.NewRepository(ghRepository, ghOwner, ghHostname, ghToken, "gotk", ghOwner+"@users.noreply.github.com") repository, err := git.NewRepository(ghRepository, ghOwner, ghHostname, ghToken, "flux", ghOwner+"@users.noreply.github.com")
if err != nil { if err != nil {
return err return err
} }
@@ -183,7 +184,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("components are up to date") logger.Successf("components are up to date")
} }
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -213,9 +214,9 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("generating deploy key failed: %w", err) return fmt.Errorf("generating deploy key failed: %w", err)
} }
keyName := "gotk" keyName := "flux"
if ghPath != "" { if ghPath != "" {
keyName = fmt.Sprintf("gotk-%s", ghPath) keyName = fmt.Sprintf("flux-%s", ghPath)
} }
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil { if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -26,7 +26,10 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/git" "github.com/fluxcd/pkg/git"
) )
@@ -41,20 +44,23 @@ the bootstrap command will perform an upgrade if needed.`,
Example: ` # Create a GitLab API token and export it as an env var Example: ` # Create a GitLab API token and export it as an env var
export GITLAB_TOKEN=<my-token> export GITLAB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitLab group # Run bootstrap for a private repo using HTTPS token authentication
gotk bootstrap gitlab --owner=<group> --repository=<repo name> flux bootstrap gitlab --owner=<group> --repository=<repo name>
# Run bootstrap for a private repo using SSH authentication
flux bootstrap gitlab --owner=<group> --repository=<repo name> --ssh-hostname=gitlab.com
# Run bootstrap for a repository path # Run bootstrap for a repository path
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster flux bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account # Run bootstrap for a public repository on a personal account
gotk bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true flux bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on a GitLab server # Run bootstrap for a private repo hosted on a GitLab server
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain> flux bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a an existing repository with a branch named main # Run bootstrap for a an existing repository with a branch named main
gotk bootstrap gitlab --owner=<organization> --repository=<repo name> --branch=main flux bootstrap gitlab --owner=<organization> --repository=<repo name> --branch=main
`, `,
RunE: bootstrapGitLabCmdRun, RunE: bootstrapGitLabCmdRun,
} }
@@ -77,7 +83,7 @@ func init() {
bootstrapGitLabCmd.Flags().BoolVar(&glPrivate, "private", true, "is private repository") bootstrapGitLabCmd.Flags().BoolVar(&glPrivate, "private", true, "is private repository")
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval") bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname") bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, defaults to hostname if not specified") bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, when specified a deploy key will be added to the repository")
bootstrapGitLabCmd.Flags().StringVar(&glPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path") bootstrapGitLabCmd.Flags().StringVar(&glPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
bootstrapCmd.AddCommand(bootstrapGitLabCmd) bootstrapCmd.AddCommand(bootstrapGitLabCmd)
@@ -93,7 +99,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
repository, err := git.NewRepository(glRepository, glOwner, glHostname, glToken, "gotk", glOwner+"@users.noreply.gitlab.com") repository, err := git.NewRepository(glRepository, glOwner, glHostname, glToken, "flux", glOwner+"@users.noreply.gitlab.com")
if err != nil { if err != nil {
return err return err
} }
@@ -107,7 +113,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
IsPersonal: glPersonal, IsPersonal: glPersonal,
} }
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -172,34 +178,54 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("install completed") logger.Successf("install completed")
} }
// setup SSH deploy key repoURL := repository.GetURL()
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 glSSHHostname != "" {
if err != nil { // setup SSH deploy key
return fmt.Errorf("generating deploy key failed: %w", err) 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)
}
keyName := "gotk" key, err := generateDeployKey(ctx, kubeClient, u, namespace)
if glPath != "" { if err != nil {
keyName = fmt.Sprintf("gotk-%s", glPath) return fmt.Errorf("generating deploy key failed: %w", err)
} }
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil { keyName := "flux"
if glPath != "" {
keyName = fmt.Sprintf("flux-%s", glPath)
}
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
return err
} else if changed {
logger.Successf("deploy key configured")
}
}
} else {
// setup HTTPS token auth
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
Namespace: namespace,
},
StringData: map[string]string{
"username": "git",
"password": glToken,
},
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err return err
} else if changed {
logger.Successf("deploy key configured")
} }
} }
// configure repo synchronization // configure repo synchronization
logger.Actionf("generating sync manifests") logger.Actionf("generating sync manifests")
if err := generateSyncManifests(repository.GetSSH(), bootstrapBranch, namespace, namespace, glPath, tmpDir, glInterval); err != nil { if err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, glPath, tmpDir, glInterval); err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,13 +18,15 @@ package main
import ( import (
"context" "context"
"fmt" "encoding/json"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"github.com/blang/semver" "github.com/blang/semver/v4"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra" "github.com/spf13/cobra"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
) )
@@ -35,10 +37,10 @@ var checkCmd = &cobra.Command{
Long: `The check command will perform a series of checks to validate that Long: `The check command will perform a series of checks to validate that
the local environment is configured correctly and if the installed components are healthy.`, the local environment is configured correctly and if the installed components are healthy.`,
Example: ` # Run pre-installation checks Example: ` # Run pre-installation checks
gotk check --pre flux check --pre
# Run installation checks # Run installation checks
gotk check flux check
`, `,
RunE: runCheckCmd, RunE: runCheckCmd,
} }
@@ -48,10 +50,14 @@ var (
checkComponents []string checkComponents []string
) )
type kubectlVersion struct {
ClientVersion *apimachineryversion.Info `json:"clientVersion"`
}
func init() { func init() {
checkCmd.Flags().BoolVarP(&checkPre, "pre", "", false, checkCmd.Flags().BoolVarP(&checkPre, "pre", "", false,
"only run pre-installation checks") "only run pre-installation checks")
checkCmd.Flags().StringSliceVar(&checkComponents, "components", defaultComponents, checkCmd.Flags().StringSliceVar(&checkComponents, "components", defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
rootCmd.AddCommand(checkCmd) rootCmd.AddCommand(checkCmd)
} }
@@ -97,14 +103,20 @@ func kubectlCheck(ctx context.Context, version string) bool {
return false return false
} }
command := "kubectl version --client --short | awk '{ print $3 }'" kubectlArgs := []string{"version", "--client", "--output", "json"}
output, err := utils.execCommand(ctx, ModeCapture, command) output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubectlArgs...)
if err != nil { if err != nil {
logger.Failuref("kubectl version can't be determined") logger.Failuref("kubectl version can't be determined")
return false return false
} }
v, err := semver.ParseTolerant(output) kv := &kubectlVersion{}
if err = json.Unmarshal([]byte(output), kv); err != nil {
logger.Failuref("kubectl version output can't be unmarshaled")
return false
}
v, err := semver.ParseTolerant(kv.ClientVersion.GitVersion)
if err != nil { if err != nil {
logger.Failuref("kubectl version can't be parsed") logger.Failuref("kubectl version can't be parsed")
return false return false
@@ -161,9 +173,8 @@ func componentsCheck() bool {
ok := true ok := true
for _, deployment := range checkComponents { for _, deployment := range checkComponents {
command := fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s", kubectlArgs := []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
namespace, deployment, timeout.String()) if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubectlArgs...); err != nil {
if output, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n")) logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
ok = false ok = false
} else { } else {

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -27,12 +27,12 @@ var completionBashCmd = &cobra.Command{
Short: "Generates bash completion scripts", Short: "Generates bash completion scripts",
Example: `To load completion run Example: `To load completion run
. <(gotk completion bash) . <(flux completion bash)
To configure your bash shell to load completions for each session add to your bashrc To configure your bash shell to load completions for each session add to your bashrc
# ~/.bashrc or ~/.profile # ~/.bashrc or ~/.profile
command -v gotk >/dev/null && . <(gotk completion bash) command -v flux >/dev/null && . <(flux completion bash)
`, `,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenBashCompletion(os.Stdout) rootCmd.GenBashCompletion(os.Stdout)

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -27,11 +27,11 @@ var completionFishCmd = &cobra.Command{
Short: "Generates fish completion scripts", Short: "Generates fish completion scripts",
Example: `To load completion run Example: `To load completion run
. <(gotk completion fish) . <(flux completion fish)
To configure your fish shell to load completions for each session write this script to your completions dir: To configure your fish shell to load completions for each session write this script to your completions dir:
gotk completion fish > ~/.config/fish/completions/gotk flux completion fish > ~/.config/fish/completions/flux
See http://fishshell.com/docs/current/index.html#completion-own for more details See http://fishshell.com/docs/current/index.html#completion-own for more details
`, `,

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -27,19 +27,19 @@ var completionPowerShellCmd = &cobra.Command{
Short: "Generates powershell completion scripts", Short: "Generates powershell completion scripts",
Example: `To load completion run Example: `To load completion run
. <(gotk completion powershell) . <(flux completion powershell)
To configure your powershell shell to load completions for each session add to your powershell profile To configure your powershell shell to load completions for each session add to your powershell profile
Windows: Windows:
cd "$env:USERPROFILE\Documents\WindowsPowerShell\Modules" cd "$env:USERPROFILE\Documents\WindowsPowerShell\Modules"
gotk completion >> gotk-completion.ps1 flux completion >> flux-completion.ps1
Linux: Linux:
cd "${XDG_CONFIG_HOME:-"$HOME/.config/"}/powershell/modules" cd "${XDG_CONFIG_HOME:-"$HOME/.config/"}/powershell/modules"
gotk completion >> gotk-completions.ps1 flux completion >> flux-completions.ps1
`, `,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenPowerShellCompletion(os.Stdout) rootCmd.GenPowerShellCompletion(os.Stdout)

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -27,20 +27,20 @@ var completionZshCmd = &cobra.Command{
Short: "Generates zsh completion scripts", Short: "Generates zsh completion scripts",
Example: `To load completion run Example: `To load completion run
. <(gotk completion zsh) && compdef _gotk gotk . <(flux completion zsh) && compdef _flux flux
To configure your zsh shell to load completions for each session add to your zshrc To configure your zsh shell to load completions for each session add to your zshrc
# ~/.zshrc or ~/.profile # ~/.zshrc or ~/.profile
command -v gotk >/dev/null && . <(gotk completion zsh) && compdef _gotk gotk command -v flux >/dev/null && . <(flux completion zsh) && compdef _flux flux
or write a cached file in one of the completion directories in your ${fpath}: or write a cached file in one of the completion directories in your ${fpath}:
echo "${fpath// /\n}" | grep -i completion echo "${fpath// /\n}" | grep -i completion
gotk completions zsh > _gotk flux completions zsh > _flux
mv _gotk ~/.oh-my-zsh/completions # oh-my-zsh mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
mv _gotk ~/.zprezto/modules/completion/external/src/ # zprezto mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
`, `,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenZshCompletion(os.Stdout) rootCmd.GenZshCompletion(os.Stdout)

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

192
cmd/flux/create_alert.go Normal file
View File

@@ -0,0 +1,192 @@
/*
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 (
"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"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var createAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Create or update a Alert resource",
Long: "The create alert command generates a Alert resource.",
Example: ` # Create an Alert for kustomization events
flux create alert \
--event-severity info \
--event-source Kustomization/flux-system \
--provider-ref slack \
flux-system
`,
RunE: createAlertCmdRun,
}
var (
aProviderRef string
aEventSeverity string
aEventSources []string
)
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>)")
createCmd.AddCommand(createAlertCmd)
}
func createAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Alert name is required")
}
name := args[0]
if aProviderRef == "" {
return fmt.Errorf("provider ref is required")
}
eventSources := []notificationv1.CrossNamespaceObjectReference{}
for _, eventSource := range aEventSources {
kind, name := utils.ParseObjectKindName(eventSource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
}
eventSources = append(eventSources, notificationv1.CrossNamespaceObjectReference{
Kind: kind,
Name: name,
})
}
if len(eventSources) == 0 {
return fmt.Errorf("at least one event source is required")
}
sourceLabels, err := parseLabels()
if err != nil {
return err
}
if !export {
logger.Generatef("generating Alert")
}
alert := notificationv1.Alert{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: sourceLabels,
},
Spec: notificationv1.AlertSpec{
ProviderRef: corev1.LocalObjectReference{
Name: aProviderRef,
},
EventSeverity: aEventSeverity,
EventSources: eventSources,
Suspend: false,
},
}
if export {
return exportAlert(alert)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying Alert")
namespacedName, err := upsertAlert(ctx, kubeClient, &alert)
if err != nil {
return err
}
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("Alert %s is ready", name)
return nil
}
func upsertAlert(ctx context.Context, kubeClient client.Client,
alert *notificationv1.Alert) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: alert.GetNamespace(),
Name: alert.GetName(),
}
var existing notificationv1.Alert
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, alert); err != nil {
return namespacedName, err
} else {
logger.Successf("Alert created")
return namespacedName, nil
}
}
return namespacedName, err
}
existing.Labels = alert.Labels
existing.Spec = alert.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return namespacedName, err
}
alert = &existing
logger.Successf("Alert updated")
return namespacedName, nil
}
func isAlertReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, alert)
if err != nil {
return false, err
}
if c := meta.GetCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -0,0 +1,190 @@
/*
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 (
"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"
"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"
)
var createAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Create or update a Provider resource",
Long: "The create alert-provider command generates a Provider resource.",
Example: ` # Create a Provider for a Slack channel
flux create alert-provider slack \
--type slack \
--channel general \
--address https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--secret-ref webhook-url
# Create a Provider for a Github repository
flux create alert-provider github-podinfo \
--type github \
--address https://github.com/stefanprodan/podinfo \
--secret-ref github-token
`,
RunE: createAlertProviderCmdRun,
}
var (
apType string
apChannel string
apUsername string
apAddress string
apSecretRef string
)
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")
createCmd.AddCommand(createAlertProviderCmd)
}
func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Provider name is required")
}
name := args[0]
if apType == "" {
return fmt.Errorf("Provider type is required")
}
sourceLabels, err := parseLabels()
if err != nil {
return err
}
if !export {
logger.Generatef("generating Provider")
}
provider := notificationv1.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: sourceLabels,
},
Spec: notificationv1.ProviderSpec{
Type: apType,
Channel: apChannel,
Username: apUsername,
Address: apAddress,
},
}
if apSecretRef != "" {
provider.Spec.SecretRef = &corev1.LocalObjectReference{
Name: apSecretRef,
}
}
if export {
return exportAlertProvider(provider)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying Provider")
namespacedName, err := upsertAlertProvider(ctx, kubeClient, &provider)
if err != nil {
return err
}
logger.Waitingf("waiting for Provider reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
return err
}
logger.Successf("Provider %s is ready", name)
return nil
}
func upsertAlertProvider(ctx context.Context, kubeClient client.Client,
provider *notificationv1.Provider) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: provider.GetNamespace(),
Name: provider.GetName(),
}
var existing notificationv1.Provider
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, provider); err != nil {
return namespacedName, err
} else {
logger.Successf("Provider created")
return namespacedName, nil
}
}
return namespacedName, err
}
existing.Labels = provider.Labels
existing.Spec = provider.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return namespacedName, err
}
provider = &existing
logger.Successf("Provider updated")
return namespacedName, nil
}
func isAlertProviderReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, provider *notificationv1.Provider) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, provider)
if err != nil {
return false, err
}
if c := meta.GetCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,14 +19,15 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/pkg/apis/meta"
"io/ioutil" "io/ioutil"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@@ -34,7 +35,6 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var createHelmReleaseCmd = &cobra.Command{ var createHelmReleaseCmd = &cobra.Command{
@@ -43,44 +43,44 @@ var createHelmReleaseCmd = &cobra.Command{
Short: "Create or update a HelmRelease resource", Short: "Create or update a HelmRelease resource",
Long: "The helmrelease create command generates a HelmRelease resource for a given HelmRepository source.", Long: "The helmrelease create command generates a HelmRelease resource for a given HelmRepository source.",
Example: ` # Create a HelmRelease with a chart from a HelmRepository source Example: ` # Create a HelmRelease with a chart from a HelmRepository source
gotk create hr podinfo \ flux create hr podinfo \
--interval=10m \ --interval=10m \
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo \ --chart=podinfo \
--chart-version=">4.0.0" --chart-version=">4.0.0"
# Create a HelmRelease with a chart from a GitRepository source # Create a HelmRelease with a chart from a GitRepository source
gotk create hr podinfo \ flux create hr podinfo \
--interval=10m \ --interval=10m \
--source=GitRepository/podinfo \ --source=GitRepository/podinfo \
--chart=./charts/podinfo --chart=./charts/podinfo
# Create a HelmRelease with a chart from a Bucket source # Create a HelmRelease with a chart from a Bucket source
gotk create hr podinfo \ flux create hr podinfo \
--interval=10m \ --interval=10m \
--source=Bucket/podinfo \ --source=Bucket/podinfo \
--chart=./charts/podinfo --chart=./charts/podinfo
# Create a HelmRelease with values from a local YAML file # Create a HelmRelease with values from a local YAML file
gotk create hr podinfo \ flux create hr podinfo \
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo \ --chart=podinfo \
--values=./my-values.yaml --values=./my-values.yaml
# Create a HelmRelease with a custom release name # Create a HelmRelease with a custom release name
gotk create hr podinfo \ flux create hr podinfo \
--release-name=podinfo-dev --release-name=podinfo-dev
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo \ --chart=podinfo \
# Create a HelmRelease targeting another namespace than the resource # Create a HelmRelease targeting another namespace than the resource
gotk create hr podinfo \ flux create hr podinfo \
--target-namespace=default \ --target-namespace=default \
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo --chart=podinfo
# Create a HelmRelease definition on disk without applying it on the cluster # Create a HelmRelease definition on disk without applying it on the cluster
gotk create hr podinfo \ flux create hr podinfo \
--source=HelmRepository/podinfo \ --source=HelmRepository/podinfo \
--chart=podinfo \ --chart=podinfo \
--values=./values.yaml \ --values=./values.yaml \
@@ -91,7 +91,7 @@ var createHelmReleaseCmd = &cobra.Command{
var ( var (
hrName string hrName string
hrSource string hrSource flags.HelmChartSource
hrDependsOn []string hrDependsOn []string
hrChart string hrChart string
hrChartVersion string hrChartVersion string
@@ -100,8 +100,8 @@ var (
) )
func init() { func init() {
createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<hr-name>'") createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
createHelmReleaseCmd.Flags().StringVar(&hrSource, "source", "", "source that contains the chart (<kind>/<name>)") createHelmReleaseCmd.Flags().Var(&hrSource, "source", hrSource.Description())
createHelmReleaseCmd.Flags().StringVar(&hrChart, "chart", "", "Helm chart name or path") 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().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().StringArrayVar(&hrDependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
@@ -112,21 +112,10 @@ func init() {
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("release name is required") return fmt.Errorf("HelmRelease name is required")
} }
name := args[0] name := args[0]
if hrSource == "" {
return fmt.Errorf("source is required")
}
sourceKind, sourceName := utils.parseObjectKindName(hrSource)
if sourceKind == "" {
return fmt.Errorf("invalid source '%s', must be in format <kind>/<name>", hrSource)
}
if !utils.containsItemString(supportedHelmChartSourceKinds, sourceKind) {
return fmt.Errorf("source kind %s is not supported, can be %v",
sourceKind, supportedHelmChartSourceKinds)
}
if hrChart == "" { if hrChart == "" {
return fmt.Errorf("chart name or path is required") return fmt.Errorf("chart name or path is required")
} }
@@ -137,7 +126,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
} }
if !export { if !export {
logger.Generatef("generating release") logger.Generatef("generating HelmRelease")
} }
helmRelease := helmv2.HelmRelease{ helmRelease := helmv2.HelmRelease{
@@ -148,7 +137,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}, },
Spec: helmv2.HelmReleaseSpec{ Spec: helmv2.HelmReleaseSpec{
ReleaseName: hrName, ReleaseName: hrName,
DependsOn: utils.makeDependsOn(hrDependsOn), DependsOn: utils.MakeDependsOn(hrDependsOn),
Interval: metav1.Duration{ Interval: metav1.Duration{
Duration: interval, Duration: interval,
}, },
@@ -158,8 +147,8 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
Chart: hrChart, Chart: hrChart,
Version: hrChartVersion, Version: hrChartVersion,
SourceRef: helmv2.CrossNamespaceObjectReference{ SourceRef: helmv2.CrossNamespaceObjectReference{
Kind: sourceKind, Kind: hrSource.Kind,
Name: sourceName, Name: hrSource.Name,
}, },
}, },
}, },
@@ -188,48 +177,30 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
logger.Actionf("applying release") logger.Actionf("applying HelmRelease")
if err := upsertHelmRelease(ctx, kubeClient, helmRelease); err != nil { namespacedName, err := upsertHelmRelease(ctx, kubeClient, &helmRelease)
return err
}
logger.Waitingf("waiting for reconciliation")
chartName := fmt.Sprintf("%s-%s", namespace, name)
if err := wait.PollImmediate(pollInterval, timeout,
isHelmChartReady(ctx, kubeClient, chartName, namespace)); err != nil {
return err
}
if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("release %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil { if err != nil {
return fmt.Errorf("release failed: %w", err) return err
} }
if helmRelease.Status.LastAppliedRevision != "" { logger.Waitingf("waiting for HelmRelease reconciliation")
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision) if err := wait.PollImmediate(pollInterval, timeout,
} else { isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return fmt.Errorf("reconciliation failed") return err
} }
logger.Successf("HelmRelease %s is ready", name)
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
return nil return nil
} }
func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmRelease helmv2.HelmRelease) error { func upsertHelmRelease(ctx context.Context, kubeClient client.Client,
helmRelease *helmv2.HelmRelease) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: helmRelease.GetNamespace(), Namespace: helmRelease.GetNamespace(),
Name: helmRelease.GetName(), Name: helmRelease.GetName(),
@@ -239,75 +210,39 @@ func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmReleas
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRelease); err != nil { if err := kubeClient.Create(ctx, helmRelease); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("release created") logger.Successf("HelmRelease created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = helmRelease.Labels existing.Labels = helmRelease.Labels
existing.Spec = helmRelease.Spec existing.Spec = helmRelease.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
helmRelease = &existing
logger.Successf("release updated") logger.Successf("HelmRelease updated")
return nil return namespacedName, nil
} }
func isHelmChartReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isHelmReleaseReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var helmChart sourcev1.HelmChart err := kubeClient.Get(ctx, namespacedName, helmRelease)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmChart)
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}
return false, err
}
if c := meta.GetCondition(helmChart.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}
func isHelmReleaseReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
return func() (bool, error) {
var helmRelease helmv2.HelmRelease
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil { if err != nil {
return false, err return false, err
} }
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil { // Confirm the state we are observing is for the current generation
switch c.Status { if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
case corev1.ConditionTrue: return false, nil
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
} }
return false, nil
return meta.HasReadyCondition(helmRelease.Status.Conditions), nil
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -30,10 +30,11 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var createKsCmd = &cobra.Command{ var createKsCmd = &cobra.Command{
@@ -42,7 +43,7 @@ var createKsCmd = &cobra.Command{
Short: "Create or update a Kustomization resource", Short: "Create or update a Kustomization resource",
Long: "The kustomization source create command generates a Kustomize resource for a given source.", Long: "The kustomization source create command generates a Kustomize resource for a given source.",
Example: ` # Create a Kustomization resource from a source at a given path Example: ` # Create a Kustomization resource from a source at a given path
gotk create kustomization contour \ flux create kustomization contour \
--source=contour \ --source=contour \
--path="./examples/contour/" \ --path="./examples/contour/" \
--prune=true \ --prune=true \
@@ -53,7 +54,7 @@ var createKsCmd = &cobra.Command{
--health-check-timeout=3m --health-check-timeout=3m
# Create a Kustomization resource that depends on the previous one # Create a Kustomization resource that depends on the previous one
gotk create kustomization webapp \ flux create kustomization webapp \
--depends-on=contour \ --depends-on=contour \
--source=webapp \ --source=webapp \
--path="./deploy/overlays/dev" \ --path="./deploy/overlays/dev" \
@@ -62,7 +63,7 @@ var createKsCmd = &cobra.Command{
--validation=client --validation=client
# Create a Kustomization resource that references a Bucket # Create a Kustomization resource that references a Bucket
gotk create kustomization secrets \ flux create kustomization secrets \
--source=Bucket/secrets \ --source=Bucket/secrets \
--prune=true \ --prune=true \
--interval=5m --interval=5m
@@ -71,7 +72,7 @@ var createKsCmd = &cobra.Command{
} }
var ( var (
ksSource string ksSource flags.KustomizationSource
ksPath string ksPath string
ksPrune bool ksPrune bool
ksDependsOn []string ksDependsOn []string
@@ -80,13 +81,12 @@ var (
ksHealthTimeout time.Duration ksHealthTimeout time.Duration
ksSAName string ksSAName string
ksSANamespace string ksSANamespace string
ksDecryptionProvider string ksDecryptionProvider flags.DecryptionProvider
ksDecryptionSecret string ksDecryptionSecret string
) )
func init() { func init() {
createKsCmd.Flags().StringVar(&ksSource, "source", "", createKsCmd.Flags().Var(&ksSource, "source", ksSource.Description())
"source that contains the Kubernetes manifests in the format '[<kind>/]<name>', where kind can be GitRepository or Bucket, if kind is not specified it defaults to GitRepository")
createKsCmd.Flags().StringVar(&ksPath, "path", "./", "path to the directory containing the Kustomization file") createKsCmd.Flags().StringVar(&ksPath, "path", "./", "path to the directory containing the Kustomization file")
createKsCmd.Flags().BoolVar(&ksPrune, "prune", false, "enable garbage collection") 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().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
@@ -95,30 +95,17 @@ func init() {
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().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, "sa-name", "", "service account name") createKsCmd.Flags().StringVar(&ksSAName, "sa-name", "", "service account name")
createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace") createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace")
createKsCmd.Flags().StringVar(&ksDecryptionProvider, "decryption-provider", "", "enables secrets decryption, provider can be 'sops'") 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(&ksDecryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
createCmd.AddCommand(createKsCmd) createCmd.AddCommand(createKsCmd)
} }
func createKsCmdRun(cmd *cobra.Command, args []string) error { func createKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("kustomization name is required") return fmt.Errorf("Kustomization name is required")
} }
name := args[0] name := args[0]
if ksSource == "" {
return fmt.Errorf("source is required")
}
sourceKind, sourceName := utils.parseObjectKindName(ksSource)
if sourceKind == "" {
sourceKind = sourcev1.GitRepositoryKind
}
if !utils.containsItemString(supportedKustomizationSourceKinds, sourceKind) {
return fmt.Errorf("source kind %s is not supported, can be %v",
sourceKind, supportedKustomizationSourceKinds)
}
if ksPath == "" { if ksPath == "" {
return fmt.Errorf("path is required") return fmt.Errorf("path is required")
} }
@@ -127,7 +114,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
} }
if !export { if !export {
logger.Generatef("generating kustomization") logger.Generatef("generating Kustomization")
} }
ksLabels, err := parseLabels() ksLabels, err := parseLabels()
@@ -142,15 +129,15 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
Labels: ksLabels, Labels: ksLabels,
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
DependsOn: utils.makeDependsOn(hrDependsOn), DependsOn: utils.MakeDependsOn(ksDependsOn),
Interval: metav1.Duration{ Interval: metav1.Duration{
Duration: interval, Duration: interval,
}, },
Path: ksPath, Path: ksPath,
Prune: ksPrune, Prune: ksPrune,
SourceRef: kustomizev1.CrossNamespaceSourceReference{ SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourceKind, Kind: ksSource.Kind,
Name: sourceName, Name: ksSource.Name,
}, },
Suspend: false, Suspend: false,
Validation: ksValidation, Validation: ksValidation,
@@ -206,13 +193,8 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
} }
if ksDecryptionProvider != "" { if ksDecryptionProvider != "" {
if !utils.containsItemString(supportedDecryptionProviders, ksDecryptionProvider) {
return fmt.Errorf("decryption provider %s is not supported, can be %v",
ksDecryptionProvider, supportedDecryptionProviders)
}
kustomization.Spec.Decryption = &kustomizev1.Decryption{ kustomization.Spec.Decryption = &kustomizev1.Decryption{
Provider: ksDecryptionProvider, Provider: ksDecryptionProvider.String(),
} }
if ksDecryptionSecret != "" { if ksDecryptionSecret != "" {
@@ -227,43 +209,30 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
logger.Actionf("applying kustomization") logger.Actionf("applying Kustomization")
if err := upsertKustomization(ctx, kubeClient, kustomization); err != nil { namespacedName, err := upsertKustomization(ctx, kubeClient, &kustomization)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for kustomization sync") logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil { isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err return err
} }
logger.Successf("Kustomization %s is ready", name)
logger.Successf("kustomization %s is ready", name) logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return fmt.Errorf("kustomization sync failed: %w", err)
}
if kustomization.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
} else {
return fmt.Errorf("kustomization sync failed")
}
return nil return nil
} }
func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomization kustomizev1.Kustomization) error { func upsertKustomization(ctx context.Context, kubeClient client.Client,
kustomization *kustomizev1.Kustomization) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: kustomization.GetNamespace(), Namespace: kustomization.GetNamespace(),
Name: kustomization.GetName(), Name: kustomization.GetName(),
@@ -273,39 +242,39 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomiz
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &kustomization); err != nil { if err := kubeClient.Create(ctx, kustomization); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("kustomization created") logger.Successf("Kustomization created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = kustomization.Labels existing.Labels = kustomization.Labels
existing.Spec = kustomization.Spec existing.Spec = kustomization.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
kustomization = &existing
logger.Successf("kustomization updated") logger.Successf("Kustomization updated")
return nil return namespacedName, nil
} }
func isKustomizationReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isKustomizationReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var kustomization kustomizev1.Kustomization err := kubeClient.Get(ctx, namespacedName, kustomization)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil { if err != nil {
return false, err return false, err
} }
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:

202
cmd/flux/create_receiver.go Normal file
View File

@@ -0,0 +1,202 @@
/*
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 (
"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"
"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"
)
var createReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Create or update a Receiver resource",
Long: "The create receiver command generates a Receiver resource.",
Example: ` # Create a Receiver
flux create receiver github-receiver \
--type github \
--event ping \
--event push \
--secret-ref webhook-token \
--resource GitRepository/webapp \
--resource HelmRepository/webapp
`,
RunE: createReceiverCmdRun,
}
var (
rcvType string
rcvSecretRef string
rcvEvents []string
rcvResources []string
)
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{}, "")
createCmd.AddCommand(createReceiverCmd)
}
func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Receiver name is required")
}
name := args[0]
if rcvType == "" {
return fmt.Errorf("Receiver type is required")
}
if rcvSecretRef == "" {
return fmt.Errorf("secret ref is required")
}
resources := []notificationv1.CrossNamespaceObjectReference{}
for _, resource := range rcvResources {
kind, name := utils.ParseObjectKindName(resource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", resource)
}
resources = append(resources, notificationv1.CrossNamespaceObjectReference{
Kind: kind,
Name: name,
})
}
if len(resources) == 0 {
return fmt.Errorf("atleast one resource is required")
}
sourceLabels, err := parseLabels()
if err != nil {
return err
}
if !export {
logger.Generatef("generating Receiver")
}
receiver := notificationv1.Receiver{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: sourceLabels,
},
Spec: notificationv1.ReceiverSpec{
Type: rcvType,
Events: rcvEvents,
Resources: resources,
SecretRef: corev1.LocalObjectReference{
Name: rcvSecretRef,
},
Suspend: false,
},
}
if export {
return exportReceiver(receiver)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying Receiver")
namespacedName, err := upsertReceiver(ctx, kubeClient, &receiver)
if err != nil {
return err
}
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("Receiver %s is ready", name)
logger.Successf("generated webhook URL %s", receiver.Status.URL)
return nil
}
func upsertReceiver(ctx context.Context, kubeClient client.Client,
receiver *notificationv1.Receiver) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: receiver.GetNamespace(),
Name: receiver.GetName(),
}
var existing notificationv1.Receiver
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, receiver); err != nil {
return namespacedName, err
} else {
logger.Successf("Receiver created")
return namespacedName, nil
}
}
return namespacedName, err
}
existing.Labels = receiver.Labels
existing.Spec = receiver.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return namespacedName, err
}
receiver = &existing
logger.Successf("Receiver updated")
return namespacedName, nil
}
func isReceiverReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, receiver)
if err != nil {
return false, err
}
if c := meta.GetCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -30,6 +30,8 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
@@ -40,7 +42,7 @@ var createSourceBucketCmd = &cobra.Command{
The create source bucket command generates a Bucket resource and waits for it to be downloaded. The create source bucket command generates a Bucket resource and waits for it to be downloaded.
For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`, For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a Buckets using static authentication Example: ` # Create a source from a Buckets using static authentication
gotk create source bucket podinfo \ flux create source bucket podinfo \
--bucket-name=podinfo \ --bucket-name=podinfo \
--endpoint=minio.minio.svc.cluster.local:9000 \ --endpoint=minio.minio.svc.cluster.local:9000 \
--insecure=true \ --insecure=true \
@@ -49,7 +51,7 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
--interval=10m --interval=10m
# Create a source from an Amazon S3 Bucket using IAM authentication # Create a source from an Amazon S3 Bucket using IAM authentication
gotk create source bucket podinfo \ flux create source bucket podinfo \
--bucket-name=podinfo \ --bucket-name=podinfo \
--provider=aws \ --provider=aws \
--endpoint=s3.amazonaws.com \ --endpoint=s3.amazonaws.com \
@@ -61,37 +63,33 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
var ( var (
sourceBucketName string sourceBucketName string
sourceBucketProvider string sourceBucketProvider = flags.SourceBucketProvider(sourcev1.GenericBucketProvider)
sourceBucketEndpoint string sourceBucketEndpoint string
sourceBucketAccessKey string sourceBucketAccessKey string
sourceBucketSecretKey string sourceBucketSecretKey string
sourceBucketRegion string sourceBucketRegion string
sourceBucketInsecure bool sourceBucketInsecure bool
sourceBucketSecretRef string
) )
func init() { func init() {
createSourceBucketCmd.Flags().StringVar(&sourceBucketProvider, "provider", sourcev1.GenericBucketProvider, "the S3 compatible storage provider name, can be 'generic' or 'aws'") createSourceBucketCmd.Flags().Var(&sourceBucketProvider, "provider", sourceBucketProvider.Description())
createSourceBucketCmd.Flags().StringVar(&sourceBucketName, "bucket-name", "", "the bucket name") createSourceBucketCmd.Flags().StringVar(&sourceBucketName, "bucket-name", "", "the bucket name")
createSourceBucketCmd.Flags().StringVar(&sourceBucketEndpoint, "endpoint", "", "the bucket endpoint address") createSourceBucketCmd.Flags().StringVar(&sourceBucketEndpoint, "endpoint", "", "the bucket endpoint address")
createSourceBucketCmd.Flags().StringVar(&sourceBucketAccessKey, "access-key", "", "the bucket access key") createSourceBucketCmd.Flags().StringVar(&sourceBucketAccessKey, "access-key", "", "the bucket access key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretKey, "secret-key", "", "the bucket secret key") createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretKey, "secret-key", "", "the bucket secret key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketRegion, "region", "", "the bucket region") 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().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")
createSourceCmd.AddCommand(createSourceBucketCmd) createSourceCmd.AddCommand(createSourceBucketCmd)
} }
func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error { func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("source name is required") return fmt.Errorf("Bucket source name is required")
} }
name := args[0] name := args[0]
secretName := fmt.Sprintf("bucket-%s", name)
if !utils.containsItemString(supportedSourceBucketProviders, sourceBucketProvider) {
return fmt.Errorf("bucket provider %s is not supported, can be %v",
sourceBucketProvider, supportedSourceBucketProviders)
}
if sourceBucketName == "" { if sourceBucketName == "" {
return fmt.Errorf("bucket-name is required") return fmt.Errorf("bucket-name is required")
@@ -112,7 +110,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
bucket := sourcev1.Bucket{ bucket := &sourcev1.Bucket{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
@@ -120,7 +118,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
}, },
Spec: sourcev1.BucketSpec{ Spec: sourcev1.BucketSpec{
BucketName: sourceBucketName, BucketName: sourceBucketName,
Provider: sourceBucketProvider, Provider: sourceBucketProvider.String(),
Insecure: sourceBucketInsecure, Insecure: sourceBucketInsecure,
Endpoint: sourceBucketEndpoint, Endpoint: sourceBucketEndpoint,
Region: sourceBucketRegion, Region: sourceBucketRegion,
@@ -129,77 +127,76 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
}, },
}, },
} }
if sourceHelmSecretRef != "" {
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceBucketSecretRef,
}
}
if export { if export {
return exportBucket(bucket) return exportBucket(*bucket)
} }
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
logger.Generatef("generating source") logger.Generatef("generating Bucket source")
secret := corev1.Secret{ if sourceBucketSecretRef == "" {
ObjectMeta: metav1.ObjectMeta{ secretName := fmt.Sprintf("bucket-%s", name)
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{},
}
if sourceBucketAccessKey != "" && sourceBucketSecretKey != "" { secret := corev1.Secret{
secret.StringData["accesskey"] = sourceBucketAccessKey ObjectMeta: metav1.ObjectMeta{
secret.StringData["secretkey"] = sourceBucketSecretKey Name: secretName,
} Namespace: namespace,
},
if len(secret.StringData) > 0 { StringData: map[string]string{},
logger.Actionf("applying secret with the bucket credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
} }
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName, if sourceBucketAccessKey != "" && sourceBucketSecretKey != "" {
secret.StringData["accesskey"] = sourceBucketAccessKey
secret.StringData["secretkey"] = sourceBucketSecretKey
}
if len(secret.StringData) > 0 {
logger.Actionf("applying secret with the bucket credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
}
logger.Successf("authentication configured")
} }
logger.Successf("authentication configured")
} }
logger.Actionf("applying source") logger.Actionf("applying Bucket source")
if err := upsertBucket(ctx, kubeClient, bucket); err != nil { namespacedName, err := upsertBucket(ctx, kubeClient, bucket)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for download") logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isBucketReady(ctx, kubeClient, name, namespace)); err != nil { isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
return err return err
} }
logger.Successf("Bucket source reconciliation completed")
logger.Successf("download completed") if bucket.Status.Artifact == nil {
return fmt.Errorf("Bucket source reconciliation but no artifact was found")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, &bucket) logger.Successf("fetched revision: %s", bucket.Status.Artifact.Revision)
if err != nil {
return fmt.Errorf("could not retrieve bucket: %w", err)
}
if bucket.Status.Artifact != nil {
logger.Successf("fetched revision: %s", bucket.Status.Artifact.Revision)
} else {
return fmt.Errorf("download failed, artifact not found")
}
return nil return nil
} }
func upsertBucket(ctx context.Context, kubeClient client.Client, bucket sourcev1.Bucket) error { func upsertBucket(ctx context.Context, kubeClient client.Client,
bucket *sourcev1.Bucket) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: bucket.GetNamespace(), Namespace: bucket.GetNamespace(),
Name: bucket.GetName(), Name: bucket.GetName(),
@@ -209,22 +206,22 @@ func upsertBucket(ctx context.Context, kubeClient client.Client, bucket sourcev1
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &bucket); err != nil { if err := kubeClient.Create(ctx, bucket); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("source created") logger.Successf("Bucket source created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = bucket.Labels existing.Labels = bucket.Labels
existing.Spec = bucket.Spec existing.Spec = bucket.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
bucket = &existing
logger.Successf("source updated") logger.Successf("Bucket source updated")
return nil return namespacedName, nil
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,12 +20,15 @@ import (
"context" "context"
"crypto/elliptic" "crypto/elliptic"
"fmt" "fmt"
"github.com/fluxcd/pkg/apis/meta"
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"os" "os"
"time" "time"
"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" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -47,35 +50,35 @@ The create source git command generates a GitRepository resource and waits for i
For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret. For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`, For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a public Git repository master branch Example: ` # Create a source from a public Git repository master branch
gotk create source git podinfo \ flux create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--branch=master --branch=master
# Create a source from a Git repository pinned to specific git tag # Create a source from a Git repository pinned to specific git tag
gotk create source git podinfo \ flux create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--tag="3.2.3" --tag="3.2.3"
# Create a source from a public Git repository tag that matches a semver range # Create a source from a public Git repository tag that matches a semver range
gotk create source git podinfo \ flux create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.0 <3.3.0" --tag-semver=">=3.2.0 <3.3.0"
# Create a source from a Git repository using SSH authentication # Create a source from a Git repository using SSH authentication
gotk create source git podinfo \ flux create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \ --url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master --branch=master
# Create a source from a Git repository using SSH authentication and an # Create a source from a Git repository using SSH authentication and an
# ECDSA P-521 curve public key # ECDSA P-521 curve public key
gotk create source git podinfo \ flux create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \ --url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master \ --branch=master \
--ssh-key-algorithm=ecdsa \ --ssh-key-algorithm=ecdsa \
--ssh-ecdsa-curve=p521 --ssh-ecdsa-curve=p521
# Create a source from a Git repository using basic authentication # Create a source from a Git repository using basic authentication
gotk create source git podinfo \ flux create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--username=username \ --username=username \
--password=password --password=password
@@ -84,15 +87,17 @@ For private Git repositories, the basic authentication credentials are stored in
} }
var ( var (
sourceGitURL string sourceGitURL string
sourceGitBranch string sourceGitBranch string
sourceGitTag string sourceGitTag string
sourceGitSemver string sourceGitSemver string
sourceGitUsername string sourceGitUsername string
sourceGitPassword string sourceGitPassword string
sourceGitKeyAlgorithm PublicKeyAlgorithm = "rsa"
sourceGitRSABits RSAKeyBits = 2048 sourceGitKeyAlgorithm flags.PublicKeyAlgorithm = "rsa"
sourceGitECDSACurve = ECDSACurve{elliptic.P384()} sourceGitRSABits flags.RSAKeyBits = 2048
sourceGitECDSACurve = flags.ECDSACurve{Curve: elliptic.P384()}
sourceGitSecretRef string
) )
func init() { func init() {
@@ -105,13 +110,14 @@ func init() {
createSourceGitCmd.Flags().Var(&sourceGitKeyAlgorithm, "ssh-key-algorithm", sourceGitKeyAlgorithm.Description()) createSourceGitCmd.Flags().Var(&sourceGitKeyAlgorithm, "ssh-key-algorithm", sourceGitKeyAlgorithm.Description())
createSourceGitCmd.Flags().Var(&sourceGitRSABits, "ssh-rsa-bits", sourceGitRSABits.Description()) createSourceGitCmd.Flags().Var(&sourceGitRSABits, "ssh-rsa-bits", sourceGitRSABits.Description())
createSourceGitCmd.Flags().Var(&sourceGitECDSACurve, "ssh-ecdsa-curve", sourceGitECDSACurve.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")
createSourceCmd.AddCommand(createSourceGitCmd) createSourceCmd.AddCommand(createSourceGitCmd)
} }
func createSourceGitCmdRun(cmd *cobra.Command, args []string) error { func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("source name is required") return fmt.Errorf("GitRepository source name is required")
} }
name := args[0] name := args[0]
@@ -159,20 +165,27 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
} }
if export { if export {
if sourceGitSecretRef != "" {
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceGitSecretRef,
}
}
return exportGit(gitRepository) return exportGit(gitRepository)
} }
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
withAuth := false withAuth := false
// TODO(hidde): move all auth prep to separate func? // TODO(hidde): move all auth prep to separate func?
if u.Scheme == "ssh" { if sourceGitSecretRef != "" {
withAuth = true
} else if u.Scheme == "ssh" {
logger.Actionf("generating deploy key pair") logger.Actionf("generating deploy key pair")
pair, err := generateKeyPair(ctx) pair, err := generateKeyPair(ctx)
if err != nil { if err != nil {
@@ -234,42 +247,35 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("authentication configured") logger.Successf("authentication configured")
} }
logger.Generatef("generating source") logger.Generatef("generating GitRepository source")
if withAuth { if withAuth {
secretName := name
if sourceGitSecretRef != "" {
secretName = sourceGitSecretRef
}
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{ gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: name, Name: secretName,
} }
} }
logger.Actionf("applying source") logger.Actionf("applying GitRepository source")
if err := upsertGitRepository(ctx, kubeClient, gitRepository); err != nil { namespacedName, err := upsertGitRepository(ctx, kubeClient, &gitRepository)
return err
}
logger.Waitingf("waiting for git sync")
if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("git sync completed")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil { if err != nil {
return fmt.Errorf("git sync failed: %w", err) return err
} }
if gitRepository.Status.Artifact != nil { logger.Waitingf("waiting for GitRepository source reconciliation")
logger.Successf("fetched revision: %s", gitRepository.Status.Artifact.Revision) if err := wait.PollImmediate(pollInterval, timeout,
} else { isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
return fmt.Errorf("git sync failed, artifact not found") return err
} }
logger.Successf("GitRepository source reconciliation completed")
if gitRepository.Status.Artifact == nil {
return fmt.Errorf("GitRepository source reconciliation completed but no artifact was found")
}
logger.Successf("fetched revision: %s", gitRepository.Status.Artifact.Revision)
return nil return nil
} }
@@ -330,7 +336,8 @@ func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.S
return nil return nil
} }
func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepository sourcev1.GitRepository) error { func upsertGitRepository(ctx context.Context, kubeClient client.Client,
gitRepository *sourcev1.GitRepository) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: gitRepository.GetNamespace(), Namespace: gitRepository.GetNamespace(),
Name: gitRepository.GetName(), Name: gitRepository.GetName(),
@@ -340,35 +347,30 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepos
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &gitRepository); err != nil { if err := kubeClient.Create(ctx, gitRepository); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("source created") logger.Successf("GitRepository source created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = gitRepository.Labels existing.Labels = gitRepository.Labels
existing.Spec = gitRepository.Spec existing.Spec = gitRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
gitRepository = &existing
logger.Successf("source updated") logger.Successf("GitRepository source updated")
return nil return namespacedName, nil
} }
func isGitRepositoryReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isGitRepositoryReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, gitRepository *sourcev1.GitRepository) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var gitRepository sourcev1.GitRepository err := kubeClient.Get(ctx, namespacedName, gitRepository)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
@@ -41,18 +42,18 @@ var createSourceHelmCmd = &cobra.Command{
The create source helm command generates a HelmRepository resource and waits for it to fetch the index. The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`, For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a public Helm repository Example: ` # Create a source from a public Helm repository
gotk create source helm podinfo \ flux create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \ --url=https://stefanprodan.github.io/podinfo \
--interval=10m --interval=10m
# Create a source from a Helm repository using basic authentication # Create a source from a Helm repository using basic authentication
gotk create source helm podinfo \ flux create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \ --url=https://stefanprodan.github.io/podinfo \
--username=username \ --username=username \
--password=password --password=password
# Create a source from a Helm repository using TLS authentication # Create a source from a Helm repository using TLS authentication
gotk create source helm podinfo \ flux create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \ --url=https://stefanprodan.github.io/podinfo \
--cert-file=./cert.crt \ --cert-file=./cert.crt \
--key-file=./key.crt \ --key-file=./key.crt \
@@ -62,12 +63,13 @@ For private Helm repositories, the basic authentication credentials are stored i
} }
var ( var (
sourceHelmURL string sourceHelmURL string
sourceHelmUsername string sourceHelmUsername string
sourceHelmPassword string sourceHelmPassword string
sourceHelmCertFile string sourceHelmCertFile string
sourceHelmKeyFile string sourceHelmKeyFile string
sourceHelmCAFile string sourceHelmCAFile string
sourceHelmSecretRef string
) )
func init() { func init() {
@@ -77,16 +79,16 @@ func init() {
createSourceHelmCmd.Flags().StringVar(&sourceHelmCertFile, "cert-file", "", "TLS authentication cert file path") 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(&sourceHelmKeyFile, "key-file", "", "TLS authentication key file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmCAFile, "ca-file", "", "TLS authentication CA 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")
createSourceCmd.AddCommand(createSourceHelmCmd) createSourceCmd.AddCommand(createSourceHelmCmd)
} }
func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error { func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("source name is required") return fmt.Errorf("HelmRepository source name is required")
} }
name := args[0] name := args[0]
secretName := fmt.Sprintf("helm-%s", name)
if sourceHelmURL == "" { if sourceHelmURL == "" {
return fmt.Errorf("url is required") return fmt.Errorf("url is required")
@@ -107,7 +109,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("url parse failed: %w", err) return fmt.Errorf("url parse failed: %w", err)
} }
helmRepository := sourcev1.HelmRepository{ helmRepository := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
@@ -121,98 +123,97 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}, },
} }
if sourceHelmSecretRef != "" {
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceHelmSecretRef,
}
}
if export { if export {
return exportHelmRepository(helmRepository) return exportHelmRepository(*helmRepository)
} }
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
logger.Generatef("generating source") logger.Generatef("generating HelmRepository source")
if sourceHelmSecretRef == "" {
secretName := fmt.Sprintf("helm-%s", name)
secret := corev1.Secret{ secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: secretName, Name: secretName,
Namespace: namespace, Namespace: namespace,
}, },
StringData: map[string]string{}, StringData: map[string]string{},
}
if sourceHelmUsername != "" && sourceHelmPassword != "" {
secret.StringData["username"] = sourceHelmUsername
secret.StringData["password"] = sourceHelmPassword
}
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)
}
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 {
logger.Actionf("applying secret with repository credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
}
logger.Successf("authentication configured")
}
} }
if sourceHelmUsername != "" && sourceHelmPassword != "" { logger.Actionf("applying HelmRepository source")
secret.StringData["username"] = sourceHelmUsername namespacedName, err := upsertHelmRepository(ctx, kubeClient, helmRepository)
secret.StringData["password"] = sourceHelmPassword if err != nil {
}
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)
}
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 {
logger.Actionf("applying secret with repository credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
}
logger.Successf("authentication configured")
}
logger.Actionf("applying source")
if err := upsertHelmRepository(ctx, kubeClient, helmRepository); err != nil {
return err return err
} }
logger.Waitingf("waiting for index download") logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
return err return err
} }
logger.Successf("HelmRepository source reconciliation completed")
logger.Successf("index download completed") if helmRepository.Status.Artifact == nil {
return fmt.Errorf("HelmRepository source reconciliation completed but no artifact was found")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, &helmRepository) logger.Successf("fetched revision: %s", helmRepository.Status.Artifact.Revision)
if err != nil {
return fmt.Errorf("helm index failed: %w", err)
}
if helmRepository.Status.Artifact != nil {
logger.Successf("fetched revision: %s", helmRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("index download failed, artifact not found")
}
return nil return nil
} }
func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRepository sourcev1.HelmRepository) error { func upsertHelmRepository(ctx context.Context, kubeClient client.Client,
helmRepository *sourcev1.HelmRepository) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: helmRepository.GetNamespace(), Namespace: helmRepository.GetNamespace(),
Name: helmRepository.GetName(), Name: helmRepository.GetName(),
@@ -222,22 +223,22 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRep
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRepository); err != nil { if err := kubeClient.Create(ctx, helmRepository); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("source created") logger.Successf("source created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = helmRepository.Labels existing.Labels = helmRepository.Labels
existing.Spec = helmRepository.Spec existing.Spec = helmRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
helmRepository = &existing
logger.Successf("source updated") logger.Successf("source updated")
return nil return namespacedName, nil
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
@@ -40,12 +41,12 @@ var createTenantCmd = &cobra.Command{
The create tenant command generates namespaces and role bindings to limit the The create tenant command generates namespaces and role bindings to limit the
reconcilers scope to the tenant namespaces.`, reconcilers scope to the tenant namespaces.`,
Example: ` # Create a tenant with access to a namespace Example: ` # Create a tenant with access to a namespace
gotk create tenant dev-team \ flux create tenant dev-team \
--with-namespace=frontend \ --with-namespace=frontend \
--label=environment=dev --label=environment=dev
# Generate tenant namespaces and role bindings in YAML format # Generate tenant namespaces and role bindings in YAML format
gotk create tenant dev-team \ flux create tenant dev-team \
--with-namespace=frontend \ --with-namespace=frontend \
--with-namespace=backend \ --with-namespace=backend \
--export > dev-team.yaml --export > dev-team.yaml
@@ -144,7 +145,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

88
cmd/flux/delete_alert.go Normal file
View File

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

View File

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

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
) )
@@ -33,7 +34,7 @@ var deleteHelmReleaseCmd = &cobra.Command{
Short: "Delete a HelmRelease resource", Short: "Delete a HelmRelease resource",
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.", Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
Example: ` # Delete a Helm release and the Kubernetes resources created by it Example: ` # Delete a Helm release and the Kubernetes resources created by it
gotk delete hr podinfo flux delete hr podinfo
`, `,
RunE: deleteHelmReleaseCmdRun, RunE: deleteHelmReleaseCmdRun,
} }
@@ -51,7 +52,7 @@ func deleteHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -32,7 +33,7 @@ var deleteKsCmd = &cobra.Command{
Short: "Delete a Kustomization resource", Short: "Delete a Kustomization resource",
Long: "The delete kustomization command deletes the given Kustomization from the cluster.", Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
Example: ` # Delete a kustomization and the Kubernetes resources created by it Example: ` # Delete a kustomization and the Kubernetes resources created by it
gotk delete kustomization podinfo flux delete kustomization podinfo
`, `,
RunE: deleteKsCmdRun, RunE: deleteKsCmdRun,
} }
@@ -50,7 +51,7 @@ func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

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

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -31,7 +32,7 @@ var deleteSourceBucketCmd = &cobra.Command{
Short: "Delete a Bucket source", Short: "Delete a Bucket source",
Long: "The delete source bucket command deletes the given Bucket from the cluster.", Long: "The delete source bucket command deletes the given Bucket from the cluster.",
Example: ` # Delete a Bucket source Example: ` # Delete a Bucket source
gotk delete source bucket podinfo flux delete source bucket podinfo
`, `,
RunE: deleteSourceBucketCmdRun, RunE: deleteSourceBucketCmdRun,
} }
@@ -49,7 +50,7 @@ func deleteSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -31,7 +32,7 @@ var deleteSourceGitCmd = &cobra.Command{
Short: "Delete a GitRepository source", Short: "Delete a GitRepository source",
Long: "The delete source git command deletes the given GitRepository from the cluster.", Long: "The delete source git command deletes the given GitRepository from the cluster.",
Example: ` # Delete a Git repository Example: ` # Delete a Git repository
gotk delete source git podinfo flux delete source git podinfo
`, `,
RunE: deleteSourceGitCmdRun, RunE: deleteSourceGitCmdRun,
} }
@@ -49,7 +50,7 @@ func deleteSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -31,7 +32,7 @@ var deleteSourceHelmCmd = &cobra.Command{
Short: "Delete a HelmRepository source", Short: "Delete a HelmRepository source",
Long: "The delete source helm command deletes the given HelmRepository from the cluster.", Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
Example: ` # Delete a Helm repository Example: ` # Delete a Helm repository
gotk delete source helm podinfo flux delete source helm podinfo
`, `,
RunE: deleteSourceHelmCmdRun, RunE: deleteSourceHelmCmdRun,
} }
@@ -49,7 +50,7 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

120
cmd/flux/export_alert.go Normal file
View File

@@ -0,0 +1,120 @@
/*
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 (
"context"
"fmt"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var exportAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Export Alert resources in YAML format",
Long: "The export alert command exports one or all Alert resources in YAML format.",
Example: ` # Export all Alert resources
flux export alert --all > alerts.yaml
# Export a Alert
flux export alert main > main.yaml
`,
RunE: exportAlertCmdRun,
}
func init() {
exportCmd.AddCommand(exportAlertCmd)
}
func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
if exportAll {
var list notificationv1.AlertList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alerts found in %s namespace", namespace)
return nil
}
for _, alert := range list.Items {
if err := exportAlert(alert); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
return exportAlert(alert)
}
return nil
}
func exportAlert(alert notificationv1.Alert) error {
gvk := notificationv1.GroupVersion.WithKind("Alert")
export := notificationv1.Alert{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: alert.Name,
Namespace: alert.Namespace,
Labels: alert.Labels,
Annotations: alert.Annotations,
},
Spec: alert.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
}

View File

@@ -0,0 +1,120 @@
/*
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 (
"context"
"fmt"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var exportAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Export Provider resources in YAML format",
Long: "The export alert-provider command exports one or all Provider resources in YAML format.",
Example: ` # Export all Provider resources
flux export alert-provider --all > alert-providers.yaml
# Export a Provider
flux export alert-provider slack > slack.yaml
`,
RunE: exportAlertProviderCmdRun,
}
func init() {
exportCmd.AddCommand(exportAlertProviderCmd)
}
func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
if exportAll {
var list notificationv1.ProviderList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alertproviders found in %s namespace", namespace)
return nil
}
for _, alertProvider := range list.Items {
if err := exportAlertProvider(alertProvider); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
return err
}
return exportAlertProvider(alertProvider)
}
return nil
}
func exportAlertProvider(alertProvider notificationv1.Provider) error {
gvk := notificationv1.GroupVersion.WithKind("Provider")
export := notificationv1.Provider{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: alertProvider.Name,
Namespace: alertProvider.Namespace,
Labels: alertProvider.Labels,
Annotations: alertProvider.Annotations,
},
Spec: alertProvider.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
) )
@@ -35,10 +36,10 @@ var exportHelmReleaseCmd = &cobra.Command{
Short: "Export HelmRelease resources in YAML format", Short: "Export HelmRelease resources in YAML format",
Long: "The export helmrelease command exports one or all HelmRelease resources in YAML format.", Long: "The export helmrelease command exports one or all HelmRelease resources in YAML format.",
Example: ` # Export all HelmRelease resources Example: ` # Export all HelmRelease resources
gotk export helmrelease --all > kustomizations.yaml flux export helmrelease --all > kustomizations.yaml
# Export a HelmRelease # Export a HelmRelease
gotk export hr my-app > app-release.yaml flux export hr my-app > app-release.yaml
`, `,
RunE: exportHelmReleaseCmdRun, RunE: exportHelmReleaseCmdRun,
} }
@@ -55,7 +56,7 @@ func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
) )
@@ -35,10 +36,10 @@ var exportKsCmd = &cobra.Command{
Short: "Export Kustomization resources in YAML format", Short: "Export Kustomization resources in YAML format",
Long: "The export kustomization command exports one or all Kustomization resources in YAML format.", Long: "The export kustomization command exports one or all Kustomization resources in YAML format.",
Example: ` # Export all Kustomization resources Example: ` # Export all Kustomization resources
gotk export kustomization --all > kustomizations.yaml flux export kustomization --all > kustomizations.yaml
# Export a Kustomization # Export a Kustomization
gotk export kustomization my-app > kustomization.yaml flux export kustomization my-app > kustomization.yaml
`, `,
RunE: exportKsCmdRun, RunE: exportKsCmdRun,
} }
@@ -55,7 +56,7 @@ func exportKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

120
cmd/flux/export_receiver.go Normal file
View File

@@ -0,0 +1,120 @@
/*
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 (
"context"
"fmt"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var exportReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Export Receiver resources in YAML format",
Long: "The export receiver command exports one or all Receiver resources in YAML format.",
Example: ` # Export all Receiver resources
flux export receiver --all > receivers.yaml
# Export a Receiver
flux export receiver main > main.yaml
`,
RunE: exportReceiverCmdRun,
}
func init() {
exportCmd.AddCommand(exportReceiverCmd)
}
func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
if !exportAll && len(args) < 1 {
return fmt.Errorf("name is required")
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
if exportAll {
var list notificationv1.ReceiverList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no receivers found in %s namespace", namespace)
return nil
}
for _, receiver := range list.Items {
if err := exportReceiver(receiver); err != nil {
return err
}
}
} else {
name := args[0]
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
return exportReceiver(receiver)
}
return nil
}
func exportReceiver(receiver notificationv1.Receiver) error {
gvk := notificationv1.GroupVersion.WithKind("Receiver")
export := notificationv1.Receiver{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: receiver.Name,
Namespace: receiver.Namespace,
Labels: receiver.Labels,
Annotations: receiver.Annotations,
},
Spec: receiver.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(resourceToString(data))
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
@@ -35,10 +36,10 @@ var exportSourceBucketCmd = &cobra.Command{
Short: "Export Bucket sources in YAML format", Short: "Export Bucket sources in YAML format",
Long: "The export source git command exports on or all Bucket sources in YAML format.", Long: "The export source git command exports on or all Bucket sources in YAML format.",
Example: ` # Export all Bucket sources Example: ` # Export all Bucket sources
gotk export source bucket --all > sources.yaml flux export source bucket --all > sources.yaml
# Export a Bucket source including the static credentials # Export a Bucket source including the static credentials
gotk export source bucket my-bucket --with-credentials > source.yaml flux export source bucket my-bucket --with-credentials > source.yaml
`, `,
RunE: exportSourceBucketCmdRun, RunE: exportSourceBucketCmdRun,
} }
@@ -55,7 +56,7 @@ func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
@@ -35,10 +36,10 @@ var exportSourceGitCmd = &cobra.Command{
Short: "Export GitRepository sources in YAML format", Short: "Export GitRepository sources in YAML format",
Long: "The export source git command exports on or all GitRepository sources in YAML format.", Long: "The export source git command exports on or all GitRepository sources in YAML format.",
Example: ` # Export all GitRepository sources Example: ` # Export all GitRepository sources
gotk export source git --all > sources.yaml flux export source git --all > sources.yaml
# Export a GitRepository source including the SSH key pair or basic auth credentials # Export a GitRepository source including the SSH key pair or basic auth credentials
gotk export source git my-private-repo --with-credentials > source.yaml flux export source git my-private-repo --with-credentials > source.yaml
`, `,
RunE: exportSourceGitCmdRun, RunE: exportSourceGitCmdRun,
} }
@@ -55,7 +56,7 @@ func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
@@ -35,10 +36,10 @@ var exportSourceHelmCmd = &cobra.Command{
Short: "Export HelmRepository sources in YAML format", Short: "Export HelmRepository sources in YAML format",
Long: "The export source git command exports on or all HelmRepository sources in YAML format.", Long: "The export source git command exports on or all HelmRepository sources in YAML format.",
Example: ` # Export all HelmRepository sources Example: ` # Export all HelmRepository sources
gotk export source helm --all > sources.yaml flux export source helm --all > sources.yaml
# Export a HelmRepository source including the basic auth credentials # Export a HelmRepository source including the basic auth credentials
gotk export source helm my-private-repo --with-credentials > source.yaml flux export source helm my-private-repo --with-credentials > source.yaml
`, `,
RunE: exportSourceHelmCmdRun, RunE: exportSourceHelmCmdRun,
} }
@@ -55,7 +56,7 @@ func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -26,6 +26,10 @@ var getCmd = &cobra.Command{
Long: "The get sub-commands print the statuses of sources and resources.", Long: "The get sub-commands print the statuses of sources and resources.",
} }
var allNamespaces bool
func init() { func init() {
getCmd.PersistentFlags().BoolVarP(&allNamespaces, "all-namespaces", "A", false,
"list the requested object(s) across all namespaces")
rootCmd.AddCommand(getCmd) rootCmd.AddCommand(getCmd)
} }

103
cmd/flux/get_alert.go Normal file
View File

@@ -0,0 +1,103 @@
/*
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 (
"context"
"os"
"strconv"
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"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"
)
var getAlertCmd = &cobra.Command{
Use: "alerts",
Short: "Get Alert statuses",
Long: "The get alert command prints the statuses of the resources.",
Example: ` # List all Alerts and their status
flux get alerts
`,
RunE: getAlertCmdRun,
}
func init() {
getCmd.AddCommand(getAlertCmd)
}
func getAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list notificationv1.AlertList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no alerts found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Suspended", "Ready", "Message"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, alert := range list.Items {
row := []string{}
if c := meta.GetCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
alert.GetName(),
//alert.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
string(c.Status),
c.Message,
}
} else {
row = []string{
alert.GetName(),
//alert.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{alert.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -0,0 +1,97 @@
/*
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 (
"context"
"os"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"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"
)
var getAlertProviderCmd = &cobra.Command{
Use: "alert-providers",
Short: "Get Provider statuses",
Long: "The get alert-provider command prints the statuses of the resources.",
Example: ` # List all Providers and their status
flux get alert-providers
`,
RunE: getAlertProviderCmdRun,
}
func init() {
getCmd.AddCommand(getAlertProviderCmd)
}
func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list notificationv1.ProviderList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no providers found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Ready", "Message"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, provider := range list.Items {
row := []string{}
if c := meta.GetCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
provider.GetName(),
string(c.Status),
c.Message,
}
} else {
row = []string{
provider.GetName(),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{provider.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,6 +18,11 @@ package main
import ( import (
"context" "context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -33,7 +38,7 @@ var getHelmReleaseCmd = &cobra.Command{
Short: "Get HelmRelease statuses", Short: "Get HelmRelease statuses",
Long: "The get helmreleases command prints the statuses of the resources.", Long: "The get helmreleases command prints the statuses of the resources.",
Example: ` # List all Helm releases and their status Example: ` # List all Helm releases and their status
gotk get helmreleases flux get helmreleases
`, `,
RunE: getHelmReleaseCmdRun, RunE: getHelmReleaseCmdRun,
} }
@@ -46,13 +51,17 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list helmv2.HelmReleaseList var list helmv2.HelmReleaseList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace)) err = kubeClient.List(ctx, &list, listOpts...)
if err != nil { if err != nil {
return err return err
} }
@@ -62,27 +71,35 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
return nil return nil
} }
for _, helmRelease := range list.Items { header := []string{"Name", "Revision", "Suspended", "Ready", "Message"}
if helmRelease.Spec.Suspend { if allNamespaces {
logger.Successf("%s is suspended", helmRelease.GetName()) header = append([]string{"Namespace"}, header...)
continue
}
isInitialized := false
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
logger.Successf("%s last applied revision %s", helmRelease.GetName(), helmRelease.Status.LastAppliedRevision)
case corev1.ConditionUnknown:
logger.Successf("%s reconciling", helmRelease.GetName())
default:
logger.Failuref("%s %s", helmRelease.GetName(), c.Message)
}
isInitialized = true
break
}
if !isInitialized {
logger.Failuref("%s is not ready", helmRelease.GetName())
}
} }
var rows [][]string
for _, helmRelease := range list.Items {
row := []string{}
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
helmRelease.GetName(),
helmRelease.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(helmRelease.Spec.Suspend)),
string(c.Status),
c.Message,
}
} else {
row = []string{
helmRelease.GetName(),
helmRelease.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(helmRelease.Spec.Suspend)),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{helmRelease.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil return nil
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,6 +18,11 @@ package main
import ( import (
"context" "context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
@@ -32,7 +37,7 @@ var getKsCmd = &cobra.Command{
Short: "Get Kustomization statuses", Short: "Get Kustomization statuses",
Long: "The get kustomizations command prints the statuses of the resources.", Long: "The get kustomizations command prints the statuses of the resources.",
Example: ` # List all kustomizations and their status Example: ` # List all kustomizations and their status
gotk get kustomizations flux get kustomizations
`, `,
RunE: getKsCmdRun, RunE: getKsCmdRun,
} }
@@ -45,13 +50,17 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list kustomizev1.KustomizationList var list kustomizev1.KustomizationList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace)) err = kubeClient.List(ctx, &list, listOpts...)
if err != nil { if err != nil {
return err return err
} }
@@ -61,27 +70,35 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
return nil return nil
} }
for _, kustomization := range list.Items { header := []string{"Name", "Revision", "Suspended", "Ready", "Message"}
if kustomization.Spec.Suspend { if allNamespaces {
logger.Successf("%s is suspended", kustomization.GetName()) header = append([]string{"Namespace"}, header...)
continue
}
isInitialized := false
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
logger.Successf("%s last applied revision %s", kustomization.GetName(), kustomization.Status.LastAppliedRevision)
case corev1.ConditionUnknown:
logger.Successf("%s reconciling", kustomization.GetName())
default:
logger.Failuref("%s %s", kustomization.GetName(), c.Message)
}
isInitialized = true
break
}
if !isInitialized {
logger.Failuref("%s is not ready", kustomization.GetName())
}
} }
var rows [][]string
for _, kustomization := range list.Items {
row := []string{}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
kustomization.GetName(),
kustomization.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(kustomization.Spec.Suspend)),
string(c.Status),
c.Message,
}
} else {
row = []string{
kustomization.GetName(),
kustomization.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(kustomization.Spec.Suspend)),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{kustomization.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil return nil
} }

98
cmd/flux/get_receiver.go Normal file
View File

@@ -0,0 +1,98 @@
/*
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 (
"context"
"os"
"strconv"
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"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"
)
var getReceiverCmd = &cobra.Command{
Use: "receivers",
Short: "Get Receiver statuses",
Long: "The get receiver command prints the statuses of the resources.",
Example: ` # List all Receiver and their status
flux get receivers
`,
RunE: getReceiverCmdRun,
}
func init() {
getCmd.AddCommand(getReceiverCmd)
}
func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list notificationv1.ReceiverList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no receivers found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Suspended", "Ready", "Message"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, receiver := range list.Items {
row := []string{}
if c := meta.GetCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
receiver.GetName(),
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
string(c.Status),
c.Message,
}
} else {
row = []string{
receiver.GetName(),
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,6 +18,9 @@ package main
import ( import (
"context" "context"
"os"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
@@ -31,7 +34,7 @@ var getSourceBucketCmd = &cobra.Command{
Short: "Get Bucket source statuses", Short: "Get Bucket source statuses",
Long: "The get sources bucket command prints the status of the Bucket sources.", Long: "The get sources bucket command prints the status of the Bucket sources.",
Example: ` # List all Buckets and their status Example: ` # List all Buckets and their status
gotk get sources bucket flux get sources bucket
`, `,
RunE: getSourceBucketCmdRun, RunE: getSourceBucketCmdRun,
} }
@@ -44,42 +47,57 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list sourcev1.BucketList var list sourcev1.BucketList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace)) err = kubeClient.List(ctx, &list, listOpts...)
if err != nil { if err != nil {
return err return err
} }
if len(list.Items) == 0 { if len(list.Items) == 0 {
logger.Failuref("no sources found in %s namespace", namespace) logger.Failuref("no bucket sources found in %s namespace", namespace)
return nil return nil
} }
// TODO(hidde): this should print a table, and should produce better output header := []string{"Name", "Revision", "Ready", "Message"}
// for items that have an artifact attached while they are in a reconciling if allNamespaces {
// 'Unknown' state. header = append([]string{"Namespace"}, header...)
for _, source := range list.Items {
isInitialized := false
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
logger.Successf("%s last fetched revision: %s", source.GetName(), source.GetArtifact().Revision)
case corev1.ConditionUnknown:
logger.Successf("%s reconciling", source.GetName())
default:
logger.Failuref("%s %s", source.GetName(), c.Message)
}
isInitialized = true
break
}
if !isInitialized {
logger.Failuref("%s is not ready", source.GetName())
}
} }
var rows [][]string
for _, source := range list.Items {
var row []string
var revision string
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
revision,
string(c.Status),
c.Message,
}
} else {
row = []string{
source.GetName(),
revision,
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{source.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil return nil
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,6 +18,9 @@ package main
import ( import (
"context" "context"
"os"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
@@ -31,7 +34,7 @@ var getSourceGitCmd = &cobra.Command{
Short: "Get GitRepository source statuses", Short: "Get GitRepository source statuses",
Long: "The get sources git command prints the status of the GitRepository sources.", Long: "The get sources git command prints the status of the GitRepository sources.",
Example: ` # List all Git repositories and their status Example: ` # List all Git repositories and their status
gotk get sources git flux get sources git
`, `,
RunE: getSourceGitCmdRun, RunE: getSourceGitCmdRun,
} }
@@ -44,42 +47,57 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list sourcev1.GitRepositoryList var list sourcev1.GitRepositoryList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace)) err = kubeClient.List(ctx, &list, listOpts...)
if err != nil { if err != nil {
return err return err
} }
if len(list.Items) == 0 { if len(list.Items) == 0 {
logger.Failuref("no sources found in %s namespace", namespace) logger.Failuref("no git sources found in %s namespace", namespace)
return nil return nil
} }
// TODO(hidde): this should print a table, and should produce better output header := []string{"Name", "Revision", "Ready", "Message"}
// for items that have an artifact attached while they are in a reconciling if allNamespaces {
// 'Unknown' state. header = append([]string{"Namespace"}, header...)
for _, source := range list.Items {
isInitialized := false
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
logger.Successf("%s last fetched revision: %s", source.GetName(), source.GetArtifact().Revision)
case corev1.ConditionUnknown:
logger.Successf("%s reconciling", source.GetName())
default:
logger.Failuref("%s %s", source.GetName(), c.Message)
}
isInitialized = true
break
}
if !isInitialized {
logger.Failuref("%s is not ready", source.GetName())
}
} }
var rows [][]string
for _, source := range list.Items {
var row []string
var revision string
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
revision,
string(c.Status),
c.Message,
}
} else {
row = []string{
source.GetName(),
revision,
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{source.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil return nil
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,6 +18,9 @@ package main
import ( import (
"context" "context"
"os"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
@@ -31,7 +34,7 @@ var getSourceHelmCmd = &cobra.Command{
Short: "Get HelmRepository source statuses", Short: "Get HelmRepository source statuses",
Long: "The get sources helm command prints the status of the HelmRepository sources.", Long: "The get sources helm command prints the status of the HelmRepository sources.",
Example: ` # List all Helm repositories and their status Example: ` # List all Helm repositories and their status
gotk get sources helm flux get sources helm
`, `,
RunE: getSourceHelmCmdRun, RunE: getSourceHelmCmdRun,
} }
@@ -44,42 +47,57 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list sourcev1.HelmRepositoryList var list sourcev1.HelmRepositoryList
err = kubeClient.List(ctx, &list, client.InNamespace(namespace)) err = kubeClient.List(ctx, &list, listOpts...)
if err != nil { if err != nil {
return err return err
} }
if len(list.Items) == 0 { if len(list.Items) == 0 {
logger.Failuref("no sources found in %s namespace", namespace) logger.Failuref("no helm sources found in %s namespace", namespace)
return nil return nil
} }
// TODO(hidde): this should print a table, and should produce better output header := []string{"Name", "Revision", "Ready", "Message"}
// for items that have an artifact attached while they are in a reconciling if allNamespaces {
// 'Unknown' state. header = append([]string{"Namespace"}, header...)
for _, source := range list.Items {
isInitialized := false
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
logger.Successf("%s last fetched revision: %s", source.GetName(), source.GetArtifact().Revision)
case corev1.ConditionUnknown:
logger.Successf("%s reconciling", source.GetName())
default:
logger.Failuref("%s %s", source.GetName(), c.Message)
}
isInitialized = true
break
}
if !isInitialized {
logger.Failuref("%s is not ready", source.GetName())
}
} }
var rows [][]string
for _, source := range list.Items {
var row []string
var revision string
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
revision,
string(c.Status),
c.Message,
}
} else {
row = []string{
source.GetName(),
revision,
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
row = append([]string{source.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil return nil
} }

181
cmd/flux/install.go Normal file
View File

@@ -0,0 +1,181 @@
/*
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 (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
)
var installCmd = &cobra.Command{
Use: "install",
Short: "Install the toolkit components",
Long: `The install command deploys the toolkit components 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
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
# Dry-run install with manifests preview
flux install --dry-run --verbose
# Write install manifests to file
flux install --export > flux-system.yaml
`,
RunE: installCmdRun,
}
var (
installExport bool
installDryRun bool
installManifestsPath string
installVersion string
installComponents []string
installRegistry string
installImagePullSecret string
installWatchAllNamespaces bool
installNetworkPolicy bool
installArch = flags.Arch(defaults.Arch)
installLogLevel = flags.LogLevel(defaults.LogLevel)
)
func init() {
installCmd.Flags().BoolVar(&installExport, "export", false,
"write the install manifests to stdout and exit")
installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false,
"only print the object that would be applied")
installCmd.Flags().StringVarP(&installVersion, "version", "v", defaults.Version,
"toolkit version")
installCmd.Flags().StringSliceVar(&installComponents, "components", defaults.Components,
"list of components, accepts comma-separated values")
installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory")
installCmd.Flags().MarkHidden("manifests")
installCmd.Flags().StringVar(&installRegistry, "registry", defaults.Registry,
"container registry where the toolkit images are published")
installCmd.Flags().StringVar(&installImagePullSecret, "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,
"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,
"deny ingress access to the toolkit controllers from other namespaces using network policies")
rootCmd.AddCommand(installCmd)
}
func installCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
tmpDir, err := ioutil.TempDir("", namespace)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
if !installExport {
logger.Generatef("generating manifests")
}
opts := install.Options{
BaseURL: installManifestsPath,
Version: installVersion,
Namespace: namespace,
Components: installComponents,
Registry: installRegistry,
ImagePullSecret: installImagePullSecret,
Arch: installArch.String(),
WatchAllNamespaces: installWatchAllNamespaces,
NetworkPolicy: installNetworkPolicy,
LogLevel: installLogLevel.String(),
NotificationController: defaults.NotificationController,
ManifestFile: fmt.Sprintf("%s.yaml", namespace),
Timeout: timeout,
}
if installManifestsPath == "" {
opts.BaseURL = install.MakeDefaultOptions().BaseURL
}
manifest, err := install.Generate(opts)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
if _, err := manifest.WriteFile(tmpDir); err != nil {
return fmt.Errorf("install failed: %w", err)
}
if verbose {
fmt.Print(manifest.Content)
} else if installExport {
fmt.Println("---")
fmt.Println("# GitOps Toolkit revision", installVersion)
fmt.Println("# Components:", strings.Join(installComponents, ","))
fmt.Print(manifest.Content)
fmt.Println("---")
return nil
}
logger.Successf("manifests build completed")
logger.Actionf("installing components in %s namespace", namespace)
applyOutput := utils.ModeStderrOS
if verbose {
applyOutput = utils.ModeOS
}
kubectlArgs := []string{"apply", "-f", filepath.Join(tmpDir, manifest.Path)}
if installDryRun {
args = append(args, "--dry-run=client")
applyOutput = utils.ModeOS
}
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
}
if installDryRun {
logger.Successf("install dry-run finished")
return nil
} else {
logger.Successf("install completed")
}
logger.Waitingf("verifying installation")
for _, deployment := range installComponents {
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
} else {
logger.Successf("%s ready", deployment)
}
}
logger.Successf("install finished")
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -26,43 +26,42 @@ import (
"github.com/spf13/cobra/doc" "github.com/spf13/cobra/doc"
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" fluxlog "github.com/fluxcd/flux2/pkg/log"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
gotklog "github.com/fluxcd/toolkit/pkg/log"
) )
var VERSION = "0.0.0-dev.0" var VERSION = "0.0.0-dev.0"
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "gotk", Use: "flux",
Version: VERSION, Version: VERSION,
SilenceUsage: true, SilenceUsage: true,
SilenceErrors: true, SilenceErrors: true,
Short: "Command line utility for assembling Kubernetes CD pipelines", Short: "Command line utility for assembling Kubernetes CD pipelines",
Long: `Command line utility for assembling Kubernetes CD pipelines the GitOps way.`, Long: `Command line utility for assembling Kubernetes CD pipelines the GitOps way.`,
Example: ` # Check prerequisites Example: ` # Check prerequisites
gotk check --pre flux check --pre
# Install the latest version of the toolkit # Install the latest version of the toolkit
gotk install --version=master flux install --version=master
# Create a source from a public Git repository # Create a source from a public Git repository
gotk create source git webapp-latest \ flux create source git webapp-latest \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--branch=master \ --branch=master \
--interval=3m --interval=3m
# List GitRepository sources and their status # List GitRepository sources and their status
gotk get sources git flux get sources git
# Trigger a GitRepository source reconciliation # Trigger a GitRepository source reconciliation
gotk reconcile source git gotk-system flux reconcile source git flux-system
# Export GitRepository sources in YAML format # Export GitRepository sources in YAML format
gotk export source git --all > sources.yaml flux export source git --all > sources.yaml
# Create a Kustomization for deploying a series of microservices # Create a Kustomization for deploying a series of microservices
gotk create kustomization webapp-dev \ flux create kustomization webapp-dev \
--source=webapp-latest \ --source=webapp-latest \
--path="./deploy/webapp/" \ --path="./deploy/webapp/" \
--prune=true \ --prune=true \
@@ -73,25 +72,25 @@ var rootCmd = &cobra.Command{
--health-check-timeout=2m --health-check-timeout=2m
# Trigger a git sync of the Kustomization's source and apply changes # Trigger a git sync of the Kustomization's source and apply changes
gotk reconcile kustomization webapp-dev --with-source flux reconcile kustomization webapp-dev --with-source
# Suspend a Kustomization reconciliation # Suspend a Kustomization reconciliation
gotk suspend kustomization webapp-dev flux suspend kustomization webapp-dev
# Export Kustomizations in YAML format # Export Kustomizations in YAML format
gotk export kustomization --all > kustomizations.yaml flux export kustomization --all > kustomizations.yaml
# Resume a Kustomization reconciliation # Resume a Kustomization reconciliation
gotk resume kustomization webapp-dev flux resume kustomization webapp-dev
# Delete a Kustomization # Delete a Kustomization
gotk delete kustomization webapp-dev flux delete kustomization webapp-dev
# Delete a GitRepository source # Delete a GitRepository source
gotk delete source git webapp-latest flux delete source git webapp-latest
# Uninstall the toolkit and delete CRDs # Uninstall the toolkit and delete CRDs
gotk uninstall --crds flux uninstall --crds
`, `,
} }
@@ -100,26 +99,13 @@ var (
namespace string namespace string
timeout time.Duration timeout time.Duration
verbose bool verbose bool
utils Utils
pollInterval = 2 * time.Second pollInterval = 2 * time.Second
logger gotklog.Logger = printLogger{} logger fluxlog.Logger = printLogger{}
) defaults = install.MakeDefaultOptions()
var (
defaultComponents = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
defaultVersion = "latest"
defaultNamespace = "gotk-system"
defaultNotification = "notification-controller"
supportedLogLevels = []string{"debug", "info", "error"}
supportedArch = []string{"amd64", "arm", "arm64"}
supportedDecryptionProviders = []string{"sops"}
supportedKustomizationSourceKinds = []string{sourcev1.GitRepositoryKind, sourcev1.BucketKind}
supportedHelmChartSourceKinds = []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind, sourcev1.BucketKind}
supportedSourceBucketProviders = []string{sourcev1.GenericBucketProvider, sourcev1.AmazonBucketProvider}
) )
func init() { func init() {
rootCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", defaultNamespace, "the namespace scope for this operation") 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().DurationVar(&timeout, "timeout", 5*time.Minute, "timeout for this operation")
rootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "print generated objects") rootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "print generated objects")
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -0,0 +1,93 @@
/*
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 (
"context"
"fmt"
"time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var reconcileAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Reconcile an Alert",
Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing alert
flux reconcile alert main
`,
RunE: reconcileAlertCmdRun,
}
func init() {
reconcileCmd.AddCommand(reconcileAlertCmd)
}
func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Alert name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
logger.Actionf("annotating Alert %s in %s namespace", name, namespace)
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
if alert.Annotations == nil {
alert.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
alert.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("Alert annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("Alert reconciliation completed")
return nil
}

View File

@@ -0,0 +1,93 @@
/*
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 (
"context"
"fmt"
"time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var reconcileAlertProviderCmd = &cobra.Command{
Use: "alert-provider [name]",
Short: "Reconcile a Provider",
Long: `The reconcile alert-provider command triggers a reconciliation of a Provider resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing provider
flux reconcile alert-provider slack
`,
RunE: reconcileAlertProviderCmdRun,
}
func init() {
reconcileCmd.AddCommand(reconcileAlertProviderCmd)
}
func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Provider name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
logger.Actionf("annotating Provider %s in %s namespace", name, namespace)
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
return err
}
if alertProvider.Annotations == nil {
alertProvider.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
alertProvider.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &alertProvider); err != nil {
return err
}
logger.Successf("Provider annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, namespacedName, &alertProvider)); err != nil {
return err
}
logger.Successf("Provider reconciliation completed")
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,14 +21,16 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/fluxcd/pkg/apis/meta"
corev1 "k8s.io/api/core/v1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
@@ -40,10 +42,10 @@ var reconcileHrCmd = &cobra.Command{
Long: ` Long: `
The reconcile kustomization command triggers a reconciliation of a HelmRelease resource and waits for it to finish.`, The reconcile kustomization command triggers a reconciliation of a HelmRelease resource and waits for it to finish.`,
Example: ` # Trigger a HelmRelease apply outside of the reconciliation interval Example: ` # Trigger a HelmRelease apply outside of the reconciliation interval
gotk reconcile hr podinfo flux reconcile hr podinfo
# Trigger a reconciliation of the HelmRelease's source and apply changes # Trigger a reconciliation of the HelmRelease's source and apply changes
gotk reconcile hr podinfo --with-source flux reconcile hr podinfo --with-source
`, `,
RunE: reconcileHrCmdRun, RunE: reconcileHrCmdRun,
} }
@@ -67,7 +69,7 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -97,26 +99,19 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt
logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace) logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace)
if helmRelease.Annotations == nil { if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
helmRelease.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
helmRelease.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &helmRelease); err != nil {
return err return err
} }
logger.Successf("HelmRelease annotated") logger.Successf("HelmRelease annotated")
logger.Waitingf("waiting for HelmRelease reconciliation") logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
helmReleaseReconciliationHandled(ctx, kubeClient, name, namespace, helmRelease.Status.LastHandledReconcileAt), helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
); err != nil { ); err != nil {
return err return err
} }
logger.Successf("HelmRelease reconciliation completed") logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease) err = kubeClient.Get(ctx, namespacedName, &helmRelease)
@@ -126,7 +121,7 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionFalse: case corev1.ConditionFalse:
return fmt.Errorf("HelmRelease reconciliation failed") return fmt.Errorf("HelmRelease reconciliation failed: %s", c.Message)
default: default:
logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision) logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision)
} }
@@ -135,19 +130,29 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
} }
func helmReleaseReconciliationHandled(ctx context.Context, kubeClient client.Client, func helmReleaseReconciliationHandled(ctx context.Context, kubeClient client.Client,
name, namespace, lastHandledReconcileAt string) wait.ConditionFunc { namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var helmRelease helmv2.HelmRelease err := kubeClient.Get(ctx, namespacedName, helmRelease)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil { if err != nil {
return false, err return false, err
} }
return helmRelease.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil return helmRelease.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
} }
} }
func requestHelmReleaseReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, helmRelease); err != nil {
return err
}
if helmRelease.Annotations == nil {
helmRelease.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
helmRelease.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
return kubeClient.Update(ctx, helmRelease)
})
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -21,10 +21,12 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/fluxcd/pkg/apis/meta"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@@ -40,10 +42,10 @@ var reconcileKsCmd = &cobra.Command{
Long: ` Long: `
The reconcile kustomization command triggers a reconciliation of a Kustomization resource and waits for it to finish.`, The reconcile kustomization command triggers a reconciliation of a Kustomization resource and waits for it to finish.`,
Example: ` # Trigger a Kustomization apply outside of the reconciliation interval Example: ` # Trigger a Kustomization apply outside of the reconciliation interval
gotk reconcile kustomization podinfo flux reconcile kustomization podinfo
# Trigger a sync of the Kustomization's source and apply changes # Trigger a sync of the Kustomization's source and apply changes
gotk reconcile kustomization podinfo --with-source flux reconcile kustomization podinfo --with-source
`, `,
RunE: reconcileKsCmdRun, RunE: reconcileKsCmdRun,
} }
@@ -53,21 +55,21 @@ var (
) )
func init() { func init() {
reconcileKsCmd.Flags().BoolVar(&syncKsWithSource, "with-source", false, "reconcile kustomization source") reconcileKsCmd.Flags().BoolVar(&syncKsWithSource, "with-source", false, "reconcile Kustomization source")
reconcileCmd.AddCommand(reconcileKsCmd) reconcileCmd.AddCommand(reconcileKsCmd)
} }
func reconcileKsCmdRun(cmd *cobra.Command, args []string) error { func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("kustomization name is required") return fmt.Errorf("Kustomization name is required")
} }
name := args[0] name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -76,7 +78,6 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,
} }
var kustomization kustomizev1.Kustomization var kustomization kustomizev1.Kustomization
err = kubeClient.Get(ctx, namespacedName, &kustomization) err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil { if err != nil {
@@ -95,37 +96,26 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
logger.Actionf("annotating kustomization %s in %s namespace", name, namespace) lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
if kustomization.Annotations == nil { logger.Actionf("annotating Kustomization %s in %s namespace", name, namespace)
kustomization.Annotations = map[string]string{ if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
kustomization.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &kustomization); err != nil {
return err return err
} }
logger.Successf("kustomization annotated") logger.Successf("Kustomization annotated")
logger.Waitingf("waiting for kustomization reconciliation") logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate( if err := wait.PollImmediate(
pollInterval, timeout, pollInterval, timeout,
kustomizeReconciliationHandled(ctx, kubeClient, name, namespace, kustomization.Status.LastHandledReconcileAt), kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
); err != nil { ); err != nil {
return err return err
} }
logger.Successf("Kustomization reconciliation completed")
logger.Successf("kustomization reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionFalse: case corev1.ConditionFalse:
return fmt.Errorf("kustomization reconciliation failed") return fmt.Errorf("Kustomization reconciliation failed")
default: default:
logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision) logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision)
} }
@@ -134,19 +124,29 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
} }
func kustomizeReconciliationHandled(ctx context.Context, kubeClient client.Client, func kustomizeReconciliationHandled(ctx context.Context, kubeClient client.Client,
name, namespace, lastHandledReconcileAt string) wait.ConditionFunc { namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var kustomize kustomizev1.Kustomization err := kubeClient.Get(ctx, namespacedName, kustomization)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomize)
if err != nil { if err != nil {
return false, err return false, err
} }
return kustomization.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
return kustomize.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
} }
} }
func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, kustomization); err != nil {
return err
}
if kustomization.Annotations == nil {
kustomization.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
kustomization.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
return kubeClient.Update(ctx, kustomization)
})
}

View File

@@ -0,0 +1,95 @@
/*
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 (
"context"
"fmt"
"time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var reconcileReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Reconcile a Receiver",
Long: `The reconcile receiver command triggers a reconciliation of a Receiver resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing receiver
flux reconcile receiver main
`,
RunE: reconcileReceiverCmdRun,
}
func init() {
reconcileCmd.AddCommand(reconcileReceiverCmd)
}
func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("receiver name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
logger.Actionf("annotating Receiver %s in %s namespace", name, namespace)
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
if receiver.Annotations == nil {
receiver.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
receiver.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("Receiver annotated")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("Receiver reconciliation completed")
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,9 +19,11 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/pkg/apis/meta"
"time" "time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@@ -36,7 +38,7 @@ var reconcileSourceBucketCmd = &cobra.Command{
Short: "Reconcile a Bucket source", Short: "Reconcile a Bucket source",
Long: `The reconcile source command triggers a reconciliation of a Bucket resource and waits for it to finish.`, Long: `The reconcile source command triggers a reconciliation of a Bucket resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing source Example: ` # Trigger a reconciliation for an existing source
gotk reconcile source bucket podinfo flux reconcile source bucket podinfo
`, `,
RunE: reconcileSourceBucketCmdRun, RunE: reconcileSourceBucketCmdRun,
} }
@@ -54,7 +56,7 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -64,7 +66,7 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating source %s in %s namespace", name, namespace) logger.Actionf("annotating Bucket source %s in %s namespace", name, namespace)
var bucket sourcev1.Bucket var bucket sourcev1.Bucket
err = kubeClient.Get(ctx, namespacedName, &bucket) err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil { if err != nil {
@@ -81,42 +83,35 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &bucket); err != nil { if err := kubeClient.Update(ctx, &bucket); err != nil {
return err return err
} }
logger.Successf("source annotated") logger.Successf("Bucket source annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isBucketReady(ctx, kubeClient, name, namespace)); err != nil { isBucketReady(ctx, kubeClient, namespacedName, &bucket)); err != nil {
return err return err
} }
logger.Successf("Bucket source reconciliation completed")
logger.Successf("bucket reconciliation completed") if bucket.Status.Artifact == nil {
return fmt.Errorf("Bucket source reconciliation completed but no artifact was found")
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return err
}
if bucket.Status.Artifact != nil {
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
} else {
return fmt.Errorf("bucket reconciliation failed, artifact not found")
} }
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
return nil return nil
} }
func isBucketReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isBucketReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var bucket sourcev1.Bucket err := kubeClient.Get(ctx, namespacedName, bucket)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil { if err != nil {
return false, err return false, err
} }
// Confirm the state we are observing is for the current generation
if bucket.Generation != bucket.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,9 +19,11 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/pkg/apis/meta"
"time" "time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@@ -34,7 +36,7 @@ var reconcileSourceGitCmd = &cobra.Command{
Short: "Reconcile a GitRepository source", Short: "Reconcile a GitRepository source",
Long: `The reconcile source command triggers a reconciliation of a GitRepository resource and waits for it to finish.`, Long: `The reconcile source command triggers a reconciliation of a GitRepository resource and waits for it to finish.`,
Example: ` # Trigger a git pull for an existing source Example: ` # Trigger a git pull for an existing source
gotk reconcile source git podinfo flux reconcile source git podinfo
`, `,
RunE: reconcileSourceGitCmdRun, RunE: reconcileSourceGitCmdRun,
} }
@@ -52,7 +54,7 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -62,7 +64,7 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating source %s in %s namespace", name, namespace) logger.Actionf("annotating GitRepository source %s in %s namespace", name, namespace)
var gitRepository sourcev1.GitRepository var gitRepository sourcev1.GitRepository
err = kubeClient.Get(ctx, namespacedName, &gitRepository) err = kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil { if err != nil {
@@ -79,25 +81,18 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &gitRepository); err != nil { if err := kubeClient.Update(ctx, &gitRepository); err != nil {
return err return err
} }
logger.Successf("source annotated") logger.Successf("GitRepository source annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
return err return err
} }
logger.Successf("GitRepository source reconciliation completed")
logger.Successf("git reconciliation completed") if gitRepository.Status.Artifact == nil {
return fmt.Errorf("GitRepository source reconciliation completed but no artifact was found")
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil {
return err
}
if gitRepository.Status.Artifact != nil {
logger.Successf("fetched revision %s", gitRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("git reconciliation failed, artifact not found")
} }
logger.Successf("fetched revision %s", gitRepository.Status.Artifact.Revision)
return nil return nil
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,9 +19,11 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/pkg/apis/meta"
"time" "time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@@ -36,7 +38,7 @@ var reconcileSourceHelmCmd = &cobra.Command{
Short: "Reconcile a HelmRepository source", Short: "Reconcile a HelmRepository source",
Long: `The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish.`, Long: `The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing source Example: ` # Trigger a reconciliation for an existing source
gotk reconcile source helm podinfo flux reconcile source helm podinfo
`, `,
RunE: reconcileSourceHelmCmdRun, RunE: reconcileSourceHelmCmdRun,
} }
@@ -47,14 +49,14 @@ func init() {
func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error { func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("source name is required") return fmt.Errorf("HelmRepository source name is required")
} }
name := args[0] name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -64,7 +66,7 @@ func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating source %s in %s namespace", name, namespace) logger.Actionf("annotating HelmRepository source %s in %s namespace", name, namespace)
var helmRepository sourcev1.HelmRepository var helmRepository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &helmRepository) err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil { if err != nil {
@@ -81,42 +83,35 @@ func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &helmRepository); err != nil { if err := kubeClient.Update(ctx, &helmRepository); err != nil {
return err return err
} }
logger.Successf("source annotated") logger.Successf("HelmRepository source annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isHelmRepositoryReady(ctx, kubeClient, namespacedName, &helmRepository)); err != nil {
return err return err
} }
logger.Successf("HelmRepository source reconciliation completed")
logger.Successf("helm reconciliation completed") if helmRepository.Status.Artifact == nil {
return fmt.Errorf("HelmRepository source reconciliation completed but no artifact was found")
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
return err
}
if helmRepository.Status.Artifact != nil {
logger.Successf("fetched revision %s", helmRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("helm reconciliation failed, artifact not found")
} }
logger.Successf("fetched revision %s", helmRepository.Status.Artifact.Revision)
return nil return nil
} }
func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRepository *sourcev1.HelmRepository) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var helmRepository sourcev1.HelmRepository err := kubeClient.Get(ctx, namespacedName, helmRepository)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil { if err != nil {
return false, err return false, err
} }
// Confirm the state we are observing is for the current generation
if helmRepository.Generation != helmRepository.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(helmRepository.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(helmRepository.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

111
cmd/flux/resume_alert.go Normal file
View File

@@ -0,0 +1,111 @@
/*
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 (
"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/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var resumeAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Resume a suspended Alert",
Long: `The resume command marks a previously suspended Alert resource for reconciliation and waits for it to
finish the apply.`,
Example: ` # Resume reconciliation for an existing Alert
flux resume alert main
`,
RunE: resumeAlertCmdRun,
}
func init() {
resumeCmd.AddCommand(resumeAlertCmd)
}
func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Alert name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
logger.Actionf("resuming Alert %s in %s namespace", name, namespace)
alert.Spec.Suspend = false
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("Alert resumed")
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertResumed(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("Alert reconciliation completed")
return nil
}
func isAlertResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, alert)
if err != nil {
return false, err
}
if c := meta.GetCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -37,7 +39,7 @@ var resumeHrCmd = &cobra.Command{
Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to
finish the apply.`, finish the apply.`,
Example: ` # Resume reconciliation for an existing Helm release Example: ` # Resume reconciliation for an existing Helm release
gotk resume hr podinfo flux resume hr podinfo
`, `,
RunE: resumeHrCmdRun, RunE: resumeHrCmdRun,
} }
@@ -55,7 +57,7 @@ func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -79,36 +81,25 @@ func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for HelmRelease reconciliation") logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseResumed(ctx, kubeClient, name, namespace)); err != nil { isHelmReleaseResumed(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return err return err
} }
logger.Successf("HelmRelease reconciliation completed") logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease) logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
if err != nil {
return err
}
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
} else {
return fmt.Errorf("HelmRelease reconciliation failed")
}
return nil return nil
} }
func isHelmReleaseResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isHelmReleaseResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var helmRelease helmv2.HelmRelease err := kubeClient.Get(ctx, namespacedName, helmRelease)
namespacedName := types.NamespacedName{ if err != nil {
Namespace: namespace, return false, err
Name: name,
} }
err := kubeClient.Get(ctx, namespacedName, &helmRelease) // Confirm the state we are observing is for the current generation
if err != nil { if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
return false, err return false, err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
@@ -36,7 +38,7 @@ var resumeKsCmd = &cobra.Command{
Long: `The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to Long: `The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to
finish the apply.`, finish the apply.`,
Example: ` # Resume reconciliation for an existing Kustomization Example: ` # Resume reconciliation for an existing Kustomization
gotk resume ks podinfo flux resume ks podinfo
`, `,
RunE: resumeKsCmdRun, RunE: resumeKsCmdRun,
} }
@@ -47,14 +49,14 @@ func init() {
func resumeKsCmdRun(cmd *cobra.Command, args []string) error { func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("kustomization name is required") return fmt.Errorf("Kustomization name is required")
} }
name := args[0] name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -69,48 +71,37 @@ func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("resuming kustomization %s in %s namespace", name, namespace) logger.Actionf("resuming Kustomization %s in %s namespace", name, namespace)
kustomization.Spec.Suspend = false kustomization.Spec.Suspend = false
if err := kubeClient.Update(ctx, &kustomization); err != nil { if err := kubeClient.Update(ctx, &kustomization); err != nil {
return err return err
} }
logger.Successf("kustomization resumed") logger.Successf("Kustomization resumed")
logger.Waitingf("waiting for kustomization sync") logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationResumed(ctx, kubeClient, name, namespace)); err != nil { isKustomizationResumed(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err return err
} }
logger.Successf("Kustomization reconciliation completed")
logger.Successf("kustomization sync completed") logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if kustomization.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
} else {
return fmt.Errorf("kustomization sync failed")
}
return nil return nil
} }
func isKustomizationResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isKustomizationResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var kustomization kustomizev1.Kustomization err := kubeClient.Get(ctx, namespacedName, kustomization)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil { if err != nil {
return false, err return false, err
} }
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:

112
cmd/flux/resume_receiver.go Normal file
View File

@@ -0,0 +1,112 @@
/*
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 (
"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/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var resumeReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Resume a suspended Receiver",
Long: `The resume command marks a previously suspended Receiver resource for reconciliation and waits for it to
finish the apply.`,
Example: ` # Resume reconciliation for an existing Receiver
flux resume receiver main
`,
RunE: resumeReceiverCmdRun,
}
func init() {
resumeCmd.AddCommand(resumeReceiverCmd)
}
func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Receiver name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
logger.Actionf("resuming Receiver %s in %s namespace", name, namespace)
receiver.Spec.Suspend = false
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("Receiver resumed")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverResumed(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("Receiver reconciliation completed")
return nil
}
func isReceiverResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, receiver)
if err != nil {
return false, err
}
if c := meta.GetCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

76
cmd/flux/suspend_alert.go Normal file
View File

@@ -0,0 +1,76 @@
/*
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 (
"context"
"fmt"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var suspendAlertCmd = &cobra.Command{
Use: "alert [name]",
Short: "Suspend reconciliation of Alert",
Long: "The suspend command disables the reconciliation of a Alert resource.",
Example: ` # Suspend reconciliation for an existing Alert
flux suspend alert main
`,
RunE: suspendAlertCmdRun,
}
func init() {
suspendCmd.AddCommand(suspendAlertCmd)
}
func suspendAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Alert name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
logger.Actionf("suspending Alert %s in %s namespace", name, namespace)
alert.Spec.Suspend = true
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("Alert suspended")
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
) )
@@ -32,7 +33,7 @@ var suspendHrCmd = &cobra.Command{
Short: "Suspend reconciliation of HelmRelease", Short: "Suspend reconciliation of HelmRelease",
Long: "The suspend command disables the reconciliation of a HelmRelease resource.", Long: "The suspend command disables the reconciliation of a HelmRelease resource.",
Example: ` # Suspend reconciliation for an existing Helm release Example: ` # Suspend reconciliation for an existing Helm release
gotk suspend hr podinfo flux suspend hr podinfo
`, `,
RunE: suspendHrCmdRun, RunE: suspendHrCmdRun,
} }
@@ -50,7 +51,7 @@ func suspendHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/flux2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@@ -30,7 +32,7 @@ var suspendKsCmd = &cobra.Command{
Short: "Suspend reconciliation of Kustomization", Short: "Suspend reconciliation of Kustomization",
Long: "The suspend command disables the reconciliation of a Kustomization resource.", Long: "The suspend command disables the reconciliation of a Kustomization resource.",
Example: ` # Suspend reconciliation for an existing Kustomization Example: ` # Suspend reconciliation for an existing Kustomization
gotk suspend ks podinfo flux suspend ks podinfo
`, `,
RunE: suspendKsCmdRun, RunE: suspendKsCmdRun,
} }
@@ -48,7 +50,7 @@ func suspendKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -0,0 +1,76 @@
/*
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 (
"context"
"fmt"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
var suspendReceiverCmd = &cobra.Command{
Use: "receiver [name]",
Short: "Suspend reconciliation of Receiver",
Long: "The suspend command disables the reconciliation of a Receiver resource.",
Example: ` # Suspend reconciliation for an existing Receiver
flux suspend receiver main
`,
RunE: suspendReceiverCmdRun,
}
func init() {
suspendCmd.AddCommand(suspendReceiverCmd)
}
func suspendReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Receiver name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
logger.Actionf("suspending Receiver %s in %s namespace", name, namespace)
receiver.Spec.Suspend = true
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("Receiver suspended")
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Flux CD contributors. Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -22,8 +22,12 @@ import (
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
@@ -33,10 +37,10 @@ var uninstallCmd = &cobra.Command{
Short: "Uninstall the toolkit components", Short: "Uninstall the toolkit components",
Long: "The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.", Long: "The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.",
Example: ` # Dry-run uninstall of all components Example: ` # Dry-run uninstall of all components
gotk uninstall --dry-run --namespace=gotk-system flux uninstall --dry-run --namespace=flux-system
# Uninstall all components and delete custom resource definitions # Uninstall all components and delete custom resource definitions
gotk uninstall --resources --crds --namespace=gotk-system flux uninstall --resources --crds --namespace=flux-system
`, `,
RunE: uninstallCmdRun, RunE: uninstallCmdRun,
} }
@@ -65,15 +69,12 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig) kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
dryRun := "" if !uninstallDryRun && !uninstallSilent {
if uninstallDryRun {
dryRun = "--dry-run=client"
} else if !uninstallSilent {
prompt := promptui.Prompt{ prompt := promptui.Prompt{
Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", namespace), Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", namespace),
IsConfirm: true, IsConfirm: true,
@@ -83,46 +84,86 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
// suspend bootstrap kustomization if it exists dryRun := "--dry-run=server"
deleteResources := uninstallResources || uninstallCRDs
// known kinds with finalizers
namespacedKinds := []string{
sourcev1.GitRepositoryKind,
sourcev1.HelmRepositoryKind,
sourcev1.BucketKind,
}
// suspend bootstrap kustomization to avoid finalizers deadlock
kustomizationName := types.NamespacedName{ kustomizationName := types.NamespacedName{
Namespace: namespace, Namespace: namespace,
Name: namespace, Name: namespace,
} }
var kustomization kustomizev1.Kustomization var kustomization kustomizev1.Kustomization
if err := kubeClient.Get(ctx, kustomizationName, &kustomization); err == nil { err = kubeClient.Get(ctx, kustomizationName, &kustomization)
if err == nil {
kustomization.Spec.Suspend = true kustomization.Spec.Suspend = true
if err := kubeClient.Update(ctx, &kustomization); err != nil { if err := kubeClient.Update(ctx, &kustomization); err != nil {
return fmt.Errorf("unable to suspend kustomization '%s': %w", kustomizationName.String(), err) return fmt.Errorf("unable to suspend kustomization '%s': %w", kustomizationName.String(), err)
} }
} }
if err == nil || apierrors.IsNotFound(err) {
namespacedKinds = append(namespacedKinds, kustomizev1.KustomizationKind)
}
if uninstallResources || uninstallCRDs { // add HelmRelease kind to deletion list if exists
var list helmv2.HelmReleaseList
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace)); err == nil {
namespacedKinds = append(namespacedKinds, helmv2.HelmReleaseKind)
}
if deleteResources {
logger.Actionf("uninstalling custom resources") logger.Actionf("uninstalling custom resources")
for _, kind := range []string{ for _, kind := range namespacedKinds {
kustomizev1.KustomizationKind, if err := deleteAll(ctx, kind, uninstallDryRun); err != nil {
sourcev1.GitRepositoryKind, logger.Failuref("kubectl: %s", err.Error())
sourcev1.HelmRepositoryKind,
} {
command := fmt.Sprintf("kubectl -n %s delete %s --all --timeout=%s %s",
namespace, kind, timeout.String(), dryRun)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
return fmt.Errorf("uninstall failed")
} }
} }
} }
kinds := "namespace,clusterroles,clusterrolebindings" var kinds []string
if uninstallCRDs { if uninstallCRDs {
kinds += ",crds" kinds = append(kinds, "crds")
} }
kinds = append(kinds, "clusterroles,clusterrolebindings", "namespace")
logger.Actionf("uninstalling components") logger.Actionf("uninstalling components")
command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --timeout=%s %s",
kinds, namespace, timeout.String(), dryRun) for _, kind := range kinds {
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil { kubectlArgs := []string{
return fmt.Errorf("uninstall failed") "delete", kind,
"-l", fmt.Sprintf("app.kubernetes.io/instance=%s", namespace),
"--ignore-not-found", "--timeout", timeout.String(),
}
if uninstallDryRun {
kubectlArgs = append(kubectlArgs, dryRun)
}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
return fmt.Errorf("uninstall failed: %w", err)
}
} }
logger.Successf("uninstall finished") logger.Successf("uninstall finished")
return nil return nil
} }
func deleteAll(ctx context.Context, kind string, dryRun bool) error {
kubectlArgs := []string{
"delete", kind, "--ignore-not-found",
"--all", "--all-namespaces",
"--timeout", timeout.String(),
}
if dryRun {
kubectlArgs = append(kubectlArgs, "--dry-run=server")
}
_, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...)
return err
}

View File

@@ -1,130 +0,0 @@
/*
Copyright 2020 The Flux CD contributors.
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 (
"crypto/elliptic"
"fmt"
"sort"
"strconv"
"strings"
)
var supportedPublicKeyAlgorithms = []string{"rsa", "ecdsa", "ed25519"}
type PublicKeyAlgorithm string
func (a *PublicKeyAlgorithm) String() string {
return string(*a)
}
func (a *PublicKeyAlgorithm) Set(str string) error {
if strings.TrimSpace(str) == "" {
return fmt.Errorf("no public key algorithm given, must be one of: %s",
strings.Join(supportedPublicKeyAlgorithms, ", "))
}
for _, v := range supportedPublicKeyAlgorithms {
if str == v {
*a = PublicKeyAlgorithm(str)
return nil
}
}
return fmt.Errorf("unsupported public key algorithm '%s', must be one of: %s",
str, strings.Join(supportedPublicKeyAlgorithms, ", "))
}
func (a *PublicKeyAlgorithm) Type() string {
return "publicKeyAlgorithm"
}
func (a *PublicKeyAlgorithm) Description() string {
return fmt.Sprintf("SSH public key algorithm (%s)", strings.Join(supportedPublicKeyAlgorithms, ", "))
}
var defaultRSAKeyBits = 2048
type RSAKeyBits int
func (b *RSAKeyBits) String() string {
return strconv.Itoa(int(*b))
}
func (b *RSAKeyBits) Set(str string) error {
if strings.TrimSpace(str) == "" {
*b = RSAKeyBits(defaultRSAKeyBits)
return nil
}
bits, err := strconv.Atoi(str)
if err != nil {
return err
}
if bits%8 != 0 {
return fmt.Errorf("RSA key bit size should be a multiples of 8")
}
*b = RSAKeyBits(bits)
return nil
}
func (b *RSAKeyBits) Type() string {
return "rsaKeyBits"
}
func (b *RSAKeyBits) Description() string {
return "SSH RSA public key bit size (multiplies of 8)"
}
type ECDSACurve struct {
elliptic.Curve
}
var supportedECDSACurves = map[string]elliptic.Curve{
"p256": elliptic.P256(),
"p384": elliptic.P384(),
"p521": elliptic.P521(),
}
func (c *ECDSACurve) String() string {
if c.Curve == nil {
return ""
}
return strings.ToLower(strings.Replace(c.Curve.Params().Name, "-", "", 1))
}
func (c *ECDSACurve) Set(str string) error {
if v, ok := supportedECDSACurves[str]; ok {
*c = ECDSACurve{v}
return nil
}
return fmt.Errorf("unsupported curve '%s', should be one of: %s", str, strings.Join(ecdsaCurves(), ", "))
}
func (c *ECDSACurve) Type() string {
return "ecdsaCurve"
}
func (c *ECDSACurve) Description() string {
return fmt.Sprintf("SSH ECDSA public key curve (%s)", strings.Join(ecdsaCurves(), ", "))
}
func ecdsaCurves() []string {
keys := make([]string, 0, len(supportedECDSACurves))
for k := range supportedECDSACurves {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}

View File

@@ -1,424 +0,0 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/krusty"
"github.com/fluxcd/pkg/untar"
)
var installCmd = &cobra.Command{
Use: "install",
Short: "Install the toolkit components",
Long: `The install command deploys the toolkit components 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 gotk-system namespace
gotk install --version=latest --namespace=gotk-system
# Dry-run install for a specific version and a series of components
gotk install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
# Dry-run install with manifests preview
gotk install --dry-run --verbose
# Write install manifests to file
gotk install --export > gotk-system.yaml
`,
RunE: installCmdRun,
}
var (
installExport bool
installDryRun bool
installManifestsPath string
installVersion string
installComponents []string
installRegistry string
installImagePullSecret string
installArch string
installWatchAllNamespaces bool
installLogLevel string
)
func init() {
installCmd.Flags().BoolVar(&installExport, "export", false,
"write the install manifests to stdout and exit")
installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false,
"only print the object that would be applied")
installCmd.Flags().StringVarP(&installVersion, "version", "v", defaultVersion,
"toolkit version")
installCmd.Flags().StringSliceVar(&installComponents, "components", defaultComponents,
"list of components, accepts comma-separated values")
installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory")
installCmd.Flags().MarkHidden("manifests")
installCmd.Flags().StringVar(&installRegistry, "registry", "ghcr.io/fluxcd",
"container registry where the toolkit images are published")
installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry")
installCmd.Flags().StringVar(&installArch, "arch", "amd64",
"arch can be amd64 or arm64")
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "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")
installCmd.Flags().StringVar(&installLogLevel, "log-level", "info", "set the controllers log level")
rootCmd.AddCommand(installCmd)
}
func installCmdRun(cmd *cobra.Command, args []string) error {
if !utils.containsItemString(supportedArch, installArch) {
return fmt.Errorf("arch %s is not supported, can be %v", installArch, supportedArch)
}
if !utils.containsItemString(supportedLogLevels, installLogLevel) {
return fmt.Errorf("log level %s is not supported, can be %v", bootstrapLogLevel, installLogLevel)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
tmpDir, err := ioutil.TempDir("", namespace)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
if !installExport {
logger.Generatef("generating manifests")
}
if installManifestsPath == "" {
err = genInstallManifests(installVersion, namespace, installComponents,
installWatchAllNamespaces, installRegistry, installImagePullSecret,
installArch, installLogLevel, tmpDir)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
installManifestsPath = tmpDir
}
manifest := path.Join(tmpDir, fmt.Sprintf("%s.yaml", namespace))
if err := buildKustomization(installManifestsPath, manifest); err != nil {
return fmt.Errorf("install failed: %w", err)
}
command := fmt.Sprintf("cat %s", manifest)
if yaml, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
return fmt.Errorf("install failed: %w", err)
} else {
if verbose {
fmt.Print(yaml)
} else if installExport {
fmt.Println("---")
fmt.Println("# GitOps Toolkit revision", installVersion, time.Now().Format(time.RFC3339))
fmt.Println("# Components:", strings.Join(installComponents, ","))
fmt.Print(yaml)
fmt.Println("---")
return nil
}
}
logger.Successf("manifests build completed")
logger.Actionf("installing components in %s namespace", namespace)
applyOutput := ModeStderrOS
if verbose {
applyOutput = ModeOS
}
dryRun := ""
if installDryRun {
dryRun = "--dry-run=client"
applyOutput = ModeOS
}
command = fmt.Sprintf("cat %s | kubectl apply -f- %s", manifest, dryRun)
if _, err := utils.execCommand(ctx, applyOutput, command); err != nil {
return fmt.Errorf("install failed")
}
if installDryRun {
logger.Successf("install dry-run finished")
return nil
} else {
logger.Successf("install completed")
}
logger.Waitingf("verifying installation")
for _, deployment := range installComponents {
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
namespace, deployment, timeout.String())
if _, err := utils.execCommand(ctx, applyOutput, command); err != nil {
return fmt.Errorf("install failed")
} else {
logger.Successf("%s ready", deployment)
}
}
logger.Successf("install finished")
return nil
}
var namespaceTmpl = `---
apiVersion: v1
kind: Namespace
metadata:
name: {{.Namespace}}
`
var labelsTmpl = `---
apiVersion: builtin
kind: LabelTransformer
metadata:
name: labels
labels:
app.kubernetes.io/instance: {{.Namespace}}
app.kubernetes.io/version: "{{.Version}}"
fieldSpecs:
- path: metadata/labels
create: true
`
var kustomizationTmpl = `---
{{- $eventsAddr := .EventsAddr }}
{{- $watchAllNamespaces := .WatchAllNamespaces }}
{{- $registry := .Registry }}
{{- $arch := .Arch }}
{{- $logLevel := .LogLevel }}
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {{.Namespace}}
transformers:
- labels.yaml
resources:
- namespace.yaml
- policies.yaml
- roles
{{- range .Components }}
- {{.}}.yaml
{{- end }}
patches:
- path: node-selector.yaml
target:
kind: Deployment
patchesJson6902:
{{- range $i, $component := .Components }}
{{- if eq $component "notification-controller" }}
- target:
group: apps
version: v1
kind: Deployment
name: {{$component}}
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args/0
value: --watch-all-namespaces={{$watchAllNamespaces}}
- op: replace
path: /spec/template/spec/containers/0/args/1
value: --log-level={{$logLevel}}
{{- else }}
- target:
group: apps
version: v1
kind: Deployment
name: {{$component}}
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args/0
value: --events-addr={{$eventsAddr}}
- op: replace
path: /spec/template/spec/containers/0/args/1
value: --watch-all-namespaces={{$watchAllNamespaces}}
- op: replace
path: /spec/template/spec/containers/0/args/2
value: --log-level={{$logLevel}}
{{- end }}
{{- end }}
{{- if $registry }}
images:
{{- range $i, $component := .Components }}
- name: fluxcd/{{$component}}
{{- if eq $arch "amd64" }}
newName: {{$registry}}/{{$component}}
{{- else }}
newName: {{$registry}}/{{$component}}-arm64
{{- end }}
{{- end }}
{{- end }}
`
var kustomizationRolesTmpl = `---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rbac.yaml
nameSuffix: -{{.Namespace}}
`
var nodeSelectorTmpl = `---
apiVersion: apps/v1
kind: Deployment
metadata:
name: all
spec:
template:
spec:
nodeSelector:
kubernetes.io/arch: {{.Arch}}
kubernetes.io/os: linux
{{- if .ImagePullSecret }}
imagePullSecrets:
- name: {{.ImagePullSecret}}
{{- end }}
`
func downloadManifests(version string, tmpDir string) error {
ghURL := "https://github.com/fluxcd/toolkit/releases/latest/download/manifests.tar.gz"
if strings.HasPrefix(version, "v") {
ghURL = fmt.Sprintf("https://github.com/fluxcd/toolkit/releases/download/%s/manifests.tar.gz", version)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
req, err := http.NewRequest("GET", ghURL, nil)
if err != nil {
return fmt.Errorf("failed to create HTTP request for %s, error: %w", ghURL, err)
}
// download
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
if err != nil {
return fmt.Errorf("failed to download artifact from %s, error: %w", ghURL, err)
}
defer resp.Body.Close()
// check response
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("faild to download artifact from %s, status: %s", ghURL, resp.Status)
}
// extract
if _, err = untar.Untar(resp.Body, tmpDir); err != nil {
return fmt.Errorf("faild to untar manifests from %s, error: %w", ghURL, err)
}
return nil
}
func genInstallManifests(version string, namespace string, components []string,
watchAllNamespaces bool, registry, imagePullSecret, arch, logLevel, tmpDir string) error {
eventsAddr := ""
if utils.containsItemString(components, defaultNotification) {
eventsAddr = fmt.Sprintf("http://%s/", defaultNotification)
}
model := struct {
Version string
Namespace string
Components []string
EventsAddr string
Registry string
ImagePullSecret string
Arch string
WatchAllNamespaces bool
LogLevel string
}{
Version: version,
Namespace: namespace,
Components: components,
EventsAddr: eventsAddr,
Registry: registry,
ImagePullSecret: imagePullSecret,
Arch: arch,
WatchAllNamespaces: watchAllNamespaces,
LogLevel: logLevel,
}
if err := downloadManifests(version, tmpDir); err != nil {
return err
}
if err := utils.execTemplate(model, namespaceTmpl, path.Join(tmpDir, "namespace.yaml")); err != nil {
return fmt.Errorf("generate namespace failed: %w", err)
}
if err := utils.execTemplate(model, labelsTmpl, path.Join(tmpDir, "labels.yaml")); err != nil {
return fmt.Errorf("generate labels failed: %w", err)
}
if err := utils.execTemplate(model, nodeSelectorTmpl, path.Join(tmpDir, "node-selector.yaml")); err != nil {
return fmt.Errorf("generate node selector failed: %w", err)
}
if err := utils.execTemplate(model, kustomizationTmpl, path.Join(tmpDir, "kustomization.yaml")); err != nil {
return fmt.Errorf("generate kustomization failed: %w", err)
}
if err := os.MkdirAll(path.Join(tmpDir, "roles"), os.ModePerm); err != nil {
return fmt.Errorf("generate roles failed: %w", err)
}
if err := utils.execTemplate(model, kustomizationRolesTmpl, path.Join(tmpDir, "roles/kustomization.yaml")); err != nil {
return fmt.Errorf("generate roles failed: %w", err)
}
if err := utils.copyFile(filepath.Join(tmpDir, "rbac.yaml"), filepath.Join(tmpDir, "roles/rbac.yaml")); err != nil {
return fmt.Errorf("generate rbac failed: %w", err)
}
return nil
}
func buildKustomization(base, manifests string) error {
kfile := filepath.Join(base, "kustomization.yaml")
fs := filesys.MakeFsOnDisk()
if !fs.Exists(kfile) {
return fmt.Errorf("%s not found", kfile)
}
opt := krusty.MakeDefaultOptions()
k := krusty.MakeKustomizer(fs, opt)
m, err := k.Run(base)
if err != nil {
return err
}
resources, err := m.AsYaml()
if err != nil {
return err
}
if err := fs.WriteFile(manifests, resources); err != nil {
return err
}
return nil
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -1,4 +1,4 @@
## gotk ## flux
Command line utility for assembling Kubernetes CD pipelines Command line utility for assembling Kubernetes CD pipelines
@@ -10,28 +10,28 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
``` ```
# Check prerequisites # Check prerequisites
gotk check --pre flux check --pre
# Install the latest version of the toolkit # Install the latest version of the toolkit
gotk install --version=master flux install --version=master
# Create a source from a public Git repository # Create a source from a public Git repository
gotk create source git webapp-latest \ flux create source git webapp-latest \
--url=https://github.com/stefanprodan/podinfo \ --url=https://github.com/stefanprodan/podinfo \
--branch=master \ --branch=master \
--interval=3m --interval=3m
# List GitRepository sources and their status # List GitRepository sources and their status
gotk get sources git flux get sources git
# Trigger a GitRepository source reconciliation # Trigger a GitRepository source reconciliation
gotk reconcile source git gotk-system flux reconcile source git flux-system
# Export GitRepository sources in YAML format # Export GitRepository sources in YAML format
gotk export source git --all > sources.yaml flux export source git --all > sources.yaml
# Create a Kustomization for deploying a series of microservices # Create a Kustomization for deploying a series of microservices
gotk create kustomization webapp-dev \ flux create kustomization webapp-dev \
--source=webapp-latest \ --source=webapp-latest \
--path="./deploy/webapp/" \ --path="./deploy/webapp/" \
--prune=true \ --prune=true \
@@ -42,50 +42,50 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
--health-check-timeout=2m --health-check-timeout=2m
# Trigger a git sync of the Kustomization's source and apply changes # Trigger a git sync of the Kustomization's source and apply changes
gotk reconcile kustomization webapp-dev --with-source flux reconcile kustomization webapp-dev --with-source
# Suspend a Kustomization reconciliation # Suspend a Kustomization reconciliation
gotk suspend kustomization webapp-dev flux suspend kustomization webapp-dev
# Export Kustomizations in YAML format # Export Kustomizations in YAML format
gotk export kustomization --all > kustomizations.yaml flux export kustomization --all > kustomizations.yaml
# Resume a Kustomization reconciliation # Resume a Kustomization reconciliation
gotk resume kustomization webapp-dev flux resume kustomization webapp-dev
# Delete a Kustomization # Delete a Kustomization
gotk delete kustomization webapp-dev flux delete kustomization webapp-dev
# Delete a GitRepository source # Delete a GitRepository source
gotk delete source git webapp-latest flux delete source git webapp-latest
# Uninstall the toolkit and delete CRDs # Uninstall the toolkit and delete CRDs
gotk uninstall --crds flux uninstall --crds
``` ```
### Options ### Options
``` ```
-h, --help help for gotk -h, --help help for flux
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system") -n, --namespace string the namespace scope for this operation (default "flux-system")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects
``` ```
### SEE ALSO ### SEE ALSO
* [gotk bootstrap](gotk_bootstrap.md) - Bootstrap toolkit components * [flux bootstrap](flux_bootstrap.md) - Bootstrap toolkit components
* [gotk check](gotk_check.md) - Check requirements and installation * [flux check](flux_check.md) - Check requirements and installation
* [gotk completion](gotk_completion.md) - Generates completion scripts for various shells * [flux completion](flux_completion.md) - Generates completion scripts for various shells
* [gotk create](gotk_create.md) - Create or update sources and resources * [flux create](flux_create.md) - Create or update sources and resources
* [gotk delete](gotk_delete.md) - Delete sources and resources * [flux delete](flux_delete.md) - Delete sources and resources
* [gotk export](gotk_export.md) - Export resources in YAML format * [flux export](flux_export.md) - Export resources in YAML format
* [gotk get](gotk_get.md) - Get sources and resources * [flux get](flux_get.md) - Get sources and resources
* [gotk install](gotk_install.md) - Install the toolkit components * [flux install](flux_install.md) - Install the toolkit components
* [gotk reconcile](gotk_reconcile.md) - Reconcile sources and resources * [flux reconcile](flux_reconcile.md) - Reconcile sources and resources
* [gotk resume](gotk_resume.md) - Resume suspended resources * [flux resume](flux_resume.md) - Resume suspended resources
* [gotk suspend](gotk_suspend.md) - Suspend resources * [flux suspend](flux_suspend.md) - Suspend resources
* [gotk uninstall](gotk_uninstall.md) - Uninstall the toolkit components * [flux uninstall](flux_uninstall.md) - Uninstall the toolkit components

View File

@@ -1,4 +1,4 @@
## gotk bootstrap ## flux bootstrap
Bootstrap toolkit components Bootstrap toolkit components
@@ -9,12 +9,13 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
### Options ### Options
``` ```
--arch string arch can be amd64 or arm64 (default "amd64") --arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "master") --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
-h, --help help for bootstrap -h, --help help for bootstrap
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--log-level string set the controllers log level (default "info") --log-level logLevel log level, available options are: (debug, info, error) (default info)
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
-v, --version string toolkit version (default "latest") -v, --version string toolkit version (default "latest")
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true) --watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
@@ -24,14 +25,14 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
``` ```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system") -n, --namespace string the namespace scope for this operation (default "flux-system")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects
``` ```
### SEE ALSO ### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines * [flux](flux.md) - Command line utility for assembling Kubernetes CD pipelines
* [gotk bootstrap github](gotk_bootstrap_github.md) - Bootstrap toolkit components in a GitHub repository * [flux bootstrap github](flux_bootstrap_github.md) - Bootstrap toolkit components in a GitHub repository
* [gotk bootstrap gitlab](gotk_bootstrap_gitlab.md) - Bootstrap toolkit components in a GitLab repository * [flux bootstrap gitlab](flux_bootstrap_gitlab.md) - Bootstrap toolkit components in a GitLab repository

View File

@@ -1,4 +1,4 @@
## gotk bootstrap github ## flux bootstrap github
Bootstrap toolkit components in a GitHub repository Bootstrap toolkit components in a GitHub repository
@@ -11,7 +11,7 @@ If the toolkit components are present on the cluster,
the bootstrap command will perform an upgrade if needed. the bootstrap command will perform an upgrade if needed.
``` ```
gotk bootstrap github [flags] flux bootstrap github [flags]
``` ```
### Examples ### Examples
@@ -21,22 +21,22 @@ gotk bootstrap github [flags]
export GITHUB_TOKEN=<my-token> export GITHUB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitHub organization # Run bootstrap for a private repo owned by a GitHub organization
gotk bootstrap github --owner=<organization> --repository=<repo name> flux bootstrap github --owner=<organization> --repository=<repo name>
# Run bootstrap for a private repo and assign organization teams to it # Run bootstrap for a private repo and assign organization teams to it
gotk bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug> flux bootstrap github --owner=<organization> --repository=<repo name> --team=<team1 slug> --team=<team2 slug>
# Run bootstrap for a repository path # Run bootstrap for a repository path
gotk bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster flux bootstrap github --owner=<organization> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account # Run bootstrap for a public repository on a personal account
gotk bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true flux bootstrap github --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on GitHub Enterprise # Run bootstrap for a private repo hosted on GitHub Enterprise
gotk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a an existing repository with a branch named main # Run bootstrap for a an existing repository with a branch named main
gotk bootstrap github --owner=<organization> --repository=<repo name> --branch=main flux bootstrap github --owner=<organization> --repository=<repo name> --branch=main
``` ```
@@ -57,13 +57,14 @@ gotk bootstrap github [flags]
### Options inherited from parent commands ### Options inherited from parent commands
``` ```
--arch string arch can be amd64 or arm64 (default "amd64") --arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "master") --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--log-level string set the controllers log level (default "info") --log-level logLevel log level, available options are: (debug, info, error) (default info)
-n, --namespace string the namespace scope for this operation (default "gotk-system") -n, --namespace string the namespace scope for this operation (default "flux-system")
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects
@@ -73,5 +74,5 @@ gotk bootstrap github [flags]
### SEE ALSO ### SEE ALSO
* [gotk bootstrap](gotk_bootstrap.md) - Bootstrap toolkit components * [flux bootstrap](flux_bootstrap.md) - Bootstrap toolkit components

View File

@@ -1,4 +1,4 @@
## gotk bootstrap gitlab ## flux bootstrap gitlab
Bootstrap toolkit components in a GitLab repository Bootstrap toolkit components in a GitLab repository
@@ -11,7 +11,7 @@ If the toolkit components are present on the cluster,
the bootstrap command will perform an upgrade if needed. the bootstrap command will perform an upgrade if needed.
``` ```
gotk bootstrap gitlab [flags] flux bootstrap gitlab [flags]
``` ```
### Examples ### Examples
@@ -20,20 +20,23 @@ gotk bootstrap gitlab [flags]
# Create a GitLab API token and export it as an env var # Create a GitLab API token and export it as an env var
export GITLAB_TOKEN=<my-token> export GITLAB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitLab group # Run bootstrap for a private repo using HTTPS token authentication
gotk bootstrap gitlab --owner=<group> --repository=<repo name> flux bootstrap gitlab --owner=<group> --repository=<repo name>
# Run bootstrap for a private repo using SSH authentication
flux bootstrap gitlab --owner=<group> --repository=<repo name> --ssh-hostname=gitlab.com
# Run bootstrap for a repository path # Run bootstrap for a repository path
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster flux bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account # Run bootstrap for a public repository on a personal account
gotk bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true flux bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on a GitLab server # Run bootstrap for a private repo hosted on a GitLab server
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain> flux bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a an existing repository with a branch named main # Run bootstrap for a an existing repository with a branch named main
gotk bootstrap gitlab --owner=<organization> --repository=<repo name> --branch=main flux bootstrap gitlab --owner=<organization> --repository=<repo name> --branch=main
``` ```
@@ -48,19 +51,20 @@ gotk bootstrap gitlab [flags]
--personal is personal repository --personal is personal repository
--private is private repository (default true) --private is private repository (default true)
--repository string GitLab repository name --repository string GitLab repository name
--ssh-hostname string GitLab SSH hostname, defaults to hostname if not specified --ssh-hostname string GitLab SSH hostname, when specified a deploy key will be added to the repository
``` ```
### Options inherited from parent commands ### Options inherited from parent commands
``` ```
--arch string arch can be amd64 or arm64 (default "amd64") --arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "master") --branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--log-level string set the controllers log level (default "info") --log-level logLevel log level, available options are: (debug, info, error) (default info)
-n, --namespace string the namespace scope for this operation (default "gotk-system") -n, --namespace string the namespace scope for this operation (default "flux-system")
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects
@@ -70,5 +74,5 @@ gotk bootstrap gitlab [flags]
### SEE ALSO ### SEE ALSO
* [gotk bootstrap](gotk_bootstrap.md) - Bootstrap toolkit components * [flux bootstrap](flux_bootstrap.md) - Bootstrap toolkit components

View File

@@ -1,4 +1,4 @@
## gotk check ## flux check
Check requirements and installation Check requirements and installation
@@ -8,17 +8,17 @@ The check command will perform a series of checks to validate that
the local environment is configured correctly and if the installed components are healthy. the local environment is configured correctly and if the installed components are healthy.
``` ```
gotk check [flags] flux check [flags]
``` ```
### Examples ### Examples
``` ```
# Run pre-installation checks # Run pre-installation checks
gotk check --pre flux check --pre
# Run installation checks # Run installation checks
gotk check flux check
``` ```
@@ -34,12 +34,12 @@ gotk check [flags]
``` ```
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
-n, --namespace string the namespace scope for this operation (default "gotk-system") -n, --namespace string the namespace scope for this operation (default "flux-system")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects
``` ```
### SEE ALSO ### SEE ALSO
* [gotk](gotk.md) - Command line utility for assembling Kubernetes CD pipelines * [flux](flux.md) - Command line utility for assembling Kubernetes CD pipelines

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