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

Compare commits

..

213 Commits

Author SHA1 Message Date
Hidde Beydals
e3d6461a80 Merge pull request #528 from fluxcd/update-components
Update helm-controller to v0.4.1
2020-11-30 13:21:29 +01:00
fluxcdbot
2bb582f7ed Update toolkit components 2020-11-30 12:10:44 +00:00
Stefan Prodan
2f9a52852f Merge pull request #527 from fluxcd/tenant-e2e
Add e2e test for create tenant
2020-11-30 12:25:31 +02:00
Stefan Prodan
137f083b4d Add e2e test for create tenant
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-30 12:16:01 +02:00
Stefan Prodan
11f4c54a40 Merge pull request #525 from fluxcd/fixes
Fix tenant and reconcile commands
2020-11-30 11:46:36 +02:00
Stefan Prodan
c813eaf6d1 Do not try to reconcile a suspended object
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-30 11:39:55 +02:00
Stefan Prodan
ffdaa9dfe9 Fix tenant service account binding
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-30 10:28:03 +02:00
Stefan Prodan
182928002b Merge pull request #526 from phillebaba/add-maintainer
Add Philip Laine to maintainer list
2020-11-30 10:27:29 +02:00
Philip Laine
7222af2b7e Add Philip Laine to maintainer list
Signed-off-by: Philip Laine <philip.laine@xenit.se>
2020-11-29 19:17:51 +01:00
Stefan Prodan
034ead5272 Merge pull request #521 from fluxcd/improve-install-script-arch-detection
Improve installer list match for arm arches
2020-11-29 14:19:48 +02:00
Aurel Canciu
eca1f19e95 Improve installer list match for arm arches
`uname -m` will print out architecture codenames based on UTS_MACHINE
and COMPAT_UTS_MACHINE kernel defined constants. These extra values will
ensure the right version of the arm binary is installed on most Linux
systems running on ARM CPUs.

Signed-off-by: Aurel Canciu <aurelcanciu@gmail.com>
2020-11-27 19:38:06 +02:00
Michael Bridgen
ec70c14649 Merge pull request #520 from fluxcd/fluxv2-maintainers
Note the shared Flux v2 maintainers and team
2020-11-27 16:05:07 +00:00
Michael Bridgen
65d906a735 Note the shared Flux v2 maintainers and team
Many of the GitOps Toolkit controllers will share maintainers with
this repo, acting as the central Flux v2 repo. For convenience of
tracking membership and applying permissions, we can share maintainers
by:

 - referring to the MAINTAINERS file here, from elsewhere;
 - making a GitHub team that tracks those shared maintainers.

This commit makes a note of this in the MAINTAINERS file, to inform
people of the arrangement, and remind them to keep the team up to
date.

Signed-off-by: Michael Bridgen <michael@weave.works>
2020-11-27 12:44:17 +00:00
Stefan Prodan
b981bae1db Merge pull request #519 from fluxcd/service-account
Add service account arg to create commands
2020-11-27 12:28:25 +02:00
Stefan Prodan
d2df9ccf33 Add service account arg to create commands
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-27 12:20:55 +02:00
Stefan Prodan
5e51f51449 Merge pull request #518 from fluxcd/docs-upgrade
Add upgrade docs to install guide
2020-11-27 12:20:07 +02:00
Stefan Prodan
2c044a27e4 Add upgrade docs to install guide
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-27 08:44:34 +02:00
Hidde Beydals
d274a1115e Merge pull request #516 from fluxcd/update-components
Update source-controller to v0.4.1
2020-11-26 19:27:32 +01:00
fluxcdbot
bfae2899f3 Update toolkit components 2020-11-26 18:16:36 +00:00
Stefan Prodan
5352a7e13a Merge pull request #513 from fluxcd/update-components
Update toolkit components to v0.4.0
2020-11-26 18:45:38 +02:00
fluxcdbot
c49f9ef26a Update toolkit components 2020-11-26 16:32:23 +00:00
Stefan Prodan
4a7376c5f5 Merge pull request #512 from fluxcd/suspend-sources
Implement suspend/resume commands for sources
2020-11-26 17:25:47 +02:00
Stefan Prodan
567ce7f987 Add suspend status to get sources commands
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-26 17:17:50 +02:00
Stefan Prodan
26bc0a8100 Add source suspend/resume commands to docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-26 16:45:25 +02:00
Stefan Prodan
e7ff319685 Add resume source commands
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-26 16:40:54 +02:00
Stefan Prodan
072138deff Add suspend source commands
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-26 16:22:47 +02:00
Stefan Prodan
dd8dc90c1e Update source-controller to v0.4.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-26 16:20:00 +02:00
Stefan Prodan
8f1da33375 Merge pull request #511 from fluxcd/kubectl
Set kubecontext and kubeconfig for kubectl exec
2020-11-26 12:04:46 +02:00
Stefan Prodan
c02fbc2794 Set kubecontext and kubeconfig for kubectl exec
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-26 11:53:08 +02:00
Stefan Prodan
371db07108 Merge pull request #506 from chanwit/sops-doc
Add a note not to apply SOPS encrypted secrets directly
2020-11-25 17:49:49 +02:00
Chanwit Kaewkasi
99f5dbf16b add a note not to apply encrypted secrets directly
Signed-off-by: Chanwit Kaewkasi <chanwit@gmail.com>
2020-11-25 22:26:52 +07:00
Stefan Prodan
0db06c8962 Merge pull request #505 from fluxcd/tenant-sa
Add service accounts to tenant command
2020-11-25 17:22:49 +02:00
Stefan Prodan
a8e5876b2e Add create tenant to CLI docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-25 16:42:52 +02:00
Stefan Prodan
8273851b73 Add service accounts to tenant command
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-25 16:39:00 +02:00
Stefan Prodan
c2967240bb Merge pull request #501 from mewzherder/patch-6
Update Upcoming events w/ Nov 30 talk
2020-11-25 16:38:35 +02:00
mewzherder
282a6270c8 Update Upcoming events w/ Nov 30 talk
Signed-off-by: mewzherder <tamao@weave.works>
2020-11-24 09:03:10 -08:00
Stefan Prodan
1b299fad90 Merge pull request #498 from fluxcd/gh-action
Add GitHub Action for Flux CLI
2020-11-23 19:15:34 +02:00
Stefan Prodan
aa8dced7ad Add GitHub Action for Flux CLI
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-23 17:58:06 +02:00
Stefan Prodan
050ba951b0 Merge pull request #496 from gtseres/main
Add reference to the bootstrap migration
2020-11-23 15:49:53 +02:00
George Tseres
5e47c16099 Update docs/guides/flux-v1-migration.md
Co-authored-by: Stefan Prodan <stefan.prodan@gmail.com>
Signed-off-by: gtseres <george.tseres@gmail.com>
2020-11-23 15:39:39 +02:00
George Tseres
902db4c732 Add reference to the bootstrap migration
Signed-off-by: gtseres <george.tseres@gmail.com>
2020-11-23 15:28:42 +02:00
Stefan Prodan
86462fbee6 Merge pull request #491 from fluxcd/hr-values-from
Add values-from arg to create helmrelease cmd
2020-11-23 11:57:34 +02:00
Stefan Prodan
48bed79439 Add values-from arg to create helmrelease cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-23 11:29:35 +02:00
Stefan Prodan
26b61c2b6b Merge pull request #492 from fluxcd/hr-kustomize-generators
Add docs on how to use Kustomize to generate Helm values
2020-11-23 10:18:54 +02:00
Stefan Prodan
3b2253ddc0 Add docs on how to use Kustomize to generate Helm values
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-22 11:30:35 +02:00
Stefan Prodan
5ddcb39129 Merge pull request #487 from fluxcd/sops-aws
Add AWS IAM role example to SOPS docs
2020-11-20 15:34:59 +02:00
Stefan Prodan
59adef5bcc Add AWS IAM role example to SOPS docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-20 15:26:10 +02:00
Stefan Prodan
875aefc8dd Merge pull request #486 from relu/add-relu-to-maintainers
Add @relu to maintainers list
2020-11-20 15:20:35 +02:00
Aurel Canciu
0dbc9d213e Add @relu to maintainers list
Signed-off-by: Aurel Canciu <aurelcanciu@gmail.com>
2020-11-20 15:03:15 +02:00
Hidde Beydals
9f4c53e321 Merge pull request #484 from fluxcd/source-reconcile-at
Utilize LastHandledReconcileAt for source reconcile commands
2020-11-20 13:35:27 +01:00
Hidde Beydals
3c8716f6ac Utilize LastHandledReconcileAt for reconcile cmds
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-11-20 13:28:28 +01:00
Hidde Beydals
1a7f31ae2e Merge pull request #485 from fluxcd/update-components
Update toolkit component manifests to v0.3.0
2020-11-20 13:28:18 +01:00
fluxcdbot
64ad69acfe Update toolkit components 2020-11-20 12:21:02 +00:00
Stefan Prodan
9f47b55aa9 Merge pull request #474 from relu/adopt-k8s-conditions
Update components to v0.3.0
2020-11-20 13:46:11 +02:00
Aurel Canciu
53a1db0703 Adjustments to support new sa name in kustomize
Supporting changes in fluxcd/kustomize-controller#180

Signed-off-by: Aurel Canciu <aurelcanciu@gmail.com>
2020-11-20 13:36:36 +02:00
Aurel Canciu
2a789ec705 Refactor to adopt k8s standardized Condition type
Updates to use metav1.Condition type and removes references for
deprecated corev1.Condition* constants and uses the new k8s api/meta
helpers in place of the old pkg/apis/meta types.

Signed-off-by: Aurel Canciu <aurelcanciu@gmail.com>
2020-11-20 13:19:40 +02:00
Hidde Beydals
3047b25193 Merge pull request #476 from fluxcd/update-components
Update helm-controller to v0.2.2
2020-11-18 12:36:12 +01:00
fluxcdbot
f66399cdc0 Update toolkit components 2020-11-18 11:14:54 +00:00
Stefan Prodan
37fb0f632b Merge pull request #473 from fluxcd/update-components
Update helm-controller to v0.2.1
2020-11-17 13:59:54 +02:00
fluxcdbot
e5dd0c7ff8 Update toolkit components 2020-11-17 11:48:54 +00:00
Stefan Prodan
51392cd54c Merge pull request #468 from RossyWhite/fix-dryrun-option
Fix --dry-run option
2020-11-16 17:31:42 +02:00
RossyWhite
02bcb4ff3c Fix --dry-run option
Signed-off-by: RossyWhite <daikishiroi@gmail.com>
2020-11-16 18:03:08 +09:00
Stefan Prodan
d84297a5b5 Merge pull request #463 from fluxcd/update-components
Update source-controller to v0.2.2
2020-11-12 18:24:28 +02:00
fluxcdbot
c3876e30a9 Update toolkit components 2020-11-12 16:11:58 +00:00
Stefan Prodan
10711ed780 Merge pull request #462 from fluxcd/update-components
Update kustomize-controller to v0.2.2
2020-11-12 17:29:43 +02:00
fluxcdbot
de4e266e33 Update toolkit components 2020-11-12 15:17:44 +00:00
Stefan Prodan
15442969f8 Merge pull request #461 from fluxcd/fix-install-path
Fix cluster path in install docs
2020-11-12 16:30:08 +02:00
Stefan Prodan
bed48ada82 Fix cluster path in install docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-12 16:14:05 +02:00
Stefan Prodan
a66004f567 Merge pull request #458 from fluxcd/kind-update
Update engineerd/setup-kind to v0.5.0
2020-11-12 12:16:07 +02:00
Stefan Prodan
72a4e3b3b8 Update engineerd/setup-kind to v0.5.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-12 12:03:25 +02:00
Stefan Prodan
16761e4fca Merge pull request #455 from fluxcd/fix-gh-https
Fix GitHub bootstrap with token auth
2020-11-12 12:01:58 +02:00
Stefan Prodan
ba34a6d401 Fix GitHub bootstrap with token auth
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-12 09:47:04 +02:00
Stefan Prodan
23912e4091 Merge pull request #452 from fluxcd/terraform-provider
Add the Terraform provider to install docs
2020-11-11 17:26:18 +02:00
Stefan Prodan
17468cb5f5 Add the Terraform provider to install docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-11 17:16:14 +02:00
Stefan Prodan
5ea7aa0a75 Merge pull request #450 from fluxcd/ssh-host-github
Add token authentication option to bootstrap
2020-11-11 16:49:43 +02:00
Stefan Prodan
7792cd6a10 Add token authentication option to bootstrap
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-11 16:41:18 +02:00
Stefan Prodan
237d186207 Merge pull request #444 from arodus/patch-1
Fix create tenant command export
2020-11-11 14:27:59 +02:00
Sebastian Karasek
c41487598e Fix create tenant command export
Signed-off-by: Sebastian Karasek <sebastian@karasek.io>
2020-11-11 13:15:17 +01:00
Stefan Prodan
2c0aa3c3af Merge pull request #446 from fluxcd/helm-values-file
Add values file example to Helm docs
2020-11-11 11:46:45 +02:00
Stefan Prodan
cedb33b2b9 Add values file example to Helm docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-11 10:26:03 +02:00
Stefan Prodan
06a3aa2c60 Merge pull request #442 from fluxcd/helm-upgrade-guide-typo
docs: fix typo
2020-11-11 09:08:36 +02:00
Hidde Beydals
3fadc94711 docs: include releaseName in HelmRelease example
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-11-10 18:27:06 +01:00
Hidde Beydals
61d02bf5e4 docs: fix typo
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-11-10 18:20:05 +01:00
Stefan Prodan
a62976461e Merge pull request #439 from fluxcd/update-components
Update notification-controller to v0.2.1
2020-11-09 17:46:07 +02:00
fluxcdbot
d7a893acf9 Update toolkit components 2020-11-09 15:37:09 +00:00
Stefan Prodan
0c67e75fb6 Merge pull request #438 from fluxcd/check-print-images
List component images in check cmd
2020-11-09 17:12:13 +02:00
Stefan Prodan
e6b84c4cfc List components images in check cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-09 16:10:06 +02:00
Stefan Prodan
5d2e793386 Merge pull request #437 from nguyenductoan/docs-fix-typo
docs: fix typo
2020-11-09 14:31:43 +02:00
nguyenductoan
f0517906b7 docs: fix typo
Signed-off-by: nguyenductoan <ductoan593@gmail.com>
2020-11-09 18:43:24 +07:00
Stefan Prodan
16fa167931 Merge pull request #435 from phillebaba/sops-azure-auth
Update authentication hint for Azure
2020-11-08 09:47:16 +02:00
Philip Laine
b036999b8c Update authentication hint for Azure
Signed-off-by: Philip Laine <philip.laine@xenit.se>
2020-11-07 16:05:18 +01:00
Stefan Prodan
1911766b7b Merge pull request #434 from fluxcd/target-namespace-ks
Add target namespace to create ks cmd
2020-11-07 14:14:57 +02:00
Stefan Prodan
9f7835d818 Add target namespace to create ks cmd
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-07 14:01:40 +02:00
Stefan Prodan
1df45e4857 Merge pull request #433 from fluxcd/customize-flux-manifests
Add customize Flux manifests section to install docs
2020-11-07 13:58:40 +02:00
Stefan Prodan
47a1743965 Add customize Flux manifests section to install docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-07 13:44:47 +02:00
Hidde Beydals
d5844bbdaa Merge pull request #430 from fluxcd/migration-title-fix
Rename migration menu items
2020-11-06 13:08:57 +01:00
Hidde Beydals
128c87ab33 Rename migration menu items
As they are used in the metadata, instead of the title from the
document.

Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-11-06 12:58:51 +01:00
Hidde Beydals
f4adcae79a Merge pull request #425 from plod/main
docs: fixing command for expected output
2020-11-05 13:09:18 +01:00
Daniel Morgan
ba4df070cf fixing command for expected output
The \ before the comment here means the subsequent lines end up getting commented out (and then not exported), see: https://stackoverflow.com/questions/9522631/how-to-put-a-line-comment-for-a-multi-line-command

Signed-off-by: Daniel Morgan <daniel@morgan.cymru>
2020-11-05 11:53:57 +00:00
Hidde Beydals
ce4ecfb388 Merge pull request #421 from fluxcd/update-roadmap
Update Helm v3 feature parity roadmap
2020-11-04 21:35:10 +01:00
Hidde Beydals
e6006e0833 Update Helm v3 feature parity roadmap
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-11-04 21:21:58 +01:00
Michael Bridgen
d500cc0bd1 Merge pull request #422 from fluxcd/flux2-www-intro
Rework Flux2 website intro
2020-11-04 15:56:01 +00:00
Michael Bridgen
71995b4f83 Refine description of Sources
Signed-off-by: Michael Bridgen <michael@weave.works>
2020-11-04 15:38:23 +00:00
Michael Bridgen
932c91d022 Move gitops toolkit index under components/
Signed-off-by: Michael Bridgen <michael@weave.works>
2020-11-04 15:37:07 +00:00
Michael Bridgen
107e7424d1 Explain more up-front in website homepage
This commit

 - adds a brief explanation of what Flux does
 - makes the target features a little more self-explanatory
 - gives GitOps Toolkit its own index page

Signed-off-by: Michael Bridgen <michael@weave.works>
2020-11-04 15:37:02 +00:00
Michael Bridgen
c93181c0ad Add section on target users
Signed-off-by: Michael Bridgen <michael@weave.works>
2020-11-04 15:29:12 +00:00
Stefan Prodan
050fca6767 Merge pull request #423 from fluxcd/update-components
Update kustomize-controller to v0.2.1
2020-11-04 17:22:53 +02:00
fluxcdbot
71827b4a1a Update toolkit components 2020-11-04 15:11:46 +00:00
Stefan Prodan
1d0315bf5e Merge pull request #420 from fluxcd/tagline
Update tag line and featured talks
2020-11-04 15:35:45 +02:00
Stefan Prodan
b2b64e7283 Update tag line and featured talks
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-04 15:26:13 +02:00
Hidde Beydals
fe0e2edd37 Merge pull request #419 from fluxcd/helm-operator-guide
Add Helm Operator migration guide
2020-11-04 13:25:34 +01:00
Hidde Beydals
e5bb3d5645 Add Helm Operator migration guide
Signed-off-by: Hidde Beydals <hello@hidde.co>
2020-11-04 12:37:09 +01:00
Stefan Prodan
b88a99347b Merge pull request #416 from L3o-pold/main
Fix datasource for cluster Grafana dashboard
2020-11-03 19:30:51 +02:00
Léopold Jacquot
344a909d19 Fix datasource for cluster Grafana dashboard
Signed-off-by: Léopold Jacquot <leopold.jacquot@infomaniak.com>
2020-11-03 17:50:29 +01:00
Stefan Prodan
3cbe3aab25 Merge pull request #405 from relu/k8s-v1.19
Update k8s to 1.19 + kustomize 0.6.4
2020-11-03 13:06:19 +02:00
Aurel Canciu
9e3a4b1810 Update pkg components & k8s 1.19, kustomize 0.6.4
Signed-off-by: Aurel Canciu <aurelcanciu@gmail.com>
2020-11-02 23:56:20 +02:00
Stefan Prodan
e855bbaa29 Merge pull request #412 from sylr/kube-context
Add Kubernetes context option to CLI commands
2020-11-02 23:53:34 +02:00
Sylvain Rabot
78d7dca985 Add a --context option
Signed-off-by: Sylvain Rabot <sylvain@abstraction.fr>
2020-11-02 21:32:55 +01:00
Stefan Prodan
9da7ded976 Merge pull request #406 from fluxcd/docs-release-procedure
Add release procedure documentation
2020-11-02 10:53:44 +02:00
Stefan Prodan
427b107d0e Add release procedure documentation
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-11-01 09:33:36 +02:00
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
260 changed files with 8407 additions and 3250 deletions

View File

@@ -21,50 +21,50 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.14.x
go-version: 1.15.x
- name: Setup Kubernetes
uses: engineerd/setup-kind@v0.4.0
uses: engineerd/setup-kind@v0.5.0
- name: Set outputs
id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Build
run: sudo go build -o ./bin/gotk ./cmd/gotk
run: sudo go build -o ./bin/flux ./cmd/flux
- name: bootstrap init
run: |
./bin/gotk bootstrap github --manifests ./manifests/install/ \
./bin/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \
--branch=main \
--path=test-cluster
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: bootstrap no-op
run: |
./bin/gotk bootstrap github --manifests ./manifests/install/ \
./bin/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \
--branch=main \
--path=test-cluster
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: uninstall
run: |
./bin/gotk suspend kustomization gotk-system
./bin/gotk uninstall --resources --crds -s
./bin/flux suspend kustomization flux-system
./bin/flux uninstall --resources --crds -s
- name: bootstrap reinstall
run: |
./bin/gotk bootstrap github --manifests ./manifests/install/ \
./bin/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \
--branch=main \
--path=test-cluster
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: delete repository
run: |
./bin/gotk bootstrap github --manifests ./manifests/install/ \
./bin/flux bootstrap github --manifests ./manifests/install/ \
--owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \
--repository=flux-test-${{ steps.vars.outputs.sha_short }} \
--branch=main \
--path=test-cluster \
--delete
@@ -73,6 +73,6 @@ jobs:
- name: Debug failure
if: failure()
run: |
kubectl -n gotk-system get all
kubectl -n gotk-system logs deploy/source-controller
kubectl -n gotk-system logs deploy/kustomize-controller
kubectl -n flux-system get all
kubectl -n flux-system logs deploy/source-controller
kubectl -n flux-system logs deploy/kustomize-controller

View File

@@ -15,18 +15,14 @@ jobs:
- name: Copy assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SOURCE_VER: ${{ 'v0.1.1' }}
KUSTOMIZE_VER: ${{ 'v0.1.1' }}
HELM_VER: ${{ 'v0.1.2' }}
NOTIFICATION_VER: ${{ 'v0.1.1' }}
run: |
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_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/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
@@ -36,21 +32,21 @@ jobs:
{
# 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/spec/v1beta1/kustomization.md" > docs/components/kustomize/kustomization.md
}
{
# 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/spec/v2beta1/helmreleases.md" > docs/components/helm/helmreleases.md
}
{
# 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/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
@@ -60,7 +56,7 @@ jobs:
{
# install script
cp install/gotk.sh docs/install.sh
cp install/flux.sh docs/install.sh
}
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master

View File

@@ -24,7 +24,7 @@ jobs:
with:
go-version: 1.15.x
- name: Setup Kubernetes
uses: engineerd/setup-kind@v0.4.0
uses: engineerd/setup-kind@v0.5.0
with:
image: kindest/node:v1.16.9
- name: Run test
@@ -37,34 +37,34 @@ jobs:
exit 1
fi
- name: Build
run: sudo go build -o ./bin/gotk ./cmd/gotk
- name: gotk check --pre
run: sudo go build -o ./bin/flux ./cmd/flux
- name: flux check --pre
run: |
./bin/gotk check --pre
- name: gotk install --manifests
./bin/flux check --pre
- name: flux install --manifests
run: |
./bin/gotk install --manifests ./manifests/install/
- name: gotk create source git
./bin/flux install --manifests ./manifests/install/
- name: flux create source git
run: |
./bin/gotk create source git podinfo \
./bin/flux create source git podinfo \
--url https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.3"
- name: gotk create source git export apply
- name: flux create source git export apply
run: |
./bin/gotk create source git podinfo-export \
./bin/flux create source git podinfo-export \
--url https://github.com/stefanprodan/podinfo \
--tag-semver=">=3.2.3" \
--export | kubectl apply -f -
./bin/gotk delete source git podinfo-export --silent
- name: gotk get sources git
./bin/flux delete source git podinfo-export --silent
- name: flux get sources git
run: |
./bin/gotk get sources git
- name: gotk get sources git --all-namespaces
./bin/flux get sources git
- name: flux get sources git --all-namespaces
run: |
./bin/gotk get sources git --all-namespaces
- name: gotk create kustomization
./bin/flux get sources git --all-namespaces
- name: flux create kustomization
run: |
./bin/gotk create kustomization podinfo \
./bin/flux create kustomization podinfo \
--source=podinfo \
--path="./deploy/overlays/dev" \
--prune=true \
@@ -73,80 +73,90 @@ jobs:
--health-check="Deployment/frontend.dev" \
--health-check="Deployment/backend.dev" \
--health-check-timeout=3m
- name: gotk reconcile kustomization --with-source
- name: flux reconcile kustomization --with-source
run: |
./bin/gotk reconcile kustomization podinfo --with-source
- name: gotk get kustomizations
./bin/flux reconcile kustomization podinfo --with-source
- name: flux get kustomizations
run: |
./bin/gotk get kustomizations
- name: gotk get kustomizations --all-namespaces
./bin/flux get kustomizations
- name: flux get kustomizations --all-namespaces
run: |
./bin/gotk get kustomizations --all-namespaces
- name: gotk suspend kustomization
./bin/flux get kustomizations --all-namespaces
- name: flux suspend kustomization
run: |
./bin/gotk suspend kustomization podinfo
- name: gotk resume kustomization
./bin/flux suspend kustomization podinfo
- name: flux resume kustomization
run: |
./bin/gotk resume kustomization podinfo
- name: gotk export
./bin/flux resume kustomization podinfo
- name: flux export
run: |
./bin/gotk export source git --all
./bin/gotk export kustomization --all
- name: gotk delete kustomization
./bin/flux export source git --all
./bin/flux export kustomization --all
- name: flux delete kustomization
run: |
./bin/gotk delete kustomization podinfo --silent
- name: gotk create source helm
./bin/flux delete kustomization podinfo --silent
- name: flux create source helm
run: |
./bin/gotk create source helm podinfo \
./bin/flux create source helm podinfo \
--url https://stefanprodan.github.io/podinfo
- name: gotk create helmrelease --source=HelmRepository/podinfo
- name: flux create helmrelease --source=HelmRepository/podinfo
run: |
./bin/gotk create hr podinfo-helm \
./bin/flux create hr podinfo-helm \
--target-namespace=default \
--source=HelmRepository/podinfo \
--chart=podinfo \
--chart-version=">4.0.0 <5.0.0"
- name: gotk create helmrelease --source=GitRepository/podinfo
- name: flux create helmrelease --source=GitRepository/podinfo
run: |
./bin/gotk create hr podinfo-git \
./bin/flux create hr podinfo-git \
--target-namespace=default \
--source=GitRepository/podinfo \
--chart=./charts/podinfo
- name: gotk reconcile helmrelease --with-source
- name: flux reconcile helmrelease --with-source
run: |
./bin/gotk reconcile helmrelease podinfo-git --with-source
- name: gotk get helmreleases
./bin/flux reconcile helmrelease podinfo-git --with-source
- name: flux get helmreleases
run: |
./bin/gotk get helmreleases
- name: gotk get helmreleases --all-namespaces
./bin/flux get helmreleases
- name: flux get helmreleases --all-namespaces
run: |
./bin/gotk get helmreleases --all-namespaces
- name: gotk export helmrelease
./bin/flux get helmreleases --all-namespaces
- name: flux export helmrelease
run: |
./bin/gotk export hr --all
- name: gotk delete helmrelease podinfo-helm
./bin/flux export hr --all
- name: flux delete helmrelease podinfo-helm
run: |
./bin/gotk delete hr podinfo-helm --silent
- name: gotk delete helmrelease podinfo-git
./bin/flux delete hr podinfo-helm --silent
- name: flux delete helmrelease podinfo-git
run: |
./bin/gotk delete hr podinfo-git --silent
- name: gotk delete source helm
./bin/flux delete hr podinfo-git --silent
- name: flux delete source helm
run: |
./bin/gotk delete source helm podinfo --silent
- name: gotk delete source git
./bin/flux delete source helm podinfo --silent
- name: flux delete source git
run: |
./bin/gotk delete source git podinfo --silent
- name: gotk check
./bin/flux delete source git podinfo --silent
- name: flux create tenant
run: |
./bin/gotk check
- name: gotk uninstall
./bin/flux create tenant dev-team --with-namespace=apps
./bin/flux -n apps create source helm podinfo \
--url https://stefanprodan.github.io/podinfo
./bin/flux -n apps create hr podinfo-helm \
--source=HelmRepository/podinfo \
--chart=podinfo \
--chart-version="5.0.x" \
--service-account=dev-team
- name: flux check
run: |
./bin/gotk uninstall --crds --silent
./bin/flux check
- name: flux uninstall
run: |
./bin/flux uninstall --crds --silent
- name: Debug failure
if: failure()
run: |
kubectl version --client --short
kubectl -n gotk-system get all
kubectl -n gotk-system get kustomizations -oyaml
kubectl -n gotk-system logs deploy/source-controller
kubectl -n gotk-system logs deploy/kustomize-controller
kubectl -n flux-system get all
kubectl -n flux-system get kustomizations -oyaml
kubectl -n flux-system logs deploy/source-controller
kubectl -n flux-system logs deploy/kustomize-controller

View File

@@ -13,15 +13,13 @@ jobs:
- uses: actions/setup-go@v2
with:
go-version: "^1.14.x"
- run: go version
# Runs a set of commands to initialize and analyze with FOSSA
- name: run FOSSA analysis
env:
- name: Add GOPATH to GITHUB_ENV
run: echo "GOPATH=$(go env GOPATH)" >>"$GITHUB_ENV"
- name: Add GOPATH to GITHUB_PATH
run: echo "$GOPATH/bin" >>"$GITHUB_PATH"
- name: Run FOSSA scan and upload build data
uses: fossa-contrib/fossa-action@v1
with:
# FOSSA Push-Only API Token
FOSSA_API_KEY: '5ee8bf422db1471e0bcf2bcb289185de'
run: |
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
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
github-token: ${{ github.token }}

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

@@ -19,7 +19,7 @@ jobs:
bump_version() {
local RELEASE_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
local CURRENT_VERSION=$(sed -n "s/\(.*$1\/.*?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
# bump kustomize

View File

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

View File

@@ -1,8 +1,10 @@
# Contributing
The GitOps Toolkit is [Apache 2.0 licensed](https://github.com/fluxcd/toolkit/blob/master/LICENSE)
and accepts contributions via GitHub pull requests. This document outlines
some of the conventions on to make it easier to get your contribution accepted.
Flux is [Apache 2.0
licensed](https://github.com/fluxcd/flux2/blob/main/LICENSE) and
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
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.
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
[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
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).
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/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

View File

@@ -2,7 +2,17 @@ The maintainers are generally available in Slack at
https://cloud-native.slack.com in #flux (https://cloud-native.slack.com/messages/CLAJ40HV3)
(obtain an invitation at https://slack.cncf.io/).
These maintainers are shared with other Flux v2-related git
repositories under https://github.com/fluxcd, as noted in their
respective MAINTAINERS files.
For convenience, they are reflected in the GitHub team
@fluxcd/flux2-maintainers -- if the list here changes, that team also
should.
In alphabetical order:
Aurel Canciu, Sortlist <aurel@sortlist.com> (github: @relu, slack: relu)
Hidde Beydals, Weaveworks <hidde@weave.works> (github: @hiddeco, slack: hidde)
Philip Laine, Xenit <philip.laine@xenit.se> (github: @phillebaba, slack: phillebaba)
Stefan Prodan, Weaveworks <stefan@weave.works> (github: @stefanprodan, slack: stefanprodan)

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
@@ -15,14 +15,15 @@ test: tidy fmt vet docs
go test ./... -coverprofile cover.out
build:
CGO_ENABLED=0 go build -o ./bin/gotk ./cmd/gotk
CGO_ENABLED=0 go build -o ./bin/flux ./cmd/flux
install:
go install cmd/gotk
go install cmd/flux
.PHONY: 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:
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,27 +1,30 @@
# GitOps Toolkit
# Flux version 2
[![e2e](https://github.com/fluxcd/toolkit/workflows/e2e/badge.svg)](https://github.com/fluxcd/toolkit/actions)
[![report](https://goreportcard.com/badge/github.com/fluxcd/toolkit)](https://goreportcard.com/report/github.com/fluxcd/toolkit)
[![license](https://img.shields.io/github/license/fluxcd/toolkit.svg)](https://github.com/fluxcd/toolkit/blob/main/LICENSE)
[![release](https://img.shields.io/github/release/fluxcd/toolkit/all.svg)](https://github.com/fluxcd/toolkit/releases)
![overview](docs/diagrams/gitops-toolkit.png)
[![e2e](https://github.com/fluxcd/flux2/workflows/e2e/badge.svg)](https://github.com/fluxcd/flux2/actions)
[![report](https://goreportcard.com/badge/github.com/fluxcd/flux2)](https://goreportcard.com/report/github.com/fluxcd/flux2)
[![license](https://img.shields.io/github/license/fluxcd/flux2.svg)](https://github.com/fluxcd/flux2/blob/main/LICENSE)
[![release](https://img.shields.io/github/release/fluxcd/flux2/all.svg)](https://github.com/fluxcd/flux2/releases)
The GitOps Toolkit is a set of composable APIs and specialized tools
that can be used to build a Continuous Delivery platform on top of Kubernetes.
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.
These tools are build with Kubernetes controller-runtime libraries, and they
can be dynamically configured with Kubernetes custom resources either by
cluster admins or by other automated tools.
The GitOps Toolkit components interact with each other via Kubernetes
events and are responsible for the reconciliation of their designated API objects.
Flux version 2 ("v2") is built from the ground up to use 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.
## `gotk` installation
Flux v2 is constructed with the [GitOps Toolkit](#gitops-toolkit), a
set of composable APIs and specialized tools for building Continuous
Delivery on top of Kubernetes.
## Flux installation
With Homebrew:
```sh
brew tap fluxcd/tap
brew install gotk
brew install fluxcd/tap/flux
```
With Bash:
@@ -30,32 +33,45 @@ With Bash:
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
# enable completions in ~/.bash_profile
. <(gotk completion bash)
. <(flux completion bash)
```
Binaries for macOS and Linux AMD64/ARM64 are available to download on the
[release page](https://github.com/fluxcd/toolkit/releases).
Binaries for macOS, Windows and Linux AMD64/ARM are available to download on the
[release page](https://github.com/fluxcd/flux2/releases).
Verify that your cluster satisfies the prerequisites with:
```sh
gotk check --pre
flux check --pre
```
## Get started
To get started with the GitOps Toolkit, start [browsing the documentation](https://toolkit.fluxcd.io)
or get started with one of the following guides:
To get started with Flux, start [browsing the
documentation](https://toolkit.fluxcd.io) or get started with one of
the following guides:
- [Get started with 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/)
- [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/)
- [Setup Notifications](https://toolkit.fluxcd.io/guides/notifications/)
- [Setup Webhook Receivers](https://toolkit.fluxcd.io/guides/webhook-receivers/)
## 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.
![overview](docs/diagrams/gitops-toolkit.png)
You can use the toolkit to extend Flux, or to build your own systems
for continuous delivery -- see [the developer
guides](https://toolkit.fluxcd.io/dev-guides/source-watcher/).
### Components
- [Toolkit CLI](https://toolkit.fluxcd.io/cmd/gotk/)
- [Source Controller](https://toolkit.fluxcd.io/components/source/controller/)
- [GitRepository CRD](https://toolkit.fluxcd.io/components/source/gitrepositories/)
- [HelmRepository CRD](https://toolkit.fluxcd.io/components/source/helmrepositories/)
@@ -72,27 +88,27 @@ or get started with one of the following guides:
## 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))
- Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
- Join the [planning discussions](https://github.com/fluxcd/toolkit/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
- Join the [planning discussions](https://github.com/fluxcd/flux2/discussions)
- And if you are completely new to Flux and the GitOps Toolkit, take a look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
- To be part of the conversation about Flux's development, [join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
- Check out [how to contribute](CONTRIBUTING.md) to the project
## Featured Talks
- [12 Oct 2020 - Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
- [4 Sep 2020 - KubeCon/CloudNativeCon 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)
### Featured Talks
### Upcoming Meetups
- [19 October 2020 - GitOps Toolkit Guide Walk-through](https://www.meetup.com/GitOps-Community/events/273640196/)
Join us 10am PT / 18:00 BST) for to this special walk-through of the GitOps Toolkit!
Through this talk you'll be able to see how the upcoming Flux v2 and GitOps Toolkit will bring
great improvements to the Flux that you love! Watch or follow along as Leigh Capili shares some
highlights and then goes through Getting Started with GitOps Toolkit.
- 2 November 2020 - GitOps Toolkit Guide Walk-through - Part 2 (TBD)
- 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
- 30 Nov 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 3 with Leigh Capili](https://www.meetup.com/Weave-User-Group/events/274657228/)
We are looking forward to seeing you with us!

6
action/Dockerfile Normal file
View File

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

25
action/README.md Normal file
View File

@@ -0,0 +1,25 @@
# Flux GitHub Action
Example workflow:
```yaml
name: e2e
on:
push:
branches:
- '*'
jobs:
kubernetes:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Flux CLI
uses: fluxcd/flux2/action@main
- name: Setup Kubernetes Kind
uses: engineerd/setup-kind@v0.5.0
- name: Install Flux in Kubernetes Kind
run: flux install
```

15
action/action.yml Normal file
View File

@@ -0,0 +1,15 @@
name: 'kustomize'
description: 'A GitHub Action for running Flux commands'
author: 'Flux project'
branding:
icon: 'command'
color: 'blue'
inputs:
version:
description: 'strict semver'
required: false
runs:
using: 'docker'
image: 'Dockerfile'
args:
- ${{ inputs.version }}

40
action/entrypoint.sh Executable file
View File

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

View File

@@ -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");
you may not use this file except in compliance with the License.
@@ -19,12 +19,8 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
@@ -33,12 +29,14 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/pkg/install"
"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{
@@ -52,56 +50,47 @@ var (
bootstrapComponents []string
bootstrapRegistry string
bootstrapImagePullSecret string
bootstrapArch string
bootstrapBranch string
bootstrapWatchAllNamespaces bool
bootstrapNetworkPolicy bool
bootstrapLogLevel string
bootstrapManifestsPath string
bootstrapArch = flags.Arch(defaults.Arch)
bootstrapLogLevel = flags.LogLevel(defaults.LogLevel)
bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"}
bootstrapTokenAuth bool
)
const (
bootstrapDefaultBranch = "main"
bootstrapInstallManifest = "toolkit-components.yaml"
bootstrapSourceManifest = "toolkit-source.yaml"
bootstrapKustomizationManifest = "toolkit-kustomization.yaml"
bootstrapDefaultBranch = "main"
)
func init() {
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapVersion, "version", "v", defaultVersion,
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapVersion, "version", "v", defaults.Version,
"toolkit version")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapComponents, "components", defaultComponents,
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapComponents, "components", defaults.Components,
"list of components, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapRegistry, "registry", "ghcr.io/fluxcd",
"container registry where the toolkit images are published")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapImagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArch, "arch", "amd64",
"arch can be amd64 or arm64")
bootstrapCmd.PersistentFlags().Var(&bootstrapArch, "arch", bootstrapArch.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapBranch, "branch", bootstrapDefaultBranch,
"default branch (for GitHub this must match the default branch setting for the organization)")
rootCmd.AddCommand(bootstrapCmd)
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")
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapNetworkPolicy, "network-policy", true,
"deny ingress access to the toolkit controllers from other namespaces using network policies")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapLogLevel, "log-level", "info", "set the controllers log level")
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapTokenAuth, "token-auth", false,
"when enabled, the personal access token will be used instead of SSH deploy key")
bootstrapCmd.PersistentFlags().Var(&bootstrapLogLevel, "log-level", bootstrapLogLevel.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory")
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
rootCmd.AddCommand(bootstrapCmd)
}
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 {
if !utils.containsItemString(bootstrapComponents, component) {
if !utils.ContainsItemString(bootstrapComponents, component) {
return fmt.Errorf("component %s is required", component)
}
}
@@ -110,13 +99,6 @@ func bootstrapValidate() error {
}
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
manifestsDir := path.Join(tmpDir, targetPath, namespace)
if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil {
return "", fmt.Errorf("creating manifests dir failed: %w", err)
}
manifest := path.Join(manifestsDir, bootstrapInstallManifest)
opts := install.Options{
BaseURL: localManifests,
Version: bootstrapVersion,
@@ -124,17 +106,18 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
Components: bootstrapComponents,
Registry: bootstrapRegistry,
ImagePullSecret: bootstrapImagePullSecret,
Arch: bootstrapArch,
Arch: bootstrapArch.String(),
WatchAllNamespaces: bootstrapWatchAllNamespaces,
NetworkPolicy: bootstrapNetworkPolicy,
LogLevel: bootstrapLogLevel,
NotificationController: defaultNotification,
ManifestsFile: fmt.Sprintf("%s.yaml", namespace),
LogLevel: bootstrapLogLevel.String(),
NotificationController: defaults.NotificationController,
ManifestFile: defaults.ManifestFile,
Timeout: timeout,
TargetPath: targetPath,
}
if localManifests == "" {
opts.BaseURL = install.MakeDefaultOptions().BaseURL
opts.BaseURL = defaults.BaseURL
}
output, err := install.Generate(opts)
@@ -142,23 +125,23 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
return "", fmt.Errorf("generating install manifests failed: %w", err)
}
if err := ioutil.WriteFile(manifest, output, os.ModePerm); err != nil {
if filePath, err := output.WriteFile(tmpDir); err != nil {
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 {
command := fmt.Sprintf("kubectl apply -f %s", manifestPath)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
kubectlArgs := []string{"apply", "-f", manifestPath}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
}
for _, deployment := range components {
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
namespace, deployment, timeout.String())
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
}
}
@@ -166,72 +149,26 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
}
func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)
gitRepository := sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: sourcev1.GitRepositorySpec{
URL: url,
Interval: metav1.Duration{
Duration: interval,
},
Reference: &sourcev1.GitRepositoryRef{
Branch: branch,
},
SecretRef: &corev1.LocalObjectReference{
Name: name,
},
},
opts := sync.Options{
Name: name,
Namespace: namespace,
URL: url,
Branch: branch,
Interval: interval,
TargetPath: targetPath,
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
}
gitData, err := yaml.Marshal(gitRepository)
manifest, err := sync.Generate(opts)
if err != nil {
return fmt.Errorf("generating install manifests failed: %w", err)
}
if _, err := manifest.WriteFile(tmpDir); err != nil {
return err
}
if err := utils.writeFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, bootstrapSourceManifest)); 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 {
if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
return err
}
@@ -239,20 +176,22 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
}
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))
if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil {
kubectlArgs := []string{"apply", "-k", filepath.Join(tmpDir, targetPath, namespace)}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return err
}
logger.Waitingf("waiting for cluster sync")
var gitRepository sourcev1.GitRepository
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
}
var kustomization kustomizev1.Kustomization
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
}

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");
you may not use this file except in compliance with the License.
@@ -26,7 +26,10 @@ import (
"time"
"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"
)
@@ -34,7 +37,7 @@ var bootstrapGitHubCmd = &cobra.Command{
Use: "github",
Short: "Bootstrap toolkit components in a GitHub repository",
Long: `The bootstrap github command creates the GitHub repository if it doesn't exists and
commits the toolkit components manifests to the master branch.
commits the toolkit components manifests to the main branch.
Then it configures the target cluster to synchronize with the repository.
If the toolkit components are present on the cluster,
the bootstrap command will perform an upgrade if needed.`,
@@ -42,36 +45,40 @@ the bootstrap command will perform an upgrade if needed.`,
export GITHUB_TOKEN=<my-token>
# 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
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
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
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
gotk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a private repo hosted on GitHub Enterprise using SSH auth
flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> --ssh-hostname=<domain>
# Run bootstrap for a private repo hosted on GitHub Enterprise using HTTPS auth
flux bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> --token-auth
# 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,
}
var (
ghOwner string
ghRepository string
ghInterval time.Duration
ghPersonal bool
ghPrivate bool
ghHostname string
ghPath string
ghTeams []string
ghDelete bool
ghOwner string
ghRepository string
ghInterval time.Duration
ghPersonal bool
ghPrivate bool
ghHostname string
ghPath string
ghTeams []string
ghDelete bool
ghSSHHostname string
)
const (
@@ -86,6 +93,7 @@ func init() {
bootstrapGitHubCmd.Flags().BoolVar(&ghPrivate, "private", true, "is private repository")
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
bootstrapGitHubCmd.Flags().StringVar(&ghSSHHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitHubCmd.Flags().StringVar(&ghPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
bootstrapGitHubCmd.Flags().BoolVar(&ghDelete, "delete", false, "delete repository (used for testing only)")
@@ -104,11 +112,15 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
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 {
return err
}
if ghSSHHostname != "" {
repository.SSHHost = ghSSHHostname
}
provider := &git.GithubProvider{
IsPrivate: ghPrivate,
IsPersonal: ghPersonal,
@@ -154,7 +166,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
}
}
// clone repository and checkout the master branch
// clone repository and checkout the main branch
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
return err
}
@@ -183,7 +195,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("components are up to date")
}
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -200,34 +212,54 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("install completed")
}
// setup SSH deploy key
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)
}
repoURL := repository.GetURL()
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
if bootstrapTokenAuth {
// setup HTTPS token auth
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
Namespace: namespace,
},
StringData: map[string]string{
"username": "git",
"password": ghToken,
},
}
keyName := "gotk"
if ghPath != "" {
keyName = fmt.Sprintf("gotk-%s", ghPath)
}
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
} else if changed {
logger.Successf("deploy key configured")
}
} else {
// setup SSH deploy key
repoURL = repository.GetSSH()
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
logger.Actionf("configuring deploy key")
u, err := url.Parse(repository.GetSSH())
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
}
keyName := "flux"
if ghPath != "" {
keyName = fmt.Sprintf("flux-%s", ghPath)
}
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
return err
} else if changed {
logger.Successf("deploy key configured")
}
}
}
// configure repo synchronization
logger.Actionf("generating sync manifests")
if err := generateSyncManifests(repository.GetSSH(), bootstrapBranch, namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
if err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
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");
you may not use this file except in compliance with the License.
@@ -26,7 +26,10 @@ import (
"time"
"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"
)
@@ -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
export GITLAB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitLab group
gotk bootstrap gitlab --owner=<group> --repository=<repo name>
# Run bootstrap for a private repo using HTTPS token authentication
flux bootstrap gitlab --owner=<group> --repository=<repo name> --token-auth
# Run bootstrap for a private repo using SSH authentication
flux bootstrap gitlab --owner=<group> --repository=<repo name>
# 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
gotk bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
flux bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal --token-auth
# 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> --token-auth
# 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 --token-auth
`,
RunE: bootstrapGitLabCmdRun,
}
@@ -77,7 +83,7 @@ func init() {
bootstrapGitLabCmd.Flags().BoolVar(&glPrivate, "private", true, "is private repository")
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, defaults to hostname if not specified")
bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
bootstrapGitLabCmd.Flags().StringVar(&glPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
@@ -93,7 +99,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
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 {
return err
}
@@ -107,7 +113,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
IsPersonal: glPersonal,
}
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -172,34 +178,54 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("install completed")
}
// setup SSH deploy key
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)
}
repoURL := repository.GetURL()
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
if bootstrapTokenAuth {
// setup HTTPS token auth
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
Namespace: namespace,
},
StringData: map[string]string{
"username": "git",
"password": glToken,
},
}
keyName := "gotk"
if glPath != "" {
keyName = fmt.Sprintf("gotk-%s", glPath)
}
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
} else if changed {
logger.Successf("deploy key configured")
}
} else {
// setup SSH deploy key
repoURL = repository.GetSSH()
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
logger.Actionf("configuring deploy key")
u, err := url.Parse(repoURL)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
}
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")
}
}
}
// configure repo synchronization
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
}

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");
you may not use this file except in compliance with the License.
@@ -18,13 +18,15 @@ package main
import (
"context"
"fmt"
"encoding/json"
"os"
"os/exec"
"strings"
"github.com/blang/semver/v4"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/kubernetes"
"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
the local environment is configured correctly and if the installed components are healthy.`,
Example: ` # Run pre-installation checks
gotk check --pre
flux check --pre
# Run installation checks
gotk check
flux check
`,
RunE: runCheckCmd,
}
@@ -48,10 +50,14 @@ var (
checkComponents []string
)
type kubectlVersion struct {
ClientVersion *apimachineryversion.Info `json:"clientVersion"`
}
func init() {
checkCmd.Flags().BoolVarP(&checkPre, "pre", "", false,
"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")
rootCmd.AddCommand(checkCmd)
}
@@ -97,14 +103,20 @@ func kubectlCheck(ctx context.Context, version string) bool {
return false
}
command := "kubectl version --client --short | awk '{ print $3 }'"
output, err := utils.execCommand(ctx, ModeCapture, command)
kubectlArgs := []string{"version", "--client", "--output", "json"}
output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...)
if err != nil {
logger.Failuref("kubectl version can't be determined")
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 {
logger.Failuref("kubectl version can't be parsed")
return false
@@ -161,14 +173,17 @@ func componentsCheck() bool {
ok := true
for _, deployment := range checkComponents {
command := fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
namespace, deployment, timeout.String())
if output, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
kubectlArgs := []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...); err != nil {
logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
ok = false
} else {
logger.Successf("%s is healthy", deployment)
}
kubectlArgs = []string{"-n", namespace, "get", "deployment", deployment, "-o", "jsonpath=\"{..image}\""}
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...); err == nil {
logger.Actionf(strings.TrimPrefix(strings.TrimSuffix(output, "\""), "\""))
}
}
return ok
}

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");
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");
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",
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
# ~/.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) {
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");
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",
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:
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
`,

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");
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",
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
Windows:
cd "$env:USERPROFILE\Documents\WindowsPowerShell\Modules"
gotk completion >> gotk-completion.ps1
flux completion >> flux-completion.ps1
Linux:
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) {
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");
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",
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
# ~/.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}:
echo "${fpath// /\n}" | grep -i completion
gotk completions zsh > _gotk
flux completions zsh > _flux
mv _gotk ~/.oh-my-zsh/completions # oh-my-zsh
mv _gotk ~/.zprezto/modules/completion/external/src/ # zprezto
mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
`,
Run: func(cmd *cobra.Command, args []string) {
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");
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");
you may not use this file except in compliance with the License.
@@ -19,11 +19,14 @@ 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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -37,11 +40,11 @@ var createAlertCmd = &cobra.Command{
Short: "Create or update a Alert resource",
Long: "The create alert command generates a Alert resource.",
Example: ` # Create an Alert for kustomization events
gotk create alert \
flux create alert \
--event-severity info \
--event-source Kustomization/gotk-system \
--event-source Kustomization/flux-system \
--provider-ref slack \
gotk-system
flux-system
`,
RunE: createAlertCmdRun,
}
@@ -61,7 +64,7 @@ func init() {
func createAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("alert name is required")
return fmt.Errorf("Alert name is required")
}
name := args[0]
@@ -71,7 +74,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
eventSources := []notificationv1.CrossNamespaceObjectReference{}
for _, eventSource := range aEventSources {
kind, name := utils.parseObjectKindName(eventSource)
kind, name := utils.ParseObjectKindName(eventSource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
}
@@ -92,7 +95,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating alert")
logger.Generatef("generating Alert")
}
alert := notificationv1.Alert{
@@ -118,28 +121,28 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
logger.Actionf("applying alert")
if err := upsertAlert(ctx, kubeClient, alert); err != nil {
logger.Actionf("applying Alert")
namespacedName, err := upsertAlert(ctx, kubeClient, &alert)
if err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, name, namespace)); err != nil {
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("alert %s is ready", name)
logger.Successf("Alert %s is ready", name)
return nil
}
func upsertAlert(ctx context.Context, kubeClient client.Client, alert notificationv1.Alert) error {
func upsertAlert(ctx context.Context, kubeClient client.Client,
alert *notificationv1.Alert) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: alert.GetNamespace(),
Name: alert.GetName(),
@@ -149,44 +152,39 @@ func upsertAlert(ctx context.Context, kubeClient client.Client, alert notificati
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &alert); err != nil {
return err
if err := kubeClient.Create(ctx, alert); err != nil {
return namespacedName, err
} else {
logger.Successf("alert created")
return nil
logger.Successf("Alert created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = alert.Labels
existing.Spec = alert.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("alert updated")
return nil
alert = &existing
logger.Successf("Alert updated")
return namespacedName, nil
}
func isAlertReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isAlertReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
return func() (bool, error) {
var alert notificationv1.Alert
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alert)
err := kubeClient.Get(ctx, namespacedName, alert)
if err != nil {
return false, err
}
if c := meta.GetCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}

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");
you may not use this file except in compliance with the License.
@@ -19,17 +19,19 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"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{
@@ -37,14 +39,14 @@ var createAlertProviderCmd = &cobra.Command{
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
gotk create alert-provider slack \
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
gotk create alert-provider github-podinfo \
flux create alert-provider github-podinfo \
--type github \
--address https://github.com/stefanprodan/podinfo \
--secret-ref github-token
@@ -71,12 +73,12 @@ func init() {
func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("provider name is required")
return fmt.Errorf("Provider name is required")
}
name := args[0]
if apType == "" {
return fmt.Errorf("type is required")
return fmt.Errorf("Provider type is required")
}
sourceLabels, err := parseLabels()
@@ -85,10 +87,10 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating provider")
logger.Generatef("generating Provider")
}
alertProvider := notificationv1.Provider{
provider := notificationv1.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
@@ -99,88 +101,88 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
Channel: apChannel,
Username: apUsername,
Address: apAddress,
SecretRef: &corev1.LocalObjectReference{
Name: apSecretRef,
},
},
}
if apSecretRef != "" {
provider.Spec.SecretRef = &corev1.LocalObjectReference{
Name: apSecretRef,
}
}
if export {
return exportAlertProvider(alertProvider)
return exportAlertProvider(provider)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
logger.Actionf("applying provider")
if err := upsertAlertProvider(ctx, kubeClient, alertProvider); err != nil {
logger.Actionf("applying Provider")
namespacedName, err := upsertAlertProvider(ctx, kubeClient, &provider)
if err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Provider reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, name, namespace)); err != nil {
isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
return err
}
logger.Successf("provider %s is ready", name)
logger.Successf("Provider %s is ready", name)
return nil
}
func upsertAlertProvider(ctx context.Context, kubeClient client.Client, alertProvider notificationv1.Provider) error {
func upsertAlertProvider(ctx context.Context, kubeClient client.Client,
provider *notificationv1.Provider) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: alertProvider.GetNamespace(),
Name: alertProvider.GetName(),
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, &alertProvider); err != nil {
return err
if err := kubeClient.Create(ctx, provider); err != nil {
return namespacedName, err
} else {
logger.Successf("provider created")
return nil
logger.Successf("Provider created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = alertProvider.Labels
existing.Spec = alertProvider.Spec
existing.Labels = provider.Labels
existing.Spec = provider.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("provider updated")
return nil
provider = &existing
logger.Successf("Provider updated")
return namespacedName, nil
}
func isAlertProviderReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isAlertProviderReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, provider *notificationv1.Provider) wait.ConditionFunc {
return func() (bool, error) {
var alertProvider notificationv1.Provider
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alertProvider)
err := kubeClient.Get(ctx, namespacedName, provider)
if err != nil {
return false, err
}
if c := meta.GetCondition(alertProvider.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}

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");
you may not use this file except in compliance with the License.
@@ -19,14 +19,16 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"io/ioutil"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -34,7 +36,6 @@ import (
"sigs.k8s.io/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var createHelmReleaseCmd = &cobra.Command{
@@ -43,44 +44,52 @@ var createHelmReleaseCmd = &cobra.Command{
Short: "Create or update a HelmRelease resource",
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
gotk create hr podinfo \
flux create hr podinfo \
--interval=10m \
--source=HelmRepository/podinfo \
--chart=podinfo \
--chart-version=">4.0.0"
# Create a HelmRelease with a chart from a GitRepository source
gotk create hr podinfo \
flux create hr podinfo \
--interval=10m \
--source=GitRepository/podinfo \
--chart=./charts/podinfo
# Create a HelmRelease with a chart from a Bucket source
gotk create hr podinfo \
flux create hr podinfo \
--interval=10m \
--source=Bucket/podinfo \
--chart=./charts/podinfo
# Create a HelmRelease with values from a local YAML file
gotk create hr podinfo \
flux create hr podinfo \
--source=HelmRepository/podinfo \
--chart=podinfo \
--values=./my-values.yaml
# Create a HelmRelease with values from a Kubernetes secret
kubectl -n app create secret generic my-secret-values \
--from-file=values.yaml=/path/to/my-secret-values.yaml
flux -n app create hr podinfo \
--source=HelmRepository/podinfo \
--chart=podinfo \
--values-from=Secret/my-secret-values
# Create a HelmRelease with a custom release name
gotk create hr podinfo \
flux create hr podinfo \
--release-name=podinfo-dev
--source=HelmRepository/podinfo \
--chart=podinfo \
# Create a HelmRelease targeting another namespace than the resource
gotk create hr podinfo \
flux create hr podinfo \
--target-namespace=default \
--source=HelmRepository/podinfo \
--chart=podinfo
# Create a HelmRelease definition on disk without applying it on the cluster
gotk create hr podinfo \
flux create hr podinfo \
--source=HelmRepository/podinfo \
--chart=podinfo \
--values=./values.yaml \
@@ -91,42 +100,35 @@ var createHelmReleaseCmd = &cobra.Command{
var (
hrName string
hrSource string
hrSource flags.HelmChartSource
hrDependsOn []string
hrChart string
hrChartVersion string
hrTargetNamespace string
hrValuesFile string
hrValuesFrom flags.HelmReleaseValuesFrom
hrSAName string
)
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(&hrSource, "source", "", "source that contains the chart (<kind>/<name>)")
createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
createHelmReleaseCmd.Flags().Var(&hrSource, "source", hrSource.Description())
createHelmReleaseCmd.Flags().StringVar(&hrChart, "chart", "", "Helm chart name or path")
createHelmReleaseCmd.Flags().StringVar(&hrChartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
createHelmReleaseCmd.Flags().StringArrayVar(&hrDependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
createHelmReleaseCmd.Flags().StringVar(&hrTargetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
createHelmReleaseCmd.Flags().StringVar(&hrSAName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
createHelmReleaseCmd.Flags().StringVar(&hrValuesFile, "values", "", "local path to the values.yaml file")
createHelmReleaseCmd.Flags().Var(&hrValuesFrom, "values-from", hrValuesFrom.Description())
createCmd.AddCommand(createHelmReleaseCmd)
}
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("release name is required")
return fmt.Errorf("HelmRelease name is required")
}
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 == "" {
return fmt.Errorf("chart name or path is required")
}
@@ -137,7 +139,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating release")
logger.Generatef("generating HelmRelease")
}
helmRelease := helmv2.HelmRelease{
@@ -148,7 +150,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
},
Spec: helmv2.HelmReleaseSpec{
ReleaseName: hrName,
DependsOn: utils.makeDependsOn(hrDependsOn),
DependsOn: utils.MakeDependsOn(hrDependsOn),
Interval: metav1.Duration{
Duration: interval,
},
@@ -158,8 +160,8 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
Chart: hrChart,
Version: hrChartVersion,
SourceRef: helmv2.CrossNamespaceObjectReference{
Kind: sourceKind,
Name: sourceName,
Kind: hrSource.Kind,
Name: hrSource.Name,
},
},
},
@@ -167,6 +169,10 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
},
}
if hrSAName != "" {
helmRelease.Spec.ServiceAccountName = hrSAName
}
if hrValuesFile != "" {
data, err := ioutil.ReadFile(hrValuesFile)
if err != nil {
@@ -181,6 +187,13 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
helmRelease.Spec.Values = &apiextensionsv1.JSON{Raw: json}
}
if hrValuesFrom.String() != "" {
helmRelease.Spec.ValuesFrom = []helmv2.ValuesReference{{
Kind: hrValuesFrom.Kind,
Name: hrValuesFrom.Name,
}}
}
if export {
return exportHelmRelease(helmRelease)
}
@@ -188,48 +201,30 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
logger.Actionf("applying release")
if err := upsertHelmRelease(ctx, kubeClient, helmRelease); err != nil {
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)
logger.Actionf("applying HelmRelease")
namespacedName, err := upsertHelmRelease(ctx, kubeClient, &helmRelease)
if err != nil {
return fmt.Errorf("release failed: %w", err)
return err
}
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
} else {
return fmt.Errorf("reconciliation failed")
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return err
}
logger.Successf("HelmRelease %s is ready", name)
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
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{
Namespace: helmRelease.GetNamespace(),
Name: helmRelease.GetName(),
@@ -239,75 +234,39 @@ func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmReleas
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRelease); err != nil {
return err
if err := kubeClient.Create(ctx, helmRelease); err != nil {
return namespacedName, err
} else {
logger.Successf("release created")
return nil
logger.Successf("HelmRelease created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = helmRelease.Labels
existing.Spec = helmRelease.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("release updated")
return nil
helmRelease = &existing
logger.Successf("HelmRelease updated")
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) {
var helmChart sourcev1.HelmChart
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)
err := kubeClient.Get(ctx, namespacedName, helmRelease)
if err != nil {
return false, err
}
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
// Confirm the state we are observing is for the current generation
if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
return false, nil
}
return false, nil
return apimeta.IsStatusConditionTrue(helmRelease.Status.Conditions, meta.ReadyCondition), 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");
you may not use this file except in compliance with the License.
@@ -25,15 +25,17 @@ import (
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var createKsCmd = &cobra.Command{
@@ -42,7 +44,7 @@ var createKsCmd = &cobra.Command{
Short: "Create or update a Kustomization resource",
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
gotk create kustomization contour \
flux create kustomization contour \
--source=contour \
--path="./examples/contour/" \
--prune=true \
@@ -53,7 +55,7 @@ var createKsCmd = &cobra.Command{
--health-check-timeout=3m
# Create a Kustomization resource that depends on the previous one
gotk create kustomization webapp \
flux create kustomization webapp \
--depends-on=contour \
--source=webapp \
--path="./deploy/overlays/dev" \
@@ -62,7 +64,7 @@ var createKsCmd = &cobra.Command{
--validation=client
# Create a Kustomization resource that references a Bucket
gotk create kustomization secrets \
flux create kustomization secrets \
--source=Bucket/secrets \
--prune=true \
--interval=5m
@@ -71,7 +73,7 @@ var createKsCmd = &cobra.Command{
}
var (
ksSource string
ksSource flags.KustomizationSource
ksPath string
ksPrune bool
ksDependsOn []string
@@ -79,46 +81,32 @@ var (
ksHealthCheck []string
ksHealthTimeout time.Duration
ksSAName string
ksSANamespace string
ksDecryptionProvider string
ksDecryptionProvider flags.DecryptionProvider
ksDecryptionSecret string
ksTargetNamespace string
)
func init() {
createKsCmd.Flags().StringVar(&ksSource, "source", "",
"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().Var(&ksSource, "source", ksSource.Description())
createKsCmd.Flags().StringVar(&ksPath, "path", "./", "path to the directory containing a kustomization.yaml file")
createKsCmd.Flags().BoolVar(&ksPrune, "prune", false, "enable garbage collection")
createKsCmd.Flags().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
createKsCmd.Flags().DurationVar(&ksHealthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
createKsCmd.Flags().StringVar(&ksValidation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'")
createKsCmd.Flags().StringVar(&ksSAName, "sa-name", "", "service account name")
createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace")
createKsCmd.Flags().StringVar(&ksDecryptionProvider, "decryption-provider", "", "enables secrets decryption, provider can be 'sops'")
createKsCmd.Flags().StringVar(&ksSAName, "service-account", "", "the name of the service account to impersonate when reconciling this Kustomization")
createKsCmd.Flags().Var(&ksDecryptionProvider, "decryption-provider", ksDecryptionProvider.Description())
createKsCmd.Flags().StringVar(&ksDecryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
createKsCmd.Flags().StringVar(&ksTargetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
createCmd.AddCommand(createKsCmd)
}
func createKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("kustomization name is required")
return fmt.Errorf("Kustomization name is required")
}
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 == "" {
return fmt.Errorf("path is required")
}
@@ -127,7 +115,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating kustomization")
logger.Generatef("generating Kustomization")
}
ksLabels, err := parseLabels()
@@ -142,18 +130,19 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
Labels: ksLabels,
},
Spec: kustomizev1.KustomizationSpec{
DependsOn: utils.makeDependsOn(ksDependsOn),
DependsOn: utils.MakeDependsOn(ksDependsOn),
Interval: metav1.Duration{
Duration: interval,
},
Path: ksPath,
Prune: ksPrune,
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourceKind,
Name: sourceName,
Kind: ksSource.Kind,
Name: ksSource.Name,
},
Suspend: false,
Validation: ksValidation,
Suspend: false,
Validation: ksValidation,
TargetNamespace: ksTargetNamespace,
},
}
@@ -198,21 +187,13 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}
}
if ksSAName != "" && ksSANamespace != "" {
kustomization.Spec.ServiceAccount = &kustomizev1.ServiceAccount{
Name: ksSAName,
Namespace: ksSANamespace,
}
if ksSAName != "" {
kustomization.Spec.ServiceAccountName = ksSAName
}
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{
Provider: ksDecryptionProvider,
Provider: ksDecryptionProvider.String(),
}
if ksDecryptionSecret != "" {
@@ -227,43 +208,30 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
logger.Actionf("applying kustomization")
if err := upsertKustomization(ctx, kubeClient, kustomization); err != nil {
logger.Actionf("applying Kustomization")
namespacedName, err := upsertKustomization(ctx, kubeClient, &kustomization)
if err != nil {
return err
}
logger.Waitingf("waiting for kustomization sync")
logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err
}
logger.Successf("Kustomization %s is ready", name)
logger.Successf("kustomization %s is ready", name)
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")
}
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
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{
Namespace: kustomization.GetNamespace(),
Name: kustomization.GetName(),
@@ -273,44 +241,44 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomiz
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &kustomization); err != nil {
return err
if err := kubeClient.Create(ctx, kustomization); err != nil {
return namespacedName, err
} else {
logger.Successf("kustomization created")
return nil
logger.Successf("Kustomization created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = kustomization.Labels
existing.Spec = kustomization.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("kustomization updated")
return nil
kustomization = &existing
logger.Successf("Kustomization updated")
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) {
var kustomization kustomizev1.Kustomization
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomization)
err := kubeClient.Get(ctx, namespacedName, kustomization)
if err != nil {
return false, err
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}

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");
you may not use this file except in compliance with the License.
@@ -23,11 +23,13 @@ import (
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"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"
)
@@ -37,7 +39,7 @@ var createReceiverCmd = &cobra.Command{
Short: "Create or update a Receiver resource",
Long: "The create receiver command generates a Receiver resource.",
Example: ` # Create a Receiver
gotk create receiver github-receiver \
flux create receiver github-receiver \
--type github \
--event ping \
--event push \
@@ -65,12 +67,12 @@ func init() {
func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("receiver name is required")
return fmt.Errorf("Receiver name is required")
}
name := args[0]
if rcvType == "" {
return fmt.Errorf("type is required")
return fmt.Errorf("Receiver type is required")
}
if rcvSecretRef == "" {
@@ -79,7 +81,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
resources := []notificationv1.CrossNamespaceObjectReference{}
for _, resource := range rcvResources {
kind, name := utils.parseObjectKindName(resource)
kind, name := utils.ParseObjectKindName(resource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", resource)
}
@@ -100,7 +102,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating receiver")
logger.Generatef("generating Receiver")
}
receiver := notificationv1.Receiver{
@@ -127,39 +129,30 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
logger.Actionf("applying receiver")
if err := upsertReceiver(ctx, kubeClient, receiver); err != nil {
logger.Actionf("applying Receiver")
namespacedName, err := upsertReceiver(ctx, kubeClient, &receiver)
if err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, name, namespace)); err != nil {
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("receiver %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return fmt.Errorf("receiver sync failed: %w", 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) error {
func upsertReceiver(ctx context.Context, kubeClient client.Client,
receiver *notificationv1.Receiver) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: receiver.GetNamespace(),
Name: receiver.GetName(),
@@ -169,44 +162,39 @@ func upsertReceiver(ctx context.Context, kubeClient client.Client, receiver noti
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &receiver); err != nil {
return err
if err := kubeClient.Create(ctx, receiver); err != nil {
return namespacedName, err
} else {
logger.Successf("receiver created")
return nil
logger.Successf("Receiver created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = receiver.Labels
existing.Spec = receiver.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("receiver updated")
return nil
receiver = &existing
logger.Successf("Receiver updated")
return namespacedName, nil
}
func isReceiverReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isReceiverReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
return func() (bool, error) {
var receiver notificationv1.Receiver
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &receiver)
err := kubeClient.Get(ctx, namespacedName, receiver)
if err != nil {
return false, err
}
if c := meta.GetCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}

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");
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");
you may not use this file except in compliance with the License.
@@ -30,6 +30,8 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
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.
For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a Buckets using static authentication
gotk create source bucket podinfo \
flux create source bucket podinfo \
--bucket-name=podinfo \
--endpoint=minio.minio.svc.cluster.local:9000 \
--insecure=true \
@@ -49,7 +51,7 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
--interval=10m
# Create a source from an Amazon S3 Bucket using IAM authentication
gotk create source bucket podinfo \
flux create source bucket podinfo \
--bucket-name=podinfo \
--provider=aws \
--endpoint=s3.amazonaws.com \
@@ -61,37 +63,33 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
var (
sourceBucketName string
sourceBucketProvider string
sourceBucketProvider = flags.SourceBucketProvider(sourcev1.GenericBucketProvider)
sourceBucketEndpoint string
sourceBucketAccessKey string
sourceBucketSecretKey string
sourceBucketRegion string
sourceBucketInsecure bool
sourceBucketSecretRef string
)
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(&sourceBucketEndpoint, "endpoint", "", "the bucket endpoint address")
createSourceBucketCmd.Flags().StringVar(&sourceBucketAccessKey, "access-key", "", "the bucket access key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretKey, "secret-key", "", "the bucket secret key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketRegion, "region", "", "the bucket region")
createSourceBucketCmd.Flags().BoolVar(&sourceBucketInsecure, "insecure", false, "for when connecting to a non-TLS S3 HTTP endpoint")
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretRef, "secret-ref", "", "the name of an existing secret containing credentials")
createSourceCmd.AddCommand(createSourceBucketCmd)
}
func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
return fmt.Errorf("Bucket source name is required")
}
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 == "" {
return fmt.Errorf("bucket-name is required")
@@ -112,7 +110,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
}
defer os.RemoveAll(tmpDir)
bucket := sourcev1.Bucket{
bucket := &sourcev1.Bucket{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
@@ -120,7 +118,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
},
Spec: sourcev1.BucketSpec{
BucketName: sourceBucketName,
Provider: sourceBucketProvider,
Provider: sourceBucketProvider.String(),
Insecure: sourceBucketInsecure,
Endpoint: sourceBucketEndpoint,
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 {
return exportBucket(bucket)
return exportBucket(*bucket)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
logger.Generatef("generating source")
logger.Generatef("generating Bucket source")
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{},
}
if sourceBucketSecretRef == "" {
secretName := fmt.Sprintf("bucket-%s", name)
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
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{},
}
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")
if err := upsertBucket(ctx, kubeClient, bucket); err != nil {
logger.Actionf("applying Bucket source")
namespacedName, err := upsertBucket(ctx, kubeClient, bucket)
if err != nil {
return err
}
logger.Waitingf("waiting for download")
logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isBucketReady(ctx, kubeClient, name, namespace)); err != nil {
isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
return err
}
logger.Successf("Bucket source reconciliation completed")
logger.Successf("download completed")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
if bucket.Status.Artifact == nil {
return fmt.Errorf("Bucket source reconciliation but no artifact was found")
}
err = kubeClient.Get(ctx, namespacedName, &bucket)
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")
}
logger.Successf("fetched revision: %s", bucket.Status.Artifact.Revision)
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{
Namespace: bucket.GetNamespace(),
Name: bucket.GetName(),
@@ -209,22 +206,22 @@ func upsertBucket(ctx context.Context, kubeClient client.Client, bucket sourcev1
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &bucket); err != nil {
return err
if err := kubeClient.Create(ctx, bucket); err != nil {
return namespacedName, err
} else {
logger.Successf("source created")
return nil
logger.Successf("Bucket source created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = bucket.Labels
existing.Spec = bucket.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("source updated")
return nil
bucket = &existing
logger.Successf("Bucket source updated")
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");
you may not use this file except in compliance with the License.
@@ -20,17 +20,21 @@ import (
"context"
"crypto/elliptic"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"io/ioutil"
"net/url"
"os"
"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"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -47,35 +51,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 private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
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 \
--branch=master
# 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 \
--tag="3.2.3"
# 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 \
--tag-semver=">=3.2.0 <3.3.0"
# 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 \
--branch=master
# Create a source from a Git repository using SSH authentication and an
# ECDSA P-521 curve public key
gotk create source git podinfo \
flux create source git podinfo \
--url=ssh://git@github.com/stefanprodan/podinfo \
--branch=master \
--ssh-key-algorithm=ecdsa \
--ssh-ecdsa-curve=p521
# 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 \
--username=username \
--password=password
@@ -84,15 +88,17 @@ For private Git repositories, the basic authentication credentials are stored in
}
var (
sourceGitURL string
sourceGitBranch string
sourceGitTag string
sourceGitSemver string
sourceGitUsername string
sourceGitPassword string
sourceGitKeyAlgorithm PublicKeyAlgorithm = "rsa"
sourceGitRSABits RSAKeyBits = 2048
sourceGitECDSACurve = ECDSACurve{elliptic.P384()}
sourceGitURL string
sourceGitBranch string
sourceGitTag string
sourceGitSemver string
sourceGitUsername string
sourceGitPassword string
sourceGitKeyAlgorithm flags.PublicKeyAlgorithm = "rsa"
sourceGitRSABits flags.RSAKeyBits = 2048
sourceGitECDSACurve = flags.ECDSACurve{Curve: elliptic.P384()}
sourceGitSecretRef string
)
func init() {
@@ -105,13 +111,14 @@ func init() {
createSourceGitCmd.Flags().Var(&sourceGitKeyAlgorithm, "ssh-key-algorithm", sourceGitKeyAlgorithm.Description())
createSourceGitCmd.Flags().Var(&sourceGitRSABits, "ssh-rsa-bits", sourceGitRSABits.Description())
createSourceGitCmd.Flags().Var(&sourceGitECDSACurve, "ssh-ecdsa-curve", sourceGitECDSACurve.Description())
createSourceGitCmd.Flags().StringVarP(&sourceGitSecretRef, "secret-ref", "", "", "the name of an existing secret containing SSH or basic credentials")
createSourceCmd.AddCommand(createSourceGitCmd)
}
func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
return fmt.Errorf("GitRepository source name is required")
}
name := args[0]
@@ -159,20 +166,27 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
}
if export {
if sourceGitSecretRef != "" {
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceGitSecretRef,
}
}
return exportGit(gitRepository)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
withAuth := false
// 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")
pair, err := generateKeyPair(ctx)
if err != nil {
@@ -234,42 +248,35 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("authentication configured")
}
logger.Generatef("generating source")
logger.Generatef("generating GitRepository source")
if withAuth {
secretName := name
if sourceGitSecretRef != "" {
secretName = sourceGitSecretRef
}
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: name,
Name: secretName,
}
}
logger.Actionf("applying source")
if err := upsertGitRepository(ctx, kubeClient, gitRepository); err != nil {
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)
logger.Actionf("applying GitRepository source")
namespacedName, err := upsertGitRepository(ctx, kubeClient, &gitRepository)
if err != nil {
return fmt.Errorf("git sync failed: %w", err)
return err
}
if gitRepository.Status.Artifact != nil {
logger.Successf("fetched revision: %s", gitRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("git sync failed, artifact not found")
logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
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
}
@@ -330,7 +337,8 @@ func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.S
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{
Namespace: gitRepository.GetNamespace(),
Name: gitRepository.GetName(),
@@ -340,44 +348,39 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepos
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &gitRepository); err != nil {
return err
if err := kubeClient.Create(ctx, gitRepository); err != nil {
return namespacedName, err
} else {
logger.Successf("source created")
return nil
logger.Successf("GitRepository source created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = gitRepository.Labels
existing.Spec = gitRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("source updated")
return nil
gitRepository = &existing
logger.Successf("GitRepository source updated")
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) {
var gitRepository sourcev1.GitRepository
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &gitRepository)
err := kubeClient.Get(ctx, namespacedName, gitRepository)
if err != nil {
return false, err
}
if c := meta.GetCondition(gitRepository.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(gitRepository.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}

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");
you may not use this file except in compliance with the License.
@@ -23,14 +23,17 @@ import (
"net/url"
"os"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -41,18 +44,18 @@ var createSourceHelmCmd = &cobra.Command{
The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
Example: ` # Create a source from a public Helm repository
gotk create source helm podinfo \
flux create source helm podinfo \
--url=https://stefanprodan.github.io/podinfo \
--interval=10m
# 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 \
--username=username \
--password=password
# 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 \
--cert-file=./cert.crt \
--key-file=./key.crt \
@@ -62,12 +65,13 @@ For private Helm repositories, the basic authentication credentials are stored i
}
var (
sourceHelmURL string
sourceHelmUsername string
sourceHelmPassword string
sourceHelmCertFile string
sourceHelmKeyFile string
sourceHelmCAFile string
sourceHelmURL string
sourceHelmUsername string
sourceHelmPassword string
sourceHelmCertFile string
sourceHelmKeyFile string
sourceHelmCAFile string
sourceHelmSecretRef string
)
func init() {
@@ -77,16 +81,16 @@ func init() {
createSourceHelmCmd.Flags().StringVar(&sourceHelmCertFile, "cert-file", "", "TLS authentication cert file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmKeyFile, "key-file", "", "TLS authentication key file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmCAFile, "ca-file", "", "TLS authentication CA file path")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmSecretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
createSourceCmd.AddCommand(createSourceHelmCmd)
}
func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
return fmt.Errorf("HelmRepository source name is required")
}
name := args[0]
secretName := fmt.Sprintf("helm-%s", name)
if sourceHelmURL == "" {
return fmt.Errorf("url is required")
@@ -107,7 +111,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("url parse failed: %w", err)
}
helmRepository := sourcev1.HelmRepository{
helmRepository := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
@@ -121,98 +125,97 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
},
}
if sourceHelmSecretRef != "" {
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceHelmSecretRef,
}
}
if export {
return exportHelmRepository(helmRepository)
return exportHelmRepository(*helmRepository)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
logger.Generatef("generating source")
logger.Generatef("generating HelmRepository source")
if sourceHelmSecretRef == "" {
secretName := fmt.Sprintf("helm-%s", name)
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{},
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
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 != "" {
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")
}
logger.Actionf("applying source")
if err := upsertHelmRepository(ctx, kubeClient, helmRepository); err != nil {
logger.Actionf("applying HelmRepository source")
namespacedName, err := upsertHelmRepository(ctx, kubeClient, helmRepository)
if err != nil {
return err
}
logger.Waitingf("waiting for index download")
logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
return err
}
logger.Successf("HelmRepository source reconciliation completed")
logger.Successf("index download completed")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
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 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")
}
logger.Successf("fetched revision: %s", helmRepository.Status.Artifact.Revision)
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{
Namespace: helmRepository.GetNamespace(),
Name: helmRepository.GetName(),
@@ -222,22 +225,47 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRep
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRepository); err != nil {
return err
if err := kubeClient.Create(ctx, helmRepository); err != nil {
return namespacedName, err
} else {
logger.Successf("source created")
return nil
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = helmRepository.Labels
existing.Spec = helmRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
helmRepository = &existing
logger.Successf("source updated")
return nil
return namespacedName, nil
}
func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRepository *sourcev1.HelmRepository) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, helmRepository)
if err != nil {
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 := apimeta.FindStatusCondition(helmRepository.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -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");
you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
@@ -37,15 +38,15 @@ var createTenantCmd = &cobra.Command{
Use: "tenant",
Short: "Create or update a tenant",
Long: `
The create tenant command generates namespaces and role bindings to limit the
The create tenant command generates namespaces, service accounts and role bindings to limit the
reconcilers scope to the tenant namespaces.`,
Example: ` # Create a tenant with access to a namespace
gotk create tenant dev-team \
flux create tenant dev-team \
--with-namespace=frontend \
--label=environment=dev
# Generate tenant namespaces and role bindings in YAML format
gotk create tenant dev-team \
flux create tenant dev-team \
--with-namespace=frontend \
--with-namespace=backend \
--export > dev-team.yaml
@@ -54,8 +55,7 @@ reconcilers scope to the tenant namespaces.`,
}
const (
tenantLabel = "toolkit.fluxcd.io/tenant"
tenantRoleBinding = "gotk-reconciler"
tenantLabel = "toolkit.fluxcd.io/tenant"
)
var (
@@ -64,7 +64,6 @@ var (
)
func init() {
createTenantCmd.Hidden = true
createTenantCmd.Flags().StringSliceVar(&tenantNamespaces, "with-namespace", nil, "namespace belonging to this tenant")
createTenantCmd.Flags().StringVar(&tenantClusterRole, "cluster-role", "cluster-admin", "cluster role of the tenant role binding")
createCmd.AddCommand(createTenantCmd)
@@ -88,6 +87,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
}
var namespaces []corev1.Namespace
var accounts []corev1.ServiceAccount
var roleBindings []rbacv1.RoleBinding
for _, ns := range tenantNamespaces {
@@ -110,9 +110,19 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
}
namespaces = append(namespaces, namespace)
account := corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: tenant,
Namespace: ns,
Labels: objLabels,
},
}
accounts = append(accounts, account)
roleBinding := rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: tenantRoleBinding,
Name: fmt.Sprintf("%s-reconciler", tenant),
Namespace: ns,
Labels: objLabels,
},
@@ -122,6 +132,11 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
Kind: "User",
Name: fmt.Sprintf("gotk:%s:reconciler", ns),
},
{
Kind: "ServiceAccount",
Name: tenant,
Namespace: ns,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
@@ -134,7 +149,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
if export {
for i, _ := range tenantNamespaces {
if err := exportTenant(namespaces[i], roleBindings[1]); err != nil {
if err := exportTenant(namespaces[i], accounts[i], roleBindings[i]); err != nil {
return err
}
}
@@ -144,7 +159,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -155,6 +170,11 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
return err
}
logger.Actionf("applying service account %s", accounts[i].Name)
if err := upsertServiceAccount(ctx, kubeClient, accounts[i]); err != nil {
return err
}
logger.Actionf("applying role binding %s", roleBindings[i].Name)
if err := upsertRoleBinding(ctx, kubeClient, roleBindings[i]); err != nil {
return err
@@ -194,6 +214,35 @@ func upsertNamespace(ctx context.Context, kubeClient client.Client, namespace co
return nil
}
func upsertServiceAccount(ctx context.Context, kubeClient client.Client, account corev1.ServiceAccount) error {
namespacedName := types.NamespacedName{
Namespace: account.GetNamespace(),
Name: account.GetName(),
}
var existing corev1.ServiceAccount
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &account); err != nil {
return err
} else {
return nil
}
}
return err
}
if !equality.Semantic.DeepDerivative(account.Labels, existing.Labels) {
existing.Labels = account.Labels
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
}
}
return nil
}
func upsertRoleBinding(ctx context.Context, kubeClient client.Client, roleBinding rbacv1.RoleBinding) error {
namespacedName := types.NamespacedName{
Namespace: roleBinding.GetNamespace(),
@@ -227,7 +276,7 @@ func upsertRoleBinding(ctx context.Context, kubeClient client.Client, roleBindin
return nil
}
func exportTenant(namespace corev1.Namespace, roleBinding rbacv1.RoleBinding) error {
func exportTenant(namespace corev1.Namespace, account corev1.ServiceAccount, roleBinding rbacv1.RoleBinding) error {
namespace.TypeMeta = metav1.TypeMeta{
APIVersion: "v1",
Kind: "Namespace",
@@ -241,6 +290,19 @@ func exportTenant(namespace corev1.Namespace, roleBinding rbacv1.RoleBinding) er
data = bytes.Replace(data, []byte("spec: {}\n"), []byte(""), 1)
fmt.Println(resourceToString(data))
account.TypeMeta = metav1.TypeMeta{
APIVersion: "v1",
Kind: "ServiceAccount",
}
data, err = yaml.Marshal(account)
if err != nil {
return err
}
fmt.Println("---")
data = bytes.Replace(data, []byte("spec: {}\n"), []byte(""), 1)
fmt.Println(resourceToString(data))
roleBinding.TypeMeta = metav1.TypeMeta{
APIVersion: "rbac.authorization.k8s.io/v1",
Kind: "RoleBinding",

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");
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");
you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -32,7 +33,7 @@ var deleteAlertCmd = &cobra.Command{
Short: "Delete a Alert resource",
Long: "The delete alert command removes the given Alert from the cluster.",
Example: ` # Delete an Alert and the Kubernetes resources created by it
gotk delete alert main
flux delete alert main
`,
RunE: deleteAlertCmdRun,
}
@@ -50,7 +51,7 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -32,7 +33,7 @@ var deleteAlertProviderCmd = &cobra.Command{
Short: "Delete a Provider resource",
Long: "The delete alert-provider command removes the given Provider from the cluster.",
Example: ` # Delete a Provider and the Kubernetes resources created by it
gotk delete alert-provider slack
flux delete alert-provider slack
`,
RunE: deleteAlertProviderCmdRun,
}
@@ -50,7 +51,7 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
)
@@ -33,7 +34,7 @@ var deleteHelmReleaseCmd = &cobra.Command{
Short: "Delete a HelmRelease resource",
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
Example: ` # Delete a Helm release and the Kubernetes resources created by it
gotk delete hr podinfo
flux delete hr podinfo
`,
RunE: deleteHelmReleaseCmdRun,
}
@@ -51,7 +52,7 @@ func deleteHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
@@ -32,7 +33,7 @@ var deleteKsCmd = &cobra.Command{
Short: "Delete a Kustomization resource",
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
Example: ` # Delete a kustomization and the Kubernetes resources created by it
gotk delete kustomization podinfo
flux delete kustomization podinfo
`,
RunE: deleteKsCmdRun,
}
@@ -50,7 +51,7 @@ func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -32,7 +33,7 @@ var deleteReceiverCmd = &cobra.Command{
Short: "Delete a Receiver resource",
Long: "The delete receiver command removes the given Receiver from the cluster.",
Example: ` # Delete an Receiver and the Kubernetes resources created by it
gotk delete receiver main
flux delete receiver main
`,
RunE: deleteReceiverCmdRun,
}
@@ -50,7 +51,7 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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");
you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
@@ -31,7 +32,7 @@ var deleteSourceBucketCmd = &cobra.Command{
Short: "Delete a Bucket source",
Long: "The delete source bucket command deletes the given Bucket from the cluster.",
Example: ` # Delete a Bucket source
gotk delete source bucket podinfo
flux delete source bucket podinfo
`,
RunE: deleteSourceBucketCmdRun,
}
@@ -49,7 +50,7 @@ func deleteSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
@@ -31,7 +32,7 @@ var deleteSourceGitCmd = &cobra.Command{
Short: "Delete a GitRepository source",
Long: "The delete source git command deletes the given GitRepository from the cluster.",
Example: ` # Delete a Git repository
gotk delete source git podinfo
flux delete source git podinfo
`,
RunE: deleteSourceGitCmdRun,
}
@@ -49,7 +50,7 @@ func deleteSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
@@ -31,7 +32,7 @@ var deleteSourceHelmCmd = &cobra.Command{
Short: "Delete a HelmRepository source",
Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
Example: ` # Delete a Helm repository
gotk delete source helm podinfo
flux delete source helm podinfo
`,
RunE: deleteSourceHelmCmdRun,
}
@@ -49,7 +50,7 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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");
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/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -34,10 +35,10 @@ var exportAlertCmd = &cobra.Command{
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
gotk export alert --all > alerts.yaml
flux export alert --all > alerts.yaml
# Export a Alert
gotk export alert main > main.yaml
flux export alert main > main.yaml
`,
RunE: exportAlertCmdRun,
}
@@ -54,7 +55,7 @@ func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -34,10 +35,10 @@ var exportAlertProviderCmd = &cobra.Command{
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
gotk export alert-provider --all > alert-providers.yaml
flux export alert-provider --all > alert-providers.yaml
# Export a Provider
gotk export alert-provider slack > slack.yaml
flux export alert-provider slack > slack.yaml
`,
RunE: exportAlertProviderCmdRun,
}
@@ -54,7 +55,7 @@ func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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/yaml"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
)
@@ -35,10 +36,10 @@ var exportHelmReleaseCmd = &cobra.Command{
Short: "Export HelmRelease resources in YAML format",
Long: "The export helmrelease command exports one or all HelmRelease resources in YAML format.",
Example: ` # Export all HelmRelease resources
gotk export helmrelease --all > kustomizations.yaml
flux export helmrelease --all > kustomizations.yaml
# Export a HelmRelease
gotk export hr my-app > app-release.yaml
flux export hr my-app > app-release.yaml
`,
RunE: exportHelmReleaseCmdRun,
}
@@ -55,7 +56,7 @@ func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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/yaml"
"github.com/fluxcd/flux2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
)
@@ -35,10 +36,10 @@ var exportKsCmd = &cobra.Command{
Short: "Export Kustomization resources in YAML format",
Long: "The export kustomization command exports one or all Kustomization resources in YAML format.",
Example: ` # Export all Kustomization resources
gotk export kustomization --all > kustomizations.yaml
flux export kustomization --all > kustomizations.yaml
# Export a Kustomization
gotk export kustomization my-app > kustomization.yaml
flux export kustomization my-app > kustomization.yaml
`,
RunE: exportKsCmdRun,
}
@@ -55,7 +56,7 @@ func exportKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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/yaml"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -34,10 +35,10 @@ var exportReceiverCmd = &cobra.Command{
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
gotk export receiver --all > receivers.yaml
flux export receiver --all > receivers.yaml
# Export a Receiver
gotk export receiver main > main.yaml
flux export receiver main > main.yaml
`,
RunE: exportReceiverCmdRun,
}
@@ -54,7 +55,7 @@ func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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");
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/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -35,10 +36,10 @@ var exportSourceBucketCmd = &cobra.Command{
Short: "Export 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
gotk export source bucket --all > sources.yaml
flux export source bucket --all > sources.yaml
# 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,
}
@@ -55,7 +56,7 @@ func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -35,10 +36,10 @@ var exportSourceGitCmd = &cobra.Command{
Short: "Export 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
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
gotk export source git my-private-repo --with-credentials > source.yaml
flux export source git my-private-repo --with-credentials > source.yaml
`,
RunE: exportSourceGitCmdRun,
}
@@ -55,7 +56,7 @@ func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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/yaml"
"github.com/fluxcd/flux2/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
@@ -35,10 +36,10 @@ var exportSourceHelmCmd = &cobra.Command{
Short: "Export 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
gotk export source helm --all > sources.yaml
flux export source helm --all > sources.yaml
# 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,
}
@@ -55,7 +56,7 @@ func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
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");
you may not use this file except in compliance with the License.
@@ -23,9 +23,11 @@ import (
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
@@ -35,7 +37,7 @@ var getAlertCmd = &cobra.Command{
Short: "Get Alert statuses",
Long: "The get alert command prints the statuses of the resources.",
Example: ` # List all Alerts and their status
gotk get alerts
flux get alerts
`,
RunE: getAlertCmdRun,
}
@@ -48,7 +50,7 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -68,28 +70,26 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
header := []string{"Name", "Suspended", "Ready", "Message"}
header := []string{"Name", "Ready", "Message", "Suspended"}
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 {
if c := apimeta.FindStatusCondition(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,
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
}
} else {
row = []string{
alert.GetName(),
//alert.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
string(corev1.ConditionFalse),
string(metav1.ConditionFalse),
"waiting to be reconciled",
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
}
}
if allNamespaces {
@@ -97,6 +97,6 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
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");
you may not use this file except in compliance with the License.
@@ -21,9 +21,11 @@ import (
"os"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
@@ -33,7 +35,7 @@ var getAlertProviderCmd = &cobra.Command{
Short: "Get Provider statuses",
Long: "The get alert-provider command prints the statuses of the resources.",
Example: ` # List all Providers and their status
gotk get alert-providers
flux get alert-providers
`,
RunE: getAlertProviderCmdRun,
}
@@ -46,7 +48,7 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -73,7 +75,7 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
var rows [][]string
for _, provider := range list.Items {
row := []string{}
if c := meta.GetCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
provider.GetName(),
string(c.Status),
@@ -82,7 +84,7 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
} else {
row = []string{
provider.GetName(),
string(corev1.ConditionFalse),
string(metav1.ConditionFalse),
"waiting to be reconciled",
}
}
@@ -91,6 +93,6 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
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");
you may not use this file except in compliance with the License.
@@ -22,10 +22,12 @@ import (
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
@@ -37,7 +39,7 @@ var getHelmReleaseCmd = &cobra.Command{
Short: "Get HelmRelease statuses",
Long: "The get helmreleases command prints the statuses of the resources.",
Example: ` # List all Helm releases and their status
gotk get helmreleases
flux get helmreleases
`,
RunE: getHelmReleaseCmdRun,
}
@@ -50,7 +52,7 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -70,28 +72,28 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
header := []string{"Name", "Revision", "Suspended", "Ready", "Message"}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, helmRelease := range list.Items {
row := []string{}
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(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,
helmRelease.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(helmRelease.Spec.Suspend)),
}
} else {
row = []string{
helmRelease.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
helmRelease.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(helmRelease.Spec.Suspend)),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
@@ -99,6 +101,6 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
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");
you may not use this file except in compliance with the License.
@@ -22,11 +22,13 @@ import (
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -36,7 +38,7 @@ var getKsCmd = &cobra.Command{
Short: "Get Kustomization statuses",
Long: "The get kustomizations command prints the statuses of the resources.",
Example: ` # List all kustomizations and their status
gotk get kustomizations
flux get kustomizations
`,
RunE: getKsCmdRun,
}
@@ -49,7 +51,7 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -69,28 +71,28 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
header := []string{"Name", "Revision", "Suspended", "Ready", "Message"}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, kustomization := range list.Items {
row := []string{}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(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,
kustomization.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(kustomization.Spec.Suspend)),
}
} else {
row = []string{
kustomization.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
kustomization.Status.LastAppliedRevision,
strings.Title(strconv.FormatBool(kustomization.Spec.Suspend)),
string(corev1.ConditionFalse),
"waiting to be reconciled",
}
}
if allNamespaces {
@@ -98,6 +100,6 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
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");
you may not use this file except in compliance with the License.
@@ -23,9 +23,11 @@ import (
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
)
@@ -35,7 +37,7 @@ var getReceiverCmd = &cobra.Command{
Short: "Get Receiver statuses",
Long: "The get receiver command prints the statuses of the resources.",
Example: ` # List all Receiver and their status
gotk get receivers
flux get receivers
`,
RunE: getReceiverCmdRun,
}
@@ -48,7 +50,7 @@ func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -68,30 +70,30 @@ func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
header := []string{"Name", "Suspended", "Ready", "Message"}
header := []string{"Name", "Ready", "Message", "Suspended"}
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 {
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
receiver.GetName(),
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
string(c.Status),
c.Message,
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
}
} else {
row = []string{
receiver.GetName(),
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
string(corev1.ConditionFalse),
string(metav1.ConditionFalse),
"waiting to be reconciled",
strings.Title(strconv.FormatBool(receiver.Spec.Suspend)),
}
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
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");
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");
you may not use this file except in compliance with the License.
@@ -19,12 +19,16 @@ package main
import (
"context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -33,7 +37,10 @@ var getSourceBucketCmd = &cobra.Command{
Short: "Get Bucket source statuses",
Long: "The get sources bucket command prints the status of the Bucket sources.",
Example: ` # List all Buckets and their status
gotk get sources bucket
flux get sources bucket
# List buckets from all namespaces
flux get sources helm --all-namespaces
`,
RunE: getSourceBucketCmdRun,
}
@@ -46,7 +53,7 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -66,7 +73,7 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
header := []string{"Name", "Revision", "Ready", "Message"}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
@@ -77,19 +84,21 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
revision,
string(c.Status),
c.Message,
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
} else {
row = []string{
source.GetName(),
revision,
string(corev1.ConditionFalse),
string(metav1.ConditionFalse),
"waiting to be reconciled",
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
}
if allNamespaces {
@@ -97,6 +106,6 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

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"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var getSourceHelmChartCmd = &cobra.Command{
Use: "chart",
Short: "Get HelmChart statuses",
Long: "The get sources chart command prints the status of the HelmCharts.",
Example: ` # List all Helm charts and their status
flux get sources chart
# List Helm charts from all namespaces
flux get sources chart --all-namespaces
`,
RunE: getSourceHelmChartCmdRun,
}
func init() {
getSourceCmd.AddCommand(getSourceHelmChartCmd)
}
func getSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
var listOpts []client.ListOption
if !allNamespaces {
listOpts = append(listOpts, client.InNamespace(namespace))
}
var list sourcev1.HelmChartList
err = kubeClient.List(ctx, &list, listOpts...)
if err != nil {
return err
}
if len(list.Items) == 0 {
logger.Failuref("no chart sources found in %s namespace", namespace)
return nil
}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
var rows [][]string
for _, source := range list.Items {
var row []string
var revision string
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
string(c.Status),
c.Message,
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
} else {
row = []string{
source.GetName(),
string(metav1.ConditionFalse),
"waiting to be reconciled",
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
}
if allNamespaces {
row = append([]string{source.Namespace}, row...)
}
rows = append(rows, row)
}
utils.PrintTable(os.Stdout, header, rows)
return nil
}

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");
you may not use this file except in compliance with the License.
@@ -19,12 +19,16 @@ package main
import (
"context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -33,7 +37,10 @@ var getSourceGitCmd = &cobra.Command{
Short: "Get GitRepository source statuses",
Long: "The get sources git command prints the status of the GitRepository sources.",
Example: ` # List all Git repositories and their status
gotk get sources git
flux get sources git
# List Git repositories from all namespaces
flux get sources git --all-namespaces
`,
RunE: getSourceGitCmdRun,
}
@@ -46,7 +53,7 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -66,7 +73,7 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
header := []string{"Name", "Revision", "Ready", "Message"}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
@@ -77,19 +84,21 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
revision,
string(c.Status),
c.Message,
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
} else {
row = []string{
source.GetName(),
revision,
string(corev1.ConditionFalse),
string(metav1.ConditionFalse),
"waiting to be reconciled",
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
}
if allNamespaces {
@@ -97,6 +106,6 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
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");
you may not use this file except in compliance with the License.
@@ -19,12 +19,16 @@ package main
import (
"context"
"os"
"strconv"
"strings"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -33,7 +37,10 @@ var getSourceHelmCmd = &cobra.Command{
Short: "Get HelmRepository source statuses",
Long: "The get sources helm command prints the status of the HelmRepository sources.",
Example: ` # List all Helm repositories and their status
gotk get sources helm
flux get sources helm
# List Helm repositories from all namespaces
flux get sources helm --all-namespaces
`,
RunE: getSourceHelmCmdRun,
}
@@ -46,7 +53,7 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -66,7 +73,7 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
header := []string{"Name", "Revision", "Ready", "Message"}
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if allNamespaces {
header = append([]string{"Namespace"}, header...)
}
@@ -77,19 +84,21 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if source.GetArtifact() != nil {
revision = source.GetArtifact().Revision
}
if c := meta.GetCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
row = []string{
source.GetName(),
revision,
string(c.Status),
c.Message,
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
} else {
row = []string{
source.GetName(),
revision,
string(corev1.ConditionFalse),
string(metav1.ConditionFalse),
"waiting to be reconciled",
revision,
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
}
}
if allNamespaces {
@@ -97,6 +106,6 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
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");
you may not use this file except in compliance with the License.
@@ -21,12 +21,14 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"github.com/fluxcd/toolkit/pkg/install"
"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
)
var installCmd = &cobra.Command{
@@ -34,17 +36,17 @@ var installCmd = &cobra.Command{
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
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
gotk install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
# Dry-run install with manifests preview
gotk install --dry-run --verbose
# Dry-run install with manifests preview
flux install --dry-run --verbose
# Write install manifests to file
gotk install --export > gotk-system.yaml
# Write install manifests to file
flux install --export > flux-system.yaml
`,
RunE: installCmdRun,
}
@@ -57,10 +59,10 @@ var (
installComponents []string
installRegistry string
installImagePullSecret string
installArch string
installWatchAllNamespaces bool
installNetworkPolicy bool
installLogLevel string
installArch = flags.Arch(defaults.Arch)
installLogLevel = flags.LogLevel(defaults.LogLevel)
)
func init() {
@@ -68,35 +70,26 @@ func init() {
"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,
installCmd.Flags().StringVarP(&installVersion, "version", "v", defaults.Version,
"toolkit version")
installCmd.Flags().StringSliceVar(&installComponents, "components", defaultComponents,
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", "ghcr.io/fluxcd",
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().StringVar(&installArch, "arch", "amd64",
"arch can be amd64 or arm64")
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", true,
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().StringVar(&installLogLevel, "log-level", "info", "set the controllers log level")
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", true,
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 {
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()
@@ -117,12 +110,12 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
Components: installComponents,
Registry: installRegistry,
ImagePullSecret: installImagePullSecret,
Arch: installArch,
Arch: installArch.String(),
WatchAllNamespaces: installWatchAllNamespaces,
NetworkPolicy: installNetworkPolicy,
LogLevel: installLogLevel,
NotificationController: defaultNotification,
ManifestsFile: fmt.Sprintf("%s.yaml", namespace),
LogLevel: installLogLevel.String(),
NotificationController: defaults.NotificationController,
ManifestFile: fmt.Sprintf("%s.yaml", namespace),
Timeout: timeout,
}
@@ -130,42 +123,39 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
opts.BaseURL = install.MakeDefaultOptions().BaseURL
}
output, err := install.Generate(opts)
manifest, err := install.Generate(opts)
if err != nil {
return fmt.Errorf("install failed: %w", err)
}
manifest := path.Join(tmpDir, fmt.Sprintf("%s.yaml", namespace))
if err := ioutil.WriteFile(manifest, output, os.ModePerm); err != nil {
if _, err := manifest.WriteFile(tmpDir); err != nil {
return fmt.Errorf("install failed: %w", err)
}
yaml := string(output)
if verbose {
fmt.Print(yaml)
fmt.Print(manifest.Content)
} else if installExport {
fmt.Println("---")
fmt.Println("# GitOps Toolkit revision", installVersion)
fmt.Println("# Components:", strings.Join(installComponents, ","))
fmt.Print(yaml)
fmt.Print(manifest.Content)
fmt.Println("---")
return nil
}
logger.Successf("manifests build completed")
logger.Actionf("installing components in %s namespace", namespace)
applyOutput := ModeStderrOS
applyOutput := utils.ModeStderrOS
if verbose {
applyOutput = ModeOS
}
dryRun := ""
if installDryRun {
dryRun = "--dry-run=client"
applyOutput = ModeOS
applyOutput = utils.ModeOS
}
command := fmt.Sprintf("kubectl apply -f %s %s", manifest, dryRun)
if _, err := utils.execCommand(ctx, applyOutput, command); err != nil {
kubectlArgs := []string{"apply", "-f", filepath.Join(tmpDir, manifest.Path)}
if installDryRun {
kubectlArgs = append(kubectlArgs, "--dry-run=client")
applyOutput = utils.ModeOS
}
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
}
@@ -178,9 +168,8 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
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 {
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
} else {
logger.Successf("%s ready", deployment)

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");
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");
you may not use this file except in compliance with the License.
@@ -26,43 +26,42 @@ import (
"github.com/spf13/cobra/doc"
_ "k8s.io/client-go/plugin/pkg/client/auth"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
gotklog "github.com/fluxcd/toolkit/pkg/log"
fluxlog "github.com/fluxcd/flux2/pkg/log"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
)
var VERSION = "0.0.0-dev.0"
var rootCmd = &cobra.Command{
Use: "gotk",
Use: "flux",
Version: VERSION,
SilenceUsage: true,
SilenceErrors: true,
Short: "Command line utility for assembling Kubernetes CD pipelines",
Long: `Command line utility for assembling Kubernetes CD pipelines the GitOps way.`,
Example: ` # Check prerequisites
gotk check --pre
flux check --pre
# Install the latest version of the toolkit
gotk install --version=master
flux install --version=master
# 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 \
--branch=master \
--interval=3m
# List GitRepository sources and their status
gotk get sources git
flux get sources git
# Trigger a GitRepository source reconciliation
gotk reconcile source git gotk-system
flux reconcile source git flux-system
# 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
gotk create kustomization webapp-dev \
flux create kustomization webapp-dev \
--source=webapp-latest \
--path="./deploy/webapp/" \
--prune=true \
@@ -73,55 +72,44 @@ var rootCmd = &cobra.Command{
--health-check-timeout=2m
# 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
gotk suspend kustomization webapp-dev
flux suspend kustomization webapp-dev
# Export Kustomizations in YAML format
gotk export kustomization --all > kustomizations.yaml
flux export kustomization --all > kustomizations.yaml
# Resume a Kustomization reconciliation
gotk resume kustomization webapp-dev
flux resume kustomization webapp-dev
# Delete a Kustomization
gotk delete kustomization webapp-dev
flux delete kustomization webapp-dev
# Delete a GitRepository source
gotk delete source git webapp-latest
flux delete source git webapp-latest
# Uninstall the toolkit and delete CRDs
gotk uninstall --crds
flux uninstall --crds
`,
}
var (
kubeconfig string
kubecontext string
namespace string
timeout time.Duration
verbose bool
utils Utils
pollInterval = 2 * time.Second
logger gotklog.Logger = printLogger{}
)
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}
logger fluxlog.Logger = printLogger{}
defaults = install.MakeDefaultOptions()
)
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().BoolVar(&verbose, "verbose", false, "print generated objects")
rootCmd.PersistentFlags().StringVarP(&kubecontext, "context", "", "", "kubernetes context to use")
}
func main() {

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");
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");
you may not use this file except in compliance with the License.
@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"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"
@@ -34,7 +36,7 @@ var reconcileAlertCmd = &cobra.Command{
Short: "Reconcile an Alert",
Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing alert
gotk reconcile alert main
flux reconcile alert main
`,
RunE: reconcileAlertCmdRun,
}
@@ -45,14 +47,14 @@ func init() {
func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("alert name is required")
return fmt.Errorf("Alert name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -62,13 +64,17 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
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.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating Alert %s in %s namespace", name, namespace)
if alert.Annotations == nil {
alert.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
@@ -76,18 +82,17 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
} 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.Successf("Alert annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, name, namespace)); err != nil {
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("alert reconciliation completed")
logger.Successf("Alert 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");
you may not use this file except in compliance with the License.
@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"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"
@@ -34,7 +36,7 @@ var reconcileAlertProviderCmd = &cobra.Command{
Short: "Reconcile a Provider",
Long: `The reconcile alert-provider command triggers a reconciliation of a Provider resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing provider
gotk reconcile alert-provider slack
flux reconcile alert-provider slack
`,
RunE: reconcileAlertProviderCmdRun,
}
@@ -45,14 +47,14 @@ func init() {
func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("provider name is required")
return fmt.Errorf("Provider name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -62,7 +64,7 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
Name: name,
}
logger.Actionf("annotating provider %s in %s namespace", name, namespace)
logger.Actionf("annotating Provider %s in %s namespace", name, namespace)
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
@@ -79,15 +81,13 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &alertProvider); err != nil {
return err
}
logger.Successf("provider annotated")
logger.Successf("Provider annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, name, namespace)); err != nil {
isAlertProviderReady(ctx, kubeClient, namespacedName, &alertProvider)); err != nil {
return err
}
logger.Successf("provider reconciliation completed")
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");
you may not use this file except in compliance with the License.
@@ -22,12 +22,14 @@ import (
"time"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"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"
@@ -41,10 +43,10 @@ var reconcileHrCmd = &cobra.Command{
Long: `
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
gotk reconcile hr podinfo
flux reconcile hr podinfo
# 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,
}
@@ -68,7 +70,7 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -84,6 +86,10 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
return err
}
if helmRelease.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
if syncHrWithSource {
switch helmRelease.Spec.Chart.Spec.SourceRef.Kind {
case sourcev1.HelmRepositoryKind:
@@ -98,29 +104,29 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
}
}
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt
logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace)
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName); err != nil {
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
return err
}
logger.Successf("HelmRelease annotated")
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
helmReleaseReconciliationHandled(ctx, kubeClient, name, namespace, helmRelease.Status.LastHandledReconcileAt),
helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
); err != nil {
return err
}
logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionFalse:
return fmt.Errorf("HelmRelease reconciliation failed")
case metav1.ConditionFalse:
return fmt.Errorf("HelmRelease reconciliation failed: %s", c.Message)
default:
logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision)
}
@@ -129,39 +135,29 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
}
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) {
var helmRelease helmv2.HelmRelease
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
err := kubeClient.Get(ctx, namespacedName, helmRelease)
if err != nil {
return false, err
}
return helmRelease.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestHelmReleaseReconciliation(ctx context.Context, kubeClient client.Client, namespacedName types.NamespacedName) error {
var release helmv2.HelmRelease
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, &release); err != nil {
if err := kubeClient.Get(ctx, namespacedName, helmRelease); err != nil {
return err
}
if release.Annotations == nil {
release.Annotations = map[string]string{
if helmRelease.Annotations == nil {
helmRelease.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
release.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
helmRelease.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
err = kubeClient.Update(ctx, &release)
return
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");
you may not use this file except in compliance with the License.
@@ -21,10 +21,11 @@ import (
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
@@ -41,10 +42,10 @@ var reconcileKsCmd = &cobra.Command{
Long: `
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
gotk reconcile kustomization podinfo
flux reconcile kustomization podinfo
# 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,
}
@@ -54,21 +55,21 @@ var (
)
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)
}
func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("kustomization name is required")
return fmt.Errorf("Kustomization name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -77,13 +78,16 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
Namespace: namespace,
Name: name,
}
var kustomization kustomizev1.Kustomization
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if kustomization.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
if syncKsWithSource {
switch kustomization.Spec.SourceRef.Kind {
case sourcev1.GitRepositoryKind:
@@ -96,62 +100,46 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
}
}
logger.Actionf("annotating kustomization %s in %s namespace", name, namespace)
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName); err != nil {
lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
logger.Actionf("annotating Kustomization %s in %s namespace", name, namespace)
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
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(
pollInterval, timeout,
kustomizeReconciliationHandled(ctx, kubeClient, name, namespace, kustomization.Status.LastHandledReconcileAt),
kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
); err != nil {
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 {
switch c.Status {
case corev1.ConditionFalse:
return fmt.Errorf("kustomization reconciliation failed")
default:
logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision)
}
if apimeta.IsStatusConditionFalse(kustomization.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("Kustomization reconciliation failed")
}
logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision)
return nil
}
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) {
var kustomize kustomizev1.Kustomization
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomize)
err := kubeClient.Get(ctx, namespacedName, kustomization)
if err != nil {
return false, err
}
return kustomize.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
return kustomization.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Client, namespacedName types.NamespacedName) error {
var kustomization kustomizev1.Kustomization
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 {
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),
@@ -159,8 +147,6 @@ func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Clien
} else {
kustomization.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
err = kubeClient.Update(ctx, &kustomization)
return
return kubeClient.Update(ctx, kustomization)
})
}

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");
you may not use this file except in compliance with the License.
@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"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"
@@ -34,7 +36,7 @@ var reconcileReceiverCmd = &cobra.Command{
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
gotk reconcile receiver main
flux reconcile receiver main
`,
RunE: reconcileReceiverCmdRun,
}
@@ -52,7 +54,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -62,13 +64,17 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
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.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating Receiver %s in %s namespace", name, namespace)
if receiver.Annotations == nil {
receiver.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
@@ -79,15 +85,15 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("receiver annotated")
logger.Successf("Receiver annotated")
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, name, namespace)); err != nil {
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("receiver reconciliation completed")
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");
you may not use this file except in compliance with the License.

View File

@@ -0,0 +1,155 @@
/*
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/pkg/apis/meta"
"k8s.io/client-go/util/retry"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var reconcileSourceBucketCmd = &cobra.Command{
Use: "bucket [name]",
Short: "Reconcile a Bucket source",
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
flux reconcile source bucket podinfo
`,
RunE: reconcileSourceBucketCmdRun,
}
func init() {
reconcileSourceCmd.AddCommand(reconcileSourceBucketCmd)
}
func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var bucket sourcev1.Bucket
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return err
}
if bucket.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
lastHandledReconcileAt := bucket.Status.LastHandledReconcileAt
logger.Actionf("annotating Bucket source %s in %s namespace", name, namespace)
if err := requestBucketReconciliation(ctx, kubeClient, namespacedName, &bucket); err != nil {
return err
}
logger.Successf("Bucket source annotated")
logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(
pollInterval, timeout,
bucketReconciliationHandled(ctx, kubeClient, namespacedName, &bucket, lastHandledReconcileAt),
); err != nil {
return err
}
logger.Successf("Bucket source reconciliation completed")
if apimeta.IsStatusConditionFalse(bucket.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("Bucket source reconciliation failed")
}
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
return nil
}
func isBucketReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, bucket)
if err != nil {
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 := apimeta.FindStatusCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}
func bucketReconciliationHandled(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, bucket)
if err != nil {
return false, err
}
return bucket.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestBucketReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, bucket); err != nil {
return err
}
if bucket.Annotations == nil {
bucket.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
bucket.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
return kubeClient.Update(ctx, bucket)
})
}

View File

@@ -0,0 +1,126 @@
/*
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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var reconcileSourceGitCmd = &cobra.Command{
Use: "git [name]",
Short: "Reconcile a GitRepository source",
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
flux reconcile source git podinfo
`,
RunE: reconcileSourceGitCmdRun,
}
func init() {
reconcileSourceCmd.AddCommand(reconcileSourceGitCmd)
}
func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var repository sourcev1.GitRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
if repository.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating GitRepository source %s in %s namespace", name, namespace)
if err := requestGitRepositoryReconciliation(ctx, kubeClient, namespacedName, &repository); err != nil {
return err
}
logger.Successf("GitRepository source annotated")
lastHandledReconcileAt := repository.Status.LastHandledReconcileAt
logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
gitRepositoryReconciliationHandled(ctx, kubeClient, namespacedName, &repository, lastHandledReconcileAt)); err != nil {
return err
}
logger.Successf("GitRepository source reconciliation completed")
if apimeta.IsStatusConditionFalse(repository.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("GitRepository source reconciliation failed")
}
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
return nil
}
func gitRepositoryReconciliationHandled(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, repository *sourcev1.GitRepository, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, repository)
if err != nil {
return false, err
}
return repository.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestGitRepositoryReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, repository *sourcev1.GitRepository) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, repository); err != nil {
return err
}
if repository.Annotations == nil {
repository.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
repository.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
return kubeClient.Update(ctx, repository)
})
}

View File

@@ -0,0 +1,127 @@
/*
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/pkg/apis/meta"
"k8s.io/client-go/util/retry"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var reconcileSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Reconcile a HelmRepository source",
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
flux reconcile source helm podinfo
`,
RunE: reconcileSourceHelmCmdRun,
}
func init() {
reconcileSourceCmd.AddCommand(reconcileSourceHelmCmd)
}
func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("HelmRepository source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var repository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
if repository.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating HelmRepository source %s in %s namespace", name, namespace)
if err := requestHelmRepositoryReconciliation(ctx, kubeClient, namespacedName, &repository); err != nil {
return err
}
logger.Successf("HelmRepository source annotated")
lastHandledReconcileAt := repository.Status.LastHandledReconcileAt
logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
helmRepositoryReconciliationHandled(ctx, kubeClient, namespacedName, &repository, lastHandledReconcileAt)); err != nil {
return err
}
logger.Successf("HelmRepository source reconciliation completed")
if apimeta.IsStatusConditionFalse(repository.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("HelmRepository source reconciliation failed")
}
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
return nil
}
func helmRepositoryReconciliationHandled(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, repository)
if err != nil {
return false, err
}
return repository.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestHelmRepositoryReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, repository); err != nil {
return err
}
if repository.Annotations == nil {
repository.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
repository.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
return kubeClient.Update(ctx, repository)
})
}

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");
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");
you may not use this file except in compliance with the License.
@@ -19,10 +19,13 @@ 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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -36,7 +39,7 @@ var resumeAlertCmd = &cobra.Command{
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
gotk resume alert main
flux resume alert main
`,
RunE: resumeAlertCmdRun,
}
@@ -54,7 +57,7 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -78,33 +81,26 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertResumed(ctx, kubeClient, name, namespace)); err != nil {
isAlertResumed(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("Alert reconciliation completed")
return nil
}
func isAlertResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isAlertResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
return func() (bool, error) {
var alert notificationv1.Alert
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alert)
err := kubeClient.Get(ctx, namespacedName, alert)
if err != nil {
return false, err
}
if c := meta.GetCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
case metav1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
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");
you may not use this file except in compliance with the License.
@@ -19,10 +19,13 @@ 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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
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"
@@ -37,7 +40,7 @@ var resumeHrCmd = &cobra.Command{
Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to
finish the apply.`,
Example: ` # Resume reconciliation for an existing Helm release
gotk resume hr podinfo
flux resume hr podinfo
`,
RunE: resumeHrCmdRun,
}
@@ -55,7 +58,7 @@ func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -79,47 +82,33 @@ func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseResumed(ctx, kubeClient, name, namespace)); err != nil {
isHelmReleaseResumed(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return err
}
logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
} else {
return fmt.Errorf("HelmRelease reconciliation failed")
}
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
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) {
var helmRelease helmv2.HelmRelease
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
err := kubeClient.Get(ctx, namespacedName, helmRelease)
if err != nil {
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
if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
return false, err
}
if c := apimeta.FindStatusCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}

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");
you may not use this file except in compliance with the License.
@@ -19,11 +19,14 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -36,7 +39,7 @@ var resumeKsCmd = &cobra.Command{
Long: `The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to
finish the apply.`,
Example: ` # Resume reconciliation for an existing Kustomization
gotk resume ks podinfo
flux resume ks podinfo
`,
RunE: resumeKsCmdRun,
}
@@ -47,14 +50,14 @@ func init() {
func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("kustomization name is required")
return fmt.Errorf("Kustomization name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -69,56 +72,42 @@ func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
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
if err := kubeClient.Update(ctx, &kustomization); err != nil {
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,
isKustomizationResumed(ctx, kubeClient, name, namespace)); err != nil {
isKustomizationResumed(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err
}
logger.Successf("Kustomization reconciliation completed")
logger.Successf("kustomization sync completed")
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")
}
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
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) {
var kustomization kustomizev1.Kustomization
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomization)
err := kubeClient.Get(ctx, namespacedName, kustomization)
if err != nil {
return false, err
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}

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");
you may not use this file except in compliance with the License.
@@ -19,10 +19,13 @@ 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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -36,7 +39,7 @@ var resumeReceiverCmd = &cobra.Command{
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
gotk resume receiver main
flux resume receiver main
`,
RunE: resumeReceiverCmdRun,
}
@@ -54,7 +57,7 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
@@ -78,33 +81,27 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverResumed(ctx, kubeClient, name, namespace)); err != nil {
isReceiverResumed(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("Receiver reconciliation completed")
return nil
}
func isReceiverResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isReceiverResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
return func() (bool, error) {
var receiver notificationv1.Receiver
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &receiver)
err := kubeClient.Get(ctx, namespacedName, receiver)
if err != nil {
return false, err
}
if c := meta.GetCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
case metav1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
case metav1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}

31
cmd/flux/resume_source.go Normal file
View File

@@ -0,0 +1,31 @@
/*
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 (
"github.com/spf13/cobra"
)
var resumeSourceCmd = &cobra.Command{
Use: "source",
Short: "Resume sources",
Long: "The resume sub-commands resume a suspended source.",
}
func init() {
resumeCmd.AddCommand(resumeSourceCmd)
}

View File

@@ -0,0 +1,115 @@
/*
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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
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"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var resumeSourceBucketCmd = &cobra.Command{
Use: "bucket [name]",
Short: "Resume a suspended Bucket",
Long: `The resume command marks a previously suspended Bucket resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing Bucket
flux resume source bucket podinfo
`,
RunE: resumeSourceBucketCmdRun,
}
func init() {
resumeSourceCmd.AddCommand(resumeSourceBucketCmd)
}
func resumeSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var bucket sourcev1.Bucket
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return err
}
logger.Actionf("resuming source %s in %s namespace", name, namespace)
bucket.Spec.Suspend = false
if err := kubeClient.Update(ctx, &bucket); err != nil {
return err
}
logger.Successf("source resumed")
logger.Waitingf("waiting for Bucket reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isBucketResumed(ctx, kubeClient, namespacedName, &bucket)); err != nil {
return err
}
logger.Successf("Bucket reconciliation completed")
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
return nil
}
func isBucketResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, bucket)
if err != nil {
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 := apimeta.FindStatusCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -0,0 +1,115 @@
/*
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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
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"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var resumeSourceHelmChartCmd = &cobra.Command{
Use: "chart [name]",
Short: "Resume a suspended HelmChart",
Long: `The resume command marks a previously suspended HelmChart resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing HelmChart
flux resume source chart podinfo
`,
RunE: resumeSourceHelmChartCmdRun,
}
func init() {
resumeSourceCmd.AddCommand(resumeSourceHelmChartCmd)
}
func resumeSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var repository sourcev1.HelmChart
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
logger.Actionf("resuming source %s in %s namespace", name, namespace)
repository.Spec.Suspend = false
if err := kubeClient.Update(ctx, &repository); err != nil {
return err
}
logger.Successf("source resumed")
logger.Waitingf("waiting for HelmChart reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmChartResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
return err
}
logger.Successf("HelmChart reconciliation completed")
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
return nil
}
func isHelmChartResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, chart *sourcev1.HelmChart) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, chart)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if chart.Generation != chart.Status.ObservedGeneration {
return false, nil
}
if c := apimeta.FindStatusCondition(chart.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -0,0 +1,115 @@
/*
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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
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"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var resumeSourceGitCmd = &cobra.Command{
Use: "git [name]",
Short: "Resume a suspended GitRepository",
Long: `The resume command marks a previously suspended GitRepository resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing GitRepository
flux resume source git podinfo
`,
RunE: resumeSourceGitCmdRun,
}
func init() {
resumeSourceCmd.AddCommand(resumeSourceGitCmd)
}
func resumeSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var repository sourcev1.GitRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
logger.Actionf("resuming source %s in %s namespace", name, namespace)
repository.Spec.Suspend = false
if err := kubeClient.Update(ctx, &repository); err != nil {
return err
}
logger.Successf("source resumed")
logger.Waitingf("waiting for GitRepository reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
return err
}
logger.Successf("GitRepository reconciliation completed")
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
return nil
}
func isGitRepositoryResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, repository *sourcev1.GitRepository) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, repository)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if repository.Generation != repository.Status.ObservedGeneration {
return false, nil
}
if c := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -0,0 +1,115 @@
/*
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"
apimeta "k8s.io/apimachinery/pkg/api/meta"
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"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var resumeSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Resume a suspended HelmRepository",
Long: `The resume command marks a previously suspended HelmRepository resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing HelmRepository
flux resume source helm bitnami
`,
RunE: resumeSourceHelmCmdRun,
}
func init() {
resumeSourceCmd.AddCommand(resumeSourceHelmCmd)
}
func resumeSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var repository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
logger.Actionf("resuming source %s in %s namespace", name, namespace)
repository.Spec.Suspend = false
if err := kubeClient.Update(ctx, &repository); err != nil {
return err
}
logger.Successf("source resumed")
logger.Waitingf("waiting for HelmRepository reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
return err
}
logger.Successf("HelmRepository reconciliation completed")
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
return nil
}
func isHelmRepositoryResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, repository)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if repository.Generation != repository.Status.ObservedGeneration {
return false, nil
}
if c := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -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");
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");
you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -31,7 +32,7 @@ var suspendAlertCmd = &cobra.Command{
Short: "Suspend reconciliation of Alert",
Long: "The suspend command disables the reconciliation of a Alert resource.",
Example: ` # Suspend reconciliation for an existing Alert
gotk suspend alert main
flux suspend alert main
`,
RunE: suspendAlertCmdRun,
}
@@ -49,7 +50,7 @@ func suspendAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
)
@@ -32,7 +33,7 @@ var suspendHrCmd = &cobra.Command{
Short: "Suspend reconciliation of HelmRelease",
Long: "The suspend command disables the reconciliation of a HelmRelease resource.",
Example: ` # Suspend reconciliation for an existing Helm release
gotk suspend hr podinfo
flux suspend hr podinfo
`,
RunE: suspendHrCmdRun,
}
@@ -50,7 +51,7 @@ func suspendHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
@@ -30,7 +32,7 @@ var suspendKsCmd = &cobra.Command{
Short: "Suspend reconciliation of Kustomization",
Long: "The suspend command disables the reconciliation of a Kustomization resource.",
Example: ` # Suspend reconciliation for an existing Kustomization
gotk suspend ks podinfo
flux suspend ks podinfo
`,
RunE: suspendKsCmdRun,
}
@@ -48,7 +50,7 @@ func suspendKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
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");
you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
)
@@ -31,7 +32,7 @@ var suspendReceiverCmd = &cobra.Command{
Short: "Suspend reconciliation of Receiver",
Long: "The suspend command disables the reconciliation of a Receiver resource.",
Example: ` # Suspend reconciliation for an existing Receiver
gotk suspend receiver main
flux suspend receiver main
`,
RunE: suspendReceiverCmdRun,
}
@@ -49,7 +50,7 @@ func suspendReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}

View File

@@ -0,0 +1,31 @@
/*
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 (
"github.com/spf13/cobra"
)
var suspendSourceCmd = &cobra.Command{
Use: "source",
Short: "Suspend sources",
Long: "The suspend sub-commands suspend the reconciliation of a source.",
}
func init() {
suspendCmd.AddCommand(suspendSourceCmd)
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,75 @@
/*
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"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/flux2/internal/utils"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
)
var suspendSourceHelmCmd = &cobra.Command{
Use: "helm [name]",
Short: "Suspend reconciliation of a HelmRepository",
Long: "The suspend command disables the reconciliation of a HelmRepository resource.",
Example: ` # Suspend reconciliation for an existing HelmRepository
flux suspend source helm bitnami
`,
RunE: suspendSourceHelmCmdRun,
}
func init() {
suspendSourceCmd.AddCommand(suspendSourceHelmCmd)
}
func suspendSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
var repository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &repository)
if err != nil {
return err
}
logger.Actionf("suspending source %s in %s namespace", name, namespace)
repository.Spec.Suspend = true
if err := kubeClient.Update(ctx, &repository); err != nil {
return err
}
logger.Successf("source 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");
you may not use this file except in compliance with the License.
@@ -22,8 +22,11 @@ import (
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"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"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
@@ -34,10 +37,10 @@ var uninstallCmd = &cobra.Command{
Short: "Uninstall the toolkit components",
Long: "The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.",
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
gotk uninstall --resources --crds --namespace=gotk-system
flux uninstall --resources --crds --namespace=flux-system
`,
RunE: uninstallCmdRun,
}
@@ -66,15 +69,12 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
if err != nil {
return err
}
dryRun := ""
if uninstallDryRun {
dryRun = "--dry-run=server"
} else if !uninstallSilent {
if !uninstallDryRun && !uninstallSilent {
prompt := promptui.Prompt{
Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", namespace),
IsConfirm: true,
@@ -84,31 +84,44 @@ 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{
Namespace: namespace,
Name: namespace,
}
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
if err := kubeClient.Update(ctx, &kustomization); err != nil {
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")
for _, kind := range []string{
kustomizev1.KustomizationKind,
sourcev1.GitRepositoryKind,
sourcev1.HelmRepositoryKind,
helmv2.HelmReleaseKind,
} {
command := fmt.Sprintf("kubectl -n %s delete %s --all --ignore-not-found --timeout=%s %s",
namespace, kind, timeout.String(), dryRun)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
return fmt.Errorf("uninstall failed: %w", err)
for _, kind := range namespacedKinds {
if err := deleteAll(ctx, kind, uninstallDryRun); err != nil {
logger.Failuref("kubectl: %s", err.Error())
}
}
}
@@ -123,9 +136,15 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
logger.Actionf("uninstalling components")
for _, kind := range kinds {
command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --ignore-not-found --timeout=%s %s",
kind, namespace, timeout.String(), dryRun)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
kubectlArgs := []string{
"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, kubeconfig, kubecontext, kubectlArgs...); err != nil {
return fmt.Errorf("uninstall failed: %w", err)
}
}
@@ -133,3 +152,18 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("uninstall finished")
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, kubeconfig, kubecontext, kubectlArgs...)
return err
}

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