Compare commits
426 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f38b83231c | ||
|
|
269f5e2575 | ||
|
|
893596383a | ||
|
|
8c67708829 | ||
|
|
c1528503b6 | ||
|
|
d3c56eb3d3 | ||
|
|
b10eee87ee | ||
|
|
83de469967 | ||
|
|
192978125f | ||
|
|
b4b3551e39 | ||
|
|
7f580e89d0 | ||
|
|
81a087095a | ||
|
|
bcabde3bdb | ||
|
|
c190d80d4a | ||
|
|
11081e8cb2 | ||
|
|
c5890f08ef | ||
|
|
926d8a1c37 | ||
|
|
da6dfd5a1b | ||
|
|
4318152141 | ||
|
|
759145704f | ||
|
|
5cab8f4b11 | ||
|
|
a0ce4b23d2 | ||
|
|
6d88a0c3ac | ||
|
|
db44bcd88e | ||
|
|
585ae5090d | ||
|
|
fe46793c40 | ||
|
|
be146b1cc9 | ||
|
|
e46c7bd519 | ||
|
|
f3d143e5ee | ||
|
|
fc059df8ff | ||
|
|
6c047d1e2a | ||
|
|
f6afe7f0ec | ||
|
|
ca7d2e783f | ||
|
|
0b133ca9f2 | ||
|
|
ede6785e6b | ||
|
|
6d9f39d8ea | ||
|
|
fb637ea955 | ||
|
|
e07558f5b7 | ||
|
|
b75dbf8c70 | ||
|
|
062c1e59a9 | ||
|
|
ba5eea861e | ||
|
|
ff7df54899 | ||
|
|
b75ce95086 | ||
|
|
a86d94745a | ||
|
|
c13de6089a | ||
|
|
3cb748a47e | ||
|
|
3e6e93fab4 | ||
|
|
5832811930 | ||
|
|
6f0ea04ff3 | ||
|
|
26ea167524 | ||
|
|
1393e7a62b | ||
|
|
7e1fd499ca | ||
|
|
309fd86b45 | ||
|
|
e14357f694 | ||
|
|
29f0adc587 | ||
|
|
3ab578747d | ||
|
|
2c3cb1a664 | ||
|
|
99a0c47277 | ||
|
|
c5b2c6709a | ||
|
|
8354ac937c | ||
|
|
aa5ad65286 | ||
|
|
05adb44416 | ||
|
|
adf5a5278f | ||
|
|
bb04ca36b2 | ||
|
|
fc94b1af7a | ||
|
|
d9886035c8 | ||
|
|
0e122863dd | ||
|
|
3089f67946 | ||
|
|
3654e221a9 | ||
|
|
a26dd05c49 | ||
|
|
6c844369be | ||
|
|
a03574f8c3 | ||
|
|
43c6a1531a | ||
|
|
aed7341b34 | ||
|
|
8731f00347 | ||
|
|
75a18b4548 | ||
|
|
716b41e91b | ||
|
|
e72214e266 | ||
|
|
46f9fc194c | ||
|
|
6c5f27be02 | ||
|
|
e96652bdaa | ||
|
|
b7007a35d7 | ||
|
|
e4dc56b59d | ||
|
|
b05059a9c4 | ||
|
|
121783976a | ||
|
|
5a9424d0b1 | ||
|
|
e2a3800664 | ||
|
|
56815a3624 | ||
|
|
ca6e0ff36e | ||
|
|
4c60d1657d | ||
|
|
cdd90cfa75 | ||
|
|
05b053e2e9 | ||
|
|
46c75c1af2 | ||
|
|
80cf5fa729 | ||
|
|
789aa322f1 | ||
|
|
f03c24565f | ||
|
|
b6a95be5d0 | ||
|
|
0f9e8ed3f7 | ||
|
|
50af0ba93c | ||
|
|
923a5882de | ||
|
|
83dc3e6093 | ||
|
|
43edb62f87 | ||
|
|
21e5acc0e0 | ||
|
|
be1ce74dc5 | ||
|
|
31771f3575 | ||
|
|
ffcd7d8059 | ||
|
|
4bc4aa1397 | ||
|
|
04faba95cd | ||
|
|
f712dadab5 | ||
|
|
58b3150ce3 | ||
|
|
e7225db397 | ||
|
|
8ec5492d87 | ||
|
|
c2c64a70c4 | ||
|
|
4621576f40 | ||
|
|
3b609e9b03 | ||
|
|
4f2ebd78be | ||
|
|
88dacebc94 | ||
|
|
92e7d1ad1e | ||
|
|
d5d8c340c8 | ||
|
|
b8a85b809a | ||
|
|
61be0775af | ||
|
|
404ffa5a91 | ||
|
|
f2de7e04b8 | ||
|
|
8b3e3b1dd7 | ||
|
|
81e91ac3f5 | ||
|
|
b9bde94d08 | ||
|
|
37746023c1 | ||
|
|
d3e529b8a4 | ||
|
|
eb69083ef5 | ||
|
|
96aac387c9 | ||
|
|
870f18c621 | ||
|
|
57b33e29f7 | ||
|
|
94b7917679 | ||
|
|
98fa0c4271 | ||
|
|
8282907bce | ||
|
|
323f4f5e5f | ||
|
|
744b3ebd0a | ||
|
|
3fdba35993 | ||
|
|
ebdf9ed379 | ||
|
|
a572274c5c | ||
|
|
6a6bba8669 | ||
|
|
1d1d4bbf4b | ||
|
|
d9bb4c631e | ||
|
|
722962c138 | ||
|
|
c98ff6ae87 | ||
|
|
cbef6a4cad | ||
|
|
f887a2c029 | ||
|
|
078cfe92c2 | ||
|
|
80ef184b60 | ||
|
|
f2475988bd | ||
|
|
45526108e0 | ||
|
|
414c0bbbdc | ||
|
|
6873a710d9 | ||
|
|
8a44006384 | ||
|
|
1b6061066a | ||
|
|
3a8a5982c6 | ||
|
|
ccff578492 | ||
|
|
e2402e3d84 | ||
|
|
f13b1629cf | ||
|
|
72a97bb70a | ||
|
|
67b393ce09 | ||
|
|
48e89b95bb | ||
|
|
2159ed62d0 | ||
|
|
8bb65719cd | ||
|
|
4352915945 | ||
|
|
ebd145f7f7 | ||
|
|
cd52a0eef3 | ||
|
|
69e4a86fe2 | ||
|
|
52d89a2ee1 | ||
|
|
5c60e792d9 | ||
|
|
77c9611784 | ||
|
|
66780bbf54 | ||
|
|
a8932e677e | ||
|
|
e12988a8f9 | ||
|
|
6ee4abe79e | ||
|
|
948e050d60 | ||
|
|
87feb45751 | ||
|
|
77aa81a064 | ||
|
|
a4a1db0915 | ||
|
|
57b9610af7 | ||
|
|
c3384c6499 | ||
|
|
5389859260 | ||
|
|
84c585cf61 | ||
|
|
ca496d393d | ||
|
|
3d4ca831dc | ||
|
|
7ace8de753 | ||
|
|
928d3e2185 | ||
|
|
cbf2b90320 | ||
|
|
69dce73e51 | ||
|
|
75d4f87dec | ||
|
|
4f7d89e825 | ||
|
|
da87e16321 | ||
|
|
f7aa3e7e1b | ||
|
|
deacfd6c03 | ||
|
|
3e8d1ae1d5 | ||
|
|
08f5ca39b0 | ||
|
|
31da363495 | ||
|
|
2ecd99d317 | ||
|
|
59c3d84182 | ||
|
|
641d5378f8 | ||
|
|
08512e5c43 | ||
|
|
8f7f7b23e8 | ||
|
|
2eb6ba5a48 | ||
|
|
03df386f9e | ||
|
|
5e741da69c | ||
|
|
3bea028cc9 | ||
|
|
10475b24c4 | ||
|
|
a5238e867c | ||
|
|
0e747790f9 | ||
|
|
2b4d6150d4 | ||
|
|
e22ad96164 | ||
|
|
f54907e66e | ||
|
|
fb713e9632 | ||
|
|
0b659e3f09 | ||
|
|
4c99117c7c | ||
|
|
83c3e8c2fc | ||
|
|
92277225df | ||
|
|
622ed88a11 | ||
|
|
d9414f25d5 | ||
|
|
5249d17a95 | ||
|
|
25283d357e | ||
|
|
e926321094 | ||
|
|
9c1542c3f3 | ||
|
|
25d06a53bc | ||
|
|
4d904e8216 | ||
|
|
0beab87f5b | ||
|
|
b9ceceada4 | ||
|
|
ac7ccf7b94 | ||
|
|
5aa9ae511f | ||
|
|
dd81ed896b | ||
|
|
e6bbed162d | ||
|
|
3ee8747fdc | ||
|
|
0651064999 | ||
|
|
4661e4519d | ||
|
|
19caeb178f | ||
|
|
d8235ea21b | ||
|
|
5067df179e | ||
|
|
50a1e32da3 | ||
|
|
fb85cafcc5 | ||
|
|
d06a2936cc | ||
|
|
7c77a9723a | ||
|
|
8a3e5790f5 | ||
|
|
dd093a775a | ||
|
|
a096bd2d71 | ||
|
|
2eddcde609 | ||
|
|
1849e1768a | ||
|
|
bbe62d029c | ||
|
|
68a89d3cd4 | ||
|
|
b16f1fc260 | ||
|
|
64f39e160b | ||
|
|
3a76c26822 | ||
|
|
9d9fff5796 | ||
|
|
0a92c61b09 | ||
|
|
546be76f55 | ||
|
|
d770f3f53f | ||
|
|
254cc131ae | ||
|
|
70509ffcb4 | ||
|
|
4cc2326c7f | ||
|
|
0133caaec4 | ||
|
|
7ae4f28920 | ||
|
|
b1eb0270e9 | ||
|
|
03b6de1169 | ||
|
|
9d3f75d111 | ||
|
|
5c41924b2f | ||
|
|
7cf7cf2f1e | ||
|
|
2679731bde | ||
|
|
ad73370cd7 | ||
|
|
18acae57bd | ||
|
|
b427356eca | ||
|
|
2e6ca16a4a | ||
|
|
e98f1142a6 | ||
|
|
06fa8f75c9 | ||
|
|
8cbd4e8172 | ||
|
|
83c7994266 | ||
|
|
43843581b6 | ||
|
|
7e03d64e8a | ||
|
|
c6f4d71187 | ||
|
|
69c3b90fea | ||
|
|
75309b4c93 | ||
|
|
433f13a7ed | ||
|
|
8896a1e73e | ||
|
|
54758b1692 | ||
|
|
375e00c79c | ||
|
|
e2454d91f1 | ||
|
|
6894f6f3bf | ||
|
|
d45501a129 | ||
|
|
def92e14ee | ||
|
|
11708d4189 | ||
|
|
2bc64bf419 | ||
|
|
3a3bdc62c8 | ||
|
|
72294b2a56 | ||
|
|
94940a20ef | ||
|
|
219ff2ef7d | ||
|
|
bc2de741b8 | ||
|
|
5eabd4e898 | ||
|
|
e8d6d5fe5c | ||
|
|
55bd93ff79 | ||
|
|
b34b2d779b | ||
|
|
103ed2be65 | ||
|
|
cc32c1be07 | ||
|
|
a3ba9817a3 | ||
|
|
6d5f1b17ad | ||
|
|
0d5d5fce46 | ||
|
|
375edffd15 | ||
|
|
d1982e64b2 | ||
|
|
cec8b5336c | ||
|
|
8f78263455 | ||
|
|
cb96bca6aa | ||
|
|
c18d0b9217 | ||
|
|
d1970185b9 | ||
|
|
794d3ee2f5 | ||
|
|
daeef98dfb | ||
|
|
4146df1f02 | ||
|
|
78f4dfa48d | ||
|
|
8b68d7d7e2 | ||
|
|
ba1bba17ad | ||
|
|
70f2b5028f | ||
|
|
4f4f0d70a4 | ||
|
|
9607b07e65 | ||
|
|
3570fab0f9 | ||
|
|
dd0f17d7a5 | ||
|
|
2290880389 | ||
|
|
351d287d88 | ||
|
|
eba6706f15 | ||
|
|
7f425efa6b | ||
|
|
d40685ab62 | ||
|
|
f795e3eeb8 | ||
|
|
d3944c0204 | ||
|
|
4086ab15fa | ||
|
|
446a367094 | ||
|
|
bd250c9871 | ||
|
|
91b3788362 | ||
|
|
658dbb9ea8 | ||
|
|
1257b9cbc8 | ||
|
|
0ae39d5a0a | ||
|
|
d8911e0c77 | ||
|
|
1be006a45f | ||
|
|
b95e75ddb4 | ||
|
|
15a5f75fe7 | ||
|
|
b01e27f50f | ||
|
|
b1a9583262 | ||
|
|
dd5e6377f8 | ||
|
|
fcb73554c9 | ||
|
|
5c4b3d1080 | ||
|
|
c98cd10621 | ||
|
|
e4af8ddcc8 | ||
|
|
b5ce02b8cf | ||
|
|
cd42b3fb44 | ||
|
|
6dedb767c3 | ||
|
|
4de3268f3b | ||
|
|
a72ff06d79 | ||
|
|
cf239deb3d | ||
|
|
8735229745 | ||
|
|
3f081ba2d9 | ||
|
|
3f613341cb | ||
|
|
4305b8a77d | ||
|
|
fab91d44c3 | ||
|
|
34484734d8 | ||
|
|
10c58bb007 | ||
|
|
1c1fd62a91 | ||
|
|
637ac1e3fe | ||
|
|
f52387e849 | ||
|
|
9efc4986f9 | ||
|
|
fdc366ec1c | ||
|
|
d455db444c | ||
|
|
17418c9858 | ||
|
|
5da7eb7d0d | ||
|
|
9da977ee7a | ||
|
|
8e4b2ead9b | ||
|
|
fd364828a1 | ||
|
|
afa58d8c08 | ||
|
|
179062876e | ||
|
|
a796f3609f | ||
|
|
b7c6db74d2 | ||
|
|
4f7b040405 | ||
|
|
34ca29830e | ||
|
|
78f1b634fa | ||
|
|
044bc64ad9 | ||
|
|
091f439498 | ||
|
|
a17b0a1ce0 | ||
|
|
354cd5e177 | ||
|
|
4e8f1221f7 | ||
|
|
6b179aa7d9 | ||
|
|
f748114dfa | ||
|
|
5de83f015a | ||
|
|
a6620e478a | ||
|
|
c7fcffdd8e | ||
|
|
160f59a984 | ||
|
|
d38d487c2a | ||
|
|
db28907543 | ||
|
|
c4261399b5 | ||
|
|
b4edb46269 | ||
|
|
a20ed0e630 | ||
|
|
cea869e285 | ||
|
|
e12db14d1e | ||
|
|
296bf3cc6c | ||
|
|
1789aa180d | ||
|
|
bd255800db | ||
|
|
1355962b3c | ||
|
|
bb0114e379 | ||
|
|
f9622a5b9e | ||
|
|
3a74fcd75c | ||
|
|
7265276cc2 | ||
|
|
b98027b528 | ||
|
|
b6ae7d2cdd | ||
|
|
aa887c61c3 | ||
|
|
700cef0989 | ||
|
|
3ed3e553e7 | ||
|
|
d68158ddc9 | ||
|
|
9f83a69242 | ||
|
|
bf69dbd43d | ||
|
|
465ea5ccfd | ||
|
|
92ef39e2ad | ||
|
|
0404790df9 | ||
|
|
f880e93df4 | ||
|
|
4697b1101d | ||
|
|
50ff2accd2 | ||
|
|
c7d876eb8f | ||
|
|
eda392dfcd | ||
|
|
3b91e14f6d | ||
|
|
17e3c57d7e | ||
|
|
1c744a0f97 | ||
|
|
99bdb20aeb | ||
|
|
fbe7050cb8 | ||
|
|
12ea028aa9 | ||
|
|
ea62cb5fc9 |
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,46 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve Flux v2
|
|
||||||
title: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Find out more about your support options and getting help at
|
|
||||||
|
|
||||||
https://fluxcd.io/support/
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Describe the bug
|
|
||||||
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
### To Reproduce
|
|
||||||
|
|
||||||
Steps to reproduce the behaviour:
|
|
||||||
|
|
||||||
1. Provide Flux install instructions
|
|
||||||
2. Provide a GitHub repository with Kubernetes manifests
|
|
||||||
|
|
||||||
### Expected behavior
|
|
||||||
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
|
|
||||||
- Kubernetes version:
|
|
||||||
- Git provider:
|
|
||||||
- Container registry provider:
|
|
||||||
|
|
||||||
Below please provide the output of the following commands:
|
|
||||||
|
|
||||||
```cli
|
|
||||||
flux --version
|
|
||||||
flux check
|
|
||||||
kubectl -n <namespace> get all
|
|
||||||
kubectl -n <namespace> logs deploy/source-controller
|
|
||||||
kubectl -n <namespace> logs deploy/kustomize-controller
|
|
||||||
```
|
|
||||||
84
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
84
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
description: Create a report to help us improve Flux
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## Support
|
||||||
|
Find out more about your support options and getting help at: https://fluxcd.io/support/
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear description of what the bug is.
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: |
|
||||||
|
Steps to reproduce the problem.
|
||||||
|
placeholder: |
|
||||||
|
For example:
|
||||||
|
1. Install Flux with the additional image automation controllers
|
||||||
|
2. Run command '...'
|
||||||
|
3. See error
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
description: A brief description of what you expected to happen.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Screenshots and recordings
|
||||||
|
description: |
|
||||||
|
If applicable, add screenshots to help explain your problem. You can also record an asciinema session: https://asciinema.org/
|
||||||
|
- type: input
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: OS / Distro
|
||||||
|
description: The OS / distro you are executing `flux` on. If not applicable, write `N/A`.
|
||||||
|
placeholder: e.g. Windows 10, Ubuntu 20.04, Arch Linux, macOS 10.15...
|
||||||
|
- type: input
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Flux version
|
||||||
|
description: Run `flux version --client`. If not applicable, write `N/A`.
|
||||||
|
placeholder: e.g. v0.20.1
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Flux check
|
||||||
|
description: Run `flux check`. If not applicable, write `N/A`.
|
||||||
|
placeholder: |
|
||||||
|
For example:
|
||||||
|
► checking prerequisites
|
||||||
|
✔ Kubernetes 1.21.1 >=1.19.0-0
|
||||||
|
► checking controllers
|
||||||
|
✔ all checks passed
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Git provider
|
||||||
|
description: If applicable, add the Git provider you are having problems with, e.g. GitHub (Enterprise), GitLab, etc.
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Container Registry provider
|
||||||
|
description: If applicable, add the Container Registry provider you are having problems with, e.g. DockerHub, GitHub Packages, Quay.io, etc.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context about the problem here. This can be logs (e.g. output from `flux logs`), environment specific caveats, etc.
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: Code of Conduct
|
||||||
|
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/fluxcd/.github/blob/main/CODE_OF_CONDUCT.md)
|
||||||
|
options:
|
||||||
|
- label: I agree to follow this project's Code of Conduct
|
||||||
|
required: true
|
||||||
1
.github/aur/flux-bin/.SRCINFO.template
vendored
1
.github/aur/flux-bin/.SRCINFO.template
vendored
@@ -8,7 +8,6 @@ pkgbase = flux-bin
|
|||||||
arch = armv7h
|
arch = armv7h
|
||||||
arch = aarch64
|
arch = aarch64
|
||||||
license = APACHE
|
license = APACHE
|
||||||
optdepends = kubectl
|
|
||||||
source_x86_64 = flux-bin-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v1/flux_${PKGVER}_linux_amd64.tar.gz
|
source_x86_64 = flux-bin-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v1/flux_${PKGVER}_linux_amd64.tar.gz
|
||||||
source_armv6h = flux-bin-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v1/flux_${PKGVER}_linux_arm.tar.gz
|
source_armv6h = flux-bin-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v1/flux_${PKGVER}_linux_arm.tar.gz
|
||||||
source_armv7h = flux-bin-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v1/flux_${PKGVER}_linux_arm.tar.gz
|
source_armv7h = flux-bin-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v1/flux_${PKGVER}_linux_arm.tar.gz
|
||||||
|
|||||||
3
.github/aur/flux-bin/PKGBUILD.template
vendored
3
.github/aur/flux-bin/PKGBUILD.template
vendored
@@ -8,8 +8,7 @@ pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
|||||||
url="https://fluxcd.io/"
|
url="https://fluxcd.io/"
|
||||||
arch=("x86_64" "armv6h" "armv7h" "aarch64")
|
arch=("x86_64" "armv6h" "armv7h" "aarch64")
|
||||||
license=("APACHE")
|
license=("APACHE")
|
||||||
optdepends=('kubectl: for apply actions on the Kubernetes cluster',
|
optdepends=('bash-completion: auto-completion for flux in Bash',
|
||||||
'bash-completion: auto-completion for flux in Bash',
|
|
||||||
'zsh-completions: auto-completion for flux in ZSH')
|
'zsh-completions: auto-completion for flux in ZSH')
|
||||||
source_x86_64=(
|
source_x86_64=(
|
||||||
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_amd64.tar.gz"
|
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_amd64.tar.gz"
|
||||||
|
|||||||
1
.github/aur/flux-go/.SRCINFO.template
vendored
1
.github/aur/flux-go/.SRCINFO.template
vendored
@@ -10,7 +10,6 @@ pkgbase = flux-go
|
|||||||
license = APACHE
|
license = APACHE
|
||||||
makedepends = go
|
makedepends = go
|
||||||
depends = glibc
|
depends = glibc
|
||||||
optdepends = kubectl
|
|
||||||
provides = flux-bin
|
provides = flux-bin
|
||||||
conflicts = flux-bin
|
conflicts = flux-bin
|
||||||
replaces = flux-cli
|
replaces = flux-cli
|
||||||
|
|||||||
5
.github/aur/flux-go/PKGBUILD.template
vendored
5
.github/aur/flux-go/PKGBUILD.template
vendored
@@ -12,9 +12,8 @@ provides=("flux-bin")
|
|||||||
conflicts=("flux-bin")
|
conflicts=("flux-bin")
|
||||||
replaces=("flux-cli")
|
replaces=("flux-cli")
|
||||||
depends=("glibc")
|
depends=("glibc")
|
||||||
makedepends=('go>=1.16', 'kustomize>=3.0')
|
makedepends=('go>=1.17', 'kustomize>=3.0')
|
||||||
optdepends=('kubectl: for apply actions on the Kubernetes cluster',
|
optdepends=('bash-completion: auto-completion for flux in Bash',
|
||||||
'bash-completion: auto-completion for flux in Bash',
|
|
||||||
'zsh-completions: auto-completion for flux in ZSH')
|
'zsh-completions: auto-completion for flux in ZSH')
|
||||||
source=(
|
source=(
|
||||||
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/archive/v${pkgver}.tar.gz"
|
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/archive/v${pkgver}.tar.gz"
|
||||||
|
|||||||
1
.github/aur/flux-scm/.SRCINFO.template
vendored
1
.github/aur/flux-scm/.SRCINFO.template
vendored
@@ -10,7 +10,6 @@ pkgbase = flux-scm
|
|||||||
license = APACHE
|
license = APACHE
|
||||||
makedepends = go
|
makedepends = go
|
||||||
depends = glibc
|
depends = glibc
|
||||||
optdepends = kubectl
|
|
||||||
provides = flux-bin
|
provides = flux-bin
|
||||||
conflicts = flux-bin
|
conflicts = flux-bin
|
||||||
source = git+https://github.com/fluxcd/flux2.git
|
source = git+https://github.com/fluxcd/flux2.git
|
||||||
|
|||||||
7
.github/aur/flux-scm/PKGBUILD.template
vendored
7
.github/aur/flux-scm/PKGBUILD.template
vendored
@@ -11,9 +11,8 @@ license=("APACHE")
|
|||||||
provides=("flux-bin")
|
provides=("flux-bin")
|
||||||
conflicts=("flux-bin")
|
conflicts=("flux-bin")
|
||||||
depends=("glibc")
|
depends=("glibc")
|
||||||
makedepends=('go>=1.16', 'kustomize>=3.0')
|
makedepends=('go>=1.17', 'kustomize>=3.0', 'git')
|
||||||
optdepends=('kubectl: for apply actions on the Kubernetes cluster',
|
optdepends=('bash-completion: auto-completion for flux in Bash',
|
||||||
'bash-completion: auto-completion for flux in Bash',
|
|
||||||
'zsh-completions: auto-completion for flux in ZSH')
|
'zsh-completions: auto-completion for flux in ZSH')
|
||||||
source=(
|
source=(
|
||||||
"git+https://github.com/fluxcd/flux2.git"
|
"git+https://github.com/fluxcd/flux2.git"
|
||||||
@@ -33,7 +32,7 @@ build() {
|
|||||||
export CGO_CXXFLAGS="$CXXFLAGS"
|
export CGO_CXXFLAGS="$CXXFLAGS"
|
||||||
export CGO_CPPFLAGS="$CPPFLAGS"
|
export CGO_CPPFLAGS="$CPPFLAGS"
|
||||||
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
|
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
|
||||||
make cmd/flux/manifests
|
make cmd/flux/.manifests.done
|
||||||
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
|
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
72
.github/runners/README.md
vendored
Normal file
72
.github/runners/README.md
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Flux ARM64 GitHub runners
|
||||||
|
|
||||||
|
The Flux ARM64 end-to-end tests run on Equinix instances provisioned with Docker and GitHub self-hosted runners.
|
||||||
|
|
||||||
|
## Current instances
|
||||||
|
|
||||||
|
| Runner | Instance | Region |
|
||||||
|
|---------------|---------------------|--------|
|
||||||
|
| equinix-arm-1 | flux-equinix-arm-01 | AMS1 |
|
||||||
|
| equinix-arm-2 | flux-equinix-arm-01 | AMS1 |
|
||||||
|
| equinix-arm-3 | flux-equinix-arm-01 | AMS1 |
|
||||||
|
| equinix-arm-4 | flux-equinix-arm-02 | DFW2 |
|
||||||
|
| equinix-arm-5 | flux-equinix-arm-02 | DFW2 |
|
||||||
|
| equinix-arm-6 | flux-equinix-arm-02 | DFW2 |
|
||||||
|
|
||||||
|
## Instance setup
|
||||||
|
|
||||||
|
In order to add a new runner to the GitHub Actions pool,
|
||||||
|
first create a server on Equinix with the following configuration:
|
||||||
|
- Type: c2.large.arm
|
||||||
|
- OS: Ubuntu 20.04
|
||||||
|
|
||||||
|
### Install prerequisites
|
||||||
|
|
||||||
|
- SSH into a newly created instance
|
||||||
|
```shell
|
||||||
|
ssh root@<instance-public-IP>
|
||||||
|
```
|
||||||
|
|
||||||
|
- Create the ubuntu user
|
||||||
|
```shell
|
||||||
|
adduser ubuntu
|
||||||
|
usermod -aG sudo ubuntu
|
||||||
|
su - ubuntu
|
||||||
|
```
|
||||||
|
|
||||||
|
- Create the prerequisites dir
|
||||||
|
```shell
|
||||||
|
mkdir -p prereq && cd prereq
|
||||||
|
```
|
||||||
|
|
||||||
|
- Download the prerequisites script
|
||||||
|
```shell
|
||||||
|
curl -sL https://raw.githubusercontent.com/fluxcd/flux2/main/.github/runners/prereq.sh > prereq.sh \
|
||||||
|
&& chmod +x ./prereq.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
- Install the prerequisites
|
||||||
|
```shell
|
||||||
|
sudo ./prereq.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install runners
|
||||||
|
|
||||||
|
- Retrieve the GitHub runner token from the repository [settings page](https://github.com/fluxcd/flux2/settings/actions/runners/new?arch=arm64&os=linux)
|
||||||
|
|
||||||
|
- Create 3 directories `runner1`, `runner2`, `runner3`
|
||||||
|
|
||||||
|
- In each dir run:
|
||||||
|
```shell
|
||||||
|
curl -sL https://raw.githubusercontent.com/fluxcd/flux2/main/.github/runners/runner-setup.sh > runner-setup.sh \
|
||||||
|
&& chmod +x ./runner-setup.sh
|
||||||
|
|
||||||
|
./runner-setup.sh equinix-arm-<NUMBER> <TOKEN>
|
||||||
|
```
|
||||||
|
|
||||||
|
- Reboot the instance
|
||||||
|
```shell
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
- Navigate to the GitHub repository [runners page](https://github.com/fluxcd/flux2/settings/actions/runners) and check the runner status
|
||||||
68
.github/runners/prereq.sh
vendored
Executable file
68
.github/runners/prereq.sh
vendored
Executable file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright 2021 The Flux authors. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# This script installs the prerequisites for running Flux end-to-end tests with Docker and GitHub self-hosted runners.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
KIND_VERSION=0.11.1
|
||||||
|
KUBECTL_VERSION=1.21.2
|
||||||
|
KUSTOMIZE_VERSION=4.1.3
|
||||||
|
HELM_VERSION=3.7.2
|
||||||
|
GITHUB_RUNNER_VERSION=2.285.1
|
||||||
|
PACKAGES="apt-transport-https ca-certificates software-properties-common build-essential libssl-dev gnupg lsb-release jq"
|
||||||
|
|
||||||
|
# install prerequisites
|
||||||
|
apt-get update \
|
||||||
|
&& apt-get install -y -q ${PACKAGES} \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# install docker
|
||||||
|
curl -fsSL https://get.docker.com -o get-docker.sh \
|
||||||
|
&& chmod +x get-docker.sh
|
||||||
|
./get-docker.sh
|
||||||
|
systemctl enable docker.service
|
||||||
|
systemctl enable containerd.service
|
||||||
|
usermod -aG docker ubuntu
|
||||||
|
|
||||||
|
# install kind
|
||||||
|
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-linux-arm64
|
||||||
|
install -o root -g root -m 0755 kind /usr/local/bin/kind
|
||||||
|
|
||||||
|
# install kubectl
|
||||||
|
curl -LO "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/arm64/kubectl"
|
||||||
|
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
|
||||||
|
|
||||||
|
# install kustomize
|
||||||
|
curl -Lo ./kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_linux_arm64.tar.gz \
|
||||||
|
&& tar -zxvf kustomize.tar.gz \
|
||||||
|
&& rm kustomize.tar.gz
|
||||||
|
install -o root -g root -m 0755 kustomize /usr/local/bin/kustomize
|
||||||
|
|
||||||
|
# install helm
|
||||||
|
curl -Lo ./helm.tar.gz https://get.helm.sh/helm-v${HELM_VERSION}-linux-arm64.tar.gz \
|
||||||
|
&& tar -zxvf helm.tar.gz \
|
||||||
|
&& rm helm.tar.gz
|
||||||
|
install -o root -g root -m 0755 linux-arm64/helm /usr/local/bin/helm
|
||||||
|
|
||||||
|
# download runner
|
||||||
|
curl -o actions-runner-linux-arm64.tar.gz -L https://github.com/actions/runner/releases/download/v${GITHUB_RUNNER_VERSION}/actions-runner-linux-arm64-${GITHUB_RUNNER_VERSION}.tar.gz \
|
||||||
|
&& tar xzf actions-runner-linux-arm64.tar.gz \
|
||||||
|
&& rm actions-runner-linux-arm64.tar.gz
|
||||||
|
|
||||||
|
# install runner dependencies
|
||||||
|
./bin/installdependencies.sh
|
||||||
37
.github/runners/runner-setup.sh
vendored
Executable file
37
.github/runners/runner-setup.sh
vendored
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright 2021 The Flux authors. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# This script installs a GitHub self-hosted ARM64 runner for running Flux end-to-end tests.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
RUNNER_NAME=$1
|
||||||
|
REPOSITORY_TOKEN=$2
|
||||||
|
REPOSITORY_URL=${3:-https://github.com/fluxcd/flux2}
|
||||||
|
|
||||||
|
GITHUB_RUNNER_VERSION=2.285.1
|
||||||
|
|
||||||
|
# download runner
|
||||||
|
curl -o actions-runner-linux-arm64.tar.gz -L https://github.com/actions/runner/releases/download/v${GITHUB_RUNNER_VERSION}/actions-runner-linux-arm64-${GITHUB_RUNNER_VERSION}.tar.gz \
|
||||||
|
&& tar xzf actions-runner-linux-arm64.tar.gz \
|
||||||
|
&& rm actions-runner-linux-arm64.tar.gz
|
||||||
|
|
||||||
|
# register runner with GitHub
|
||||||
|
./config.sh --unattended --url ${REPOSITORY_URL} --token ${REPOSITORY_TOKEN} --name ${RUNNER_NAME}
|
||||||
|
|
||||||
|
# start runner
|
||||||
|
sudo ./svc.sh install
|
||||||
|
sudo ./svc.sh start
|
||||||
52
.github/workflows/bootstrap.yaml
vendored
52
.github/workflows/bootstrap.yaml
vendored
@@ -17,20 +17,23 @@ jobs:
|
|||||||
uses: actions/cache@v1
|
uses: actions/cache@v1
|
||||||
with:
|
with:
|
||||||
path: ~/go/pkg/mod
|
path: ~/go/pkg/mod
|
||||||
key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-go1.17-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-go1.16-
|
${{ runner.os }}-go1.17-
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.x
|
go-version: 1.17.x
|
||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: engineerd/setup-kind@v0.5.0
|
uses: engineerd/setup-kind@v0.5.0
|
||||||
|
with:
|
||||||
|
version: v0.11.1
|
||||||
|
image: kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6
|
||||||
- name: Setup Kustomize
|
- name: Setup Kustomize
|
||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg//actions/kustomize@main
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
make cmd/flux/manifests
|
make cmd/flux/.manifests.done
|
||||||
go build -o /tmp/flux ./cmd/flux
|
go build -o /tmp/flux ./cmd/flux
|
||||||
- name: Set outputs
|
- name: Set outputs
|
||||||
id: vars
|
id: vars
|
||||||
@@ -61,20 +64,51 @@ jobs:
|
|||||||
--team=team-z
|
--team=team-z
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
- name: uninstall
|
- name: bootstrap customize
|
||||||
run: |
|
|
||||||
/tmp/flux uninstall -s --keep-namespace
|
|
||||||
kubectl delete ns flux-system --timeout=10m --wait=true
|
|
||||||
- name: bootstrap reinstall
|
|
||||||
run: |
|
run: |
|
||||||
|
make setup-bootstrap-patch
|
||||||
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||||
--owner=fluxcd-testing \
|
--owner=fluxcd-testing \
|
||||||
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
||||||
--branch=main \
|
--branch=main \
|
||||||
--path=test-cluster \
|
--path=test-cluster \
|
||||||
--team=team-z
|
--team=team-z
|
||||||
|
if [ $(kubectl get deployments.apps source-controller -o jsonpath='{.spec.template.spec.securityContext.runAsUser}') != "10000" ]; then
|
||||||
|
echo "Bootstrap not customized as controller is not running as user 10000" && exit 1
|
||||||
|
fi
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
|
GITHUB_REPO_NAME: ${{ steps.vars.outputs.test_repo_name }}
|
||||||
|
GITHUB_ORG_NAME: fluxcd-testing
|
||||||
|
- name: libgit2
|
||||||
|
run: |
|
||||||
|
/tmp/flux create source git test-libgit2 \
|
||||||
|
--url=ssh://git@github.com/fluxcd-testing/${{ steps.vars.outputs.test_repo_name }} \
|
||||||
|
--git-implementation=libgit2 \
|
||||||
|
--secret-ref=flux-system \
|
||||||
|
--branch=main
|
||||||
|
- name: uninstall
|
||||||
|
run: |
|
||||||
|
/tmp/flux uninstall -s --keep-namespace
|
||||||
|
kubectl delete ns flux-system --timeout=10m --wait=true
|
||||||
|
- name: test image automation
|
||||||
|
run: |
|
||||||
|
make setup-image-automation
|
||||||
|
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||||
|
--owner=fluxcd-testing \
|
||||||
|
--repository=${{ steps.vars.outputs.test_repo_name }} \
|
||||||
|
--branch=main \
|
||||||
|
--path=test-cluster \
|
||||||
|
--read-write-key
|
||||||
|
/tmp/flux reconcile image repository podinfo
|
||||||
|
/tmp/flux reconcile image update flux-system
|
||||||
|
/tmp/flux get images all
|
||||||
|
/tmp/flux get images policy podinfo | grep "5.2.1"
|
||||||
|
/tmp/flux get image update flux-system | grep commit
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
|
GITHUB_REPO_NAME: ${{ steps.vars.outputs.test_repo_name }}
|
||||||
|
GITHUB_ORG_NAME: fluxcd-testing
|
||||||
- name: delete repository
|
- name: delete repository
|
||||||
run: |
|
run: |
|
||||||
curl \
|
curl \
|
||||||
|
|||||||
37
.github/workflows/e2e-arm64.yaml
vendored
Normal file
37
.github/workflows/e2e-arm64.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: e2e-arm64
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ main, update-components, equinix-runners ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
# Hosted on Equinix
|
||||||
|
# Docs: https://github.com/fluxcd/flux2/tree/main/.github/runners
|
||||||
|
runs-on: [self-hosted, Linux, ARM64, equinix]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.17.x
|
||||||
|
- name: Prepare
|
||||||
|
id: prep
|
||||||
|
run: |
|
||||||
|
echo ::set-output name=CLUSTER::arm64-${GITHUB_SHA:0:7}-$(date +%s)
|
||||||
|
echo ::set-output name=CONTEXT::kind-arm64-${GITHUB_SHA:0:7}-$(date +%s)
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
make build
|
||||||
|
- name: Setup Kubernetes Kind
|
||||||
|
run: |
|
||||||
|
kind create cluster --name ${{ steps.prep.outputs.CLUSTER }} --kubeconfig=/tmp/${{ steps.prep.outputs.CLUSTER }}
|
||||||
|
- name: Run e2e tests
|
||||||
|
run: TEST_KUBECONFIG=/tmp/${{ steps.prep.outputs.CLUSTER }} make e2e
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
kind delete cluster --name ${{ steps.prep.outputs.CLUSTER }}
|
||||||
|
rm /tmp/${{ steps.prep.outputs.CLUSTER }}
|
||||||
66
.github/workflows/e2e-azure.yaml
vendored
Normal file
66
.github/workflows/e2e-azure.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
name: e2e-azure
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 6 * * *'
|
||||||
|
push:
|
||||||
|
branches: [ azure* ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
e2e:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Restore Go cache
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go1.17-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go1.17-
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.17.x
|
||||||
|
- name: Install libgit2
|
||||||
|
run: |
|
||||||
|
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 648ACFD622F3D138
|
||||||
|
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 0E98404D386FA1D9
|
||||||
|
echo "deb http://deb.debian.org/debian unstable main" | sudo tee -a /etc/apt/sources.list
|
||||||
|
echo "deb-src http://deb.debian.org/debian unstable main" | sudo tee -a /etc/apt/sources.list
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y --allow-downgrades libgit2-dev/unstable zlib1g-dev/unstable libssh2-1-dev/unstable libpcre3-dev/unstable
|
||||||
|
- name: Setup Flux CLI
|
||||||
|
run: |
|
||||||
|
make build
|
||||||
|
mkdir -p $HOME/.local/bin
|
||||||
|
mv ./bin/flux $HOME/.local/bin
|
||||||
|
- name: Setup SOPS
|
||||||
|
run: |
|
||||||
|
wget https://github.com/mozilla/sops/releases/download/v3.7.1/sops-v3.7.1.linux
|
||||||
|
chmod +x sops-v3.7.1.linux
|
||||||
|
mkdir -p $HOME/.local/bin
|
||||||
|
mv sops-v3.7.1.linux $HOME/.local/bin/sops
|
||||||
|
- name: Setup Terraform
|
||||||
|
uses: hashicorp/setup-terraform@v1
|
||||||
|
with:
|
||||||
|
terraform_version: 1.0.7
|
||||||
|
terraform_wrapper: false
|
||||||
|
- name: Setup Azure CLI
|
||||||
|
run: |
|
||||||
|
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||||
|
- name: Run Azure e2e tests
|
||||||
|
env:
|
||||||
|
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
|
||||||
|
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
|
||||||
|
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
|
||||||
|
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
|
||||||
|
run: |
|
||||||
|
echo $HOME
|
||||||
|
echo $PATH
|
||||||
|
ls $HOME/.local/bin
|
||||||
|
az login --service-principal -u ${ARM_CLIENT_ID} -p ${ARM_CLIENT_SECRET} -t ${ARM_TENANT_ID}
|
||||||
|
cd ./tests/azure
|
||||||
|
go test -v -coverprofile cover.out -timeout 60m .
|
||||||
62
.github/workflows/e2e.yaml
vendored
62
.github/workflows/e2e.yaml
vendored
@@ -2,7 +2,7 @@ name: e2e
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main, e2e* ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
@@ -16,27 +16,29 @@ jobs:
|
|||||||
uses: actions/cache@v1
|
uses: actions/cache@v1
|
||||||
with:
|
with:
|
||||||
path: ~/go/pkg/mod
|
path: ~/go/pkg/mod
|
||||||
key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-go1.17-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-go1.16-
|
${{ runner.os }}-go1.17-
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.x
|
go-version: 1.17.x
|
||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: engineerd/setup-kind@v0.5.0
|
uses: engineerd/setup-kind@v0.5.0
|
||||||
with:
|
with:
|
||||||
version: "v0.10.0"
|
version: v0.11.1
|
||||||
image: kindest/node:v1.20.2@sha256:8f7ea6e7642c0da54f04a7ee10431549c0257315b3a634f6ef2fecaaedb19bab
|
image: kindest/node:v1.19.11@sha256:07db187ae84b4b7de440a73886f008cf903fcf5764ba8106a9fd5243d6f32729
|
||||||
config: .github/kind/config.yaml # disable KIND-net
|
config: .github/kind/config.yaml # disable KIND-net
|
||||||
- name: Setup Calico for network policy
|
- name: Setup Calico for network policy
|
||||||
run: |
|
run: |
|
||||||
kubectl apply -f https://docs.projectcalico.org/v3.16/manifests/calico.yaml
|
kubectl apply -f https://docs.projectcalico.org/v3.20/manifests/calico.yaml
|
||||||
kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
|
kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
|
||||||
- name: Setup Kustomize
|
- name: Setup Kustomize
|
||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg//actions/kustomize@main
|
||||||
- name: Run test
|
- name: Run tests
|
||||||
run: make test
|
run: make test
|
||||||
|
- name: Run e2e tests
|
||||||
|
run: TEST_KUBECONFIG=$HOME/.kube/config make e2e
|
||||||
- name: Check if working tree is dirty
|
- name: Check if working tree is dirty
|
||||||
run: |
|
run: |
|
||||||
if [[ $(git diff --stat) != '' ]]; then
|
if [[ $(git diff --stat) != '' ]]; then
|
||||||
@@ -74,6 +76,13 @@ jobs:
|
|||||||
--tag-semver=">=3.2.3" \
|
--tag-semver=">=3.2.3" \
|
||||||
--export | kubectl apply -f -
|
--export | kubectl apply -f -
|
||||||
/tmp/flux delete source git podinfo-export --silent
|
/tmp/flux delete source git podinfo-export --silent
|
||||||
|
- name: flux create source git libgit2 semver
|
||||||
|
run: |
|
||||||
|
/tmp/flux create source git podinfo-libgit2 \
|
||||||
|
--url https://github.com/stefanprodan/podinfo \
|
||||||
|
--tag-semver=">=3.2.3" \
|
||||||
|
--git-implementation=libgit2
|
||||||
|
/tmp/flux delete source git podinfo-libgit2 --silent
|
||||||
- name: flux get sources git
|
- name: flux get sources git
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux get sources git
|
/tmp/flux get sources git
|
||||||
@@ -87,10 +96,15 @@ jobs:
|
|||||||
--path="./deploy/overlays/dev" \
|
--path="./deploy/overlays/dev" \
|
||||||
--prune=true \
|
--prune=true \
|
||||||
--interval=5m \
|
--interval=5m \
|
||||||
--validation=client \
|
|
||||||
--health-check="Deployment/frontend.dev" \
|
--health-check="Deployment/frontend.dev" \
|
||||||
--health-check="Deployment/backend.dev" \
|
--health-check="Deployment/backend.dev" \
|
||||||
--health-check-timeout=3m
|
--health-check-timeout=3m
|
||||||
|
- name: flux trace
|
||||||
|
run: |
|
||||||
|
/tmp/flux trace frontend \
|
||||||
|
--kind=deployment \
|
||||||
|
--api-version=apps/v1 \
|
||||||
|
--namespace=dev
|
||||||
- name: flux reconcile kustomization --with-source
|
- name: flux reconcile kustomization --with-source
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux reconcile kustomization podinfo --with-source
|
/tmp/flux reconcile kustomization podinfo --with-source
|
||||||
@@ -164,26 +178,6 @@ jobs:
|
|||||||
--chart=podinfo \
|
--chart=podinfo \
|
||||||
--chart-version="5.0.x" \
|
--chart-version="5.0.x" \
|
||||||
--service-account=dev-team
|
--service-account=dev-team
|
||||||
- name: flux create image repository
|
|
||||||
run: |
|
|
||||||
/tmp/flux create image repository podinfo \
|
|
||||||
--image=ghcr.io/stefanprodan/podinfo \
|
|
||||||
--interval=1m
|
|
||||||
- name: flux create image policy
|
|
||||||
run: |
|
|
||||||
/tmp/flux create image policy podinfo \
|
|
||||||
--image-ref=podinfo \
|
|
||||||
--interval=1m \
|
|
||||||
--select-semver=5.0.x
|
|
||||||
- name: flux create image policy podinfo-select-alpha
|
|
||||||
run: |
|
|
||||||
/tmp/flux create image policy podinfo-alpha \
|
|
||||||
--image-ref=podinfo \
|
|
||||||
--interval=1m \
|
|
||||||
--select-alpha=desc
|
|
||||||
- name: flux get image policy
|
|
||||||
run: |
|
|
||||||
/tmp/flux get image policy podinfo | grep '5.0.3'
|
|
||||||
- name: flux2-kustomize-helm-example
|
- name: flux2-kustomize-helm-example
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux create source git flux-system \
|
/tmp/flux create source git flux-system \
|
||||||
@@ -193,7 +187,14 @@ jobs:
|
|||||||
/tmp/flux create kustomization flux-system \
|
/tmp/flux create kustomization flux-system \
|
||||||
--source=flux-system \
|
--source=flux-system \
|
||||||
--path=./clusters/staging
|
--path=./clusters/staging
|
||||||
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=2m
|
kubectl -n flux-system wait kustomization/infrastructure --for=condition=ready --timeout=5m
|
||||||
|
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=5m
|
||||||
|
kubectl -n nginx wait helmrelease/nginx --for=condition=ready --timeout=5m
|
||||||
|
kubectl -n redis wait helmrelease/redis --for=condition=ready --timeout=5m
|
||||||
|
kubectl -n podinfo wait helmrelease/podinfo --for=condition=ready --timeout=5m
|
||||||
|
- name: flux tree
|
||||||
|
run: |
|
||||||
|
/tmp/flux tree kustomization flux-system | grep Service/podinfo
|
||||||
- name: flux check
|
- name: flux check
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux check
|
/tmp/flux check
|
||||||
@@ -205,6 +206,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
kubectl version --client --short
|
kubectl version --client --short
|
||||||
kubectl -n flux-system get all
|
kubectl -n flux-system get all
|
||||||
|
kubectl -n flux-system describe pods
|
||||||
kubectl -n flux-system get kustomizations -oyaml
|
kubectl -n flux-system get kustomizations -oyaml
|
||||||
kubectl -n flux-system logs deploy/source-controller
|
kubectl -n flux-system logs deploy/source-controller
|
||||||
kubectl -n flux-system logs deploy/kustomize-controller
|
kubectl -n flux-system logs deploy/kustomize-controller
|
||||||
|
|||||||
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.x
|
go-version: 1.17.x
|
||||||
- name: Setup QEMU
|
- name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
with:
|
with:
|
||||||
@@ -50,14 +50,16 @@ jobs:
|
|||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg//actions/kustomize@main
|
||||||
- name: Generate manifests
|
- name: Generate manifests
|
||||||
run: |
|
run: |
|
||||||
make cmd/flux/manifests
|
make cmd/flux/.manifests.done
|
||||||
./manifests/scripts/bundle.sh "" ./output manifests.tar.gz
|
./manifests/scripts/bundle.sh "" ./output manifests.tar.gz
|
||||||
kustomize build ./manifests/install > ./output/install.yaml
|
kustomize build ./manifests/install > ./output/install.yaml
|
||||||
- name: Build CRDs
|
- name: Build CRDs
|
||||||
run: |
|
run: |
|
||||||
kustomize build manifests/crds > all-crds.yaml
|
kustomize build manifests/crds > all-crds.yaml
|
||||||
|
# Pinned to commit before https://github.com/fluxcd/pkg/pull/189 due to
|
||||||
|
# introduction faulty behavior.
|
||||||
- name: Generate OpenAPI JSON schemas from CRDs
|
- name: Generate OpenAPI JSON schemas from CRDs
|
||||||
uses: fluxcd/pkg//actions/crdjsonschema@main
|
uses: fluxcd/pkg//actions/crdjsonschema@49e26aa2ee9e734c3233c560253fd9542afe18ae
|
||||||
with:
|
with:
|
||||||
crd: all-crds.yaml
|
crd: all-crds.yaml
|
||||||
output: schemas
|
output: schemas
|
||||||
|
|||||||
2
.github/workflows/scan.yaml
vendored
2
.github/workflows/scan.yaml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg//actions/kustomize@main
|
||||||
- name: Build manifests
|
- name: Build manifests
|
||||||
run: |
|
run: |
|
||||||
make cmd/flux/manifests
|
make cmd/flux/.manifests.done
|
||||||
- name: Run Snyk to check for vulnerabilities
|
- name: Run Snyk to check for vulnerabilities
|
||||||
uses: snyk/actions/golang@master
|
uses: snyk/actions/golang@master
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|||||||
2
.github/workflows/update.yaml
vendored
2
.github/workflows/update.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.x
|
go-version: 1.17.x
|
||||||
- name: Update component versions
|
- name: Update component versions
|
||||||
id: update
|
id: update
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -19,6 +19,8 @@ dist/
|
|||||||
bin/
|
bin/
|
||||||
output/
|
output/
|
||||||
cmd/flux/manifests/
|
cmd/flux/manifests/
|
||||||
|
cmd/flux/.manifests.done
|
||||||
|
testbin/
|
||||||
|
|
||||||
# Docs
|
# Docs
|
||||||
site/
|
site/
|
||||||
|
|||||||
@@ -49,9 +49,17 @@ brews:
|
|||||||
folder: Formula
|
folder: Formula
|
||||||
homepage: "https://fluxcd.io/"
|
homepage: "https://fluxcd.io/"
|
||||||
description: "Flux CLI"
|
description: "Flux CLI"
|
||||||
dependencies:
|
install: |
|
||||||
- name: kubectl
|
bin.install "flux"
|
||||||
type: optional
|
|
||||||
|
bash_output = Utils.safe_popen_read(bin/"flux", "completion", "bash")
|
||||||
|
(bash_completion/"flux").write bash_output
|
||||||
|
|
||||||
|
zsh_output = Utils.safe_popen_read(bin/"flux", "completion", "zsh")
|
||||||
|
(zsh_completion/"_flux").write zsh_output
|
||||||
|
|
||||||
|
fish_output = Utils.safe_popen_read(bin/"flux", "completion", "fish")
|
||||||
|
(fish_completion/"flux.fish").write fish_output
|
||||||
test: |
|
test: |
|
||||||
system "#{bin}/flux --version"
|
system "#{bin}/flux --version"
|
||||||
publishers:
|
publishers:
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ you can sign your commit automatically with `git commit -s`.
|
|||||||
|
|
||||||
For realtime communications we use Slack: To join the conversation, simply
|
For realtime communications we use Slack: To join the conversation, simply
|
||||||
join the [CNCF](https://slack.cncf.io/) Slack workspace and use the
|
join the [CNCF](https://slack.cncf.io/) Slack workspace and use the
|
||||||
[#flux-dev](https://cloud-native.slack.com/messages/flux-dev/) channel.
|
[#flux-contributors](https://cloud-native.slack.com/messages/flux-contributors/) channel.
|
||||||
|
|
||||||
To discuss ideas and specifications we use [Github
|
To discuss ideas and specifications we use [Github
|
||||||
Discussions](https://github.com/fluxcd/flux2/discussions).
|
Discussions](https://github.com/fluxcd/flux2/discussions).
|
||||||
@@ -63,20 +63,45 @@ To get started with developing controllers, you might want to review
|
|||||||
walks you through writing a short and concise controller that watches out
|
walks you through writing a short and concise controller that watches out
|
||||||
for source changes.
|
for source changes.
|
||||||
|
|
||||||
### How to run the test suite
|
## How to run the test suite
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|
||||||
* go >= 1.16
|
* go >= 1.16
|
||||||
* kubectl >= 1.18
|
* kubectl >= 1.19
|
||||||
* kustomize >= 3.1
|
* kustomize >= 4.0
|
||||||
|
|
||||||
You can run the unit tests by simply doing
|
Install the [controller-runtime/envtest](https://github.com/kubernetes-sigs/controller-runtime/tree/master/tools/setup-envtest) binaries with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make install-envtest
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can run the unit tests with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make test
|
make test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
After [installing Kubernetes kind](https://kind.sigs.k8s.io/docs/user/quick-start#installation) on your machine,
|
||||||
|
create a cluster for testing with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make setup-kind
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can run the end-to-end tests with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
Teardown the e2e environment with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make cleanup-kind
|
||||||
|
```
|
||||||
|
|
||||||
## Acceptance policy
|
## Acceptance policy
|
||||||
|
|
||||||
These things will make a PR more likely to be accepted:
|
These things will make a PR more likely to be accepted:
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
FROM alpine:3.13 as builder
|
FROM alpine:3.15 as builder
|
||||||
|
|
||||||
RUN apk add --no-cache ca-certificates curl
|
RUN apk add --no-cache ca-certificates curl
|
||||||
|
|
||||||
ARG ARCH=linux/amd64
|
ARG ARCH=linux/amd64
|
||||||
ARG KUBECTL_VER=1.20.4
|
ARG KUBECTL_VER=1.22.2
|
||||||
|
|
||||||
RUN curl -sL https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \
|
RUN curl -sL https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \
|
||||||
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl && \
|
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl && \
|
||||||
kubectl version --client=true
|
kubectl version --client=true
|
||||||
|
|
||||||
FROM alpine:3.13 as flux-cli
|
FROM alpine:3.15 as flux-cli
|
||||||
|
|
||||||
# Create minimal nsswitch.conf file to prioritize the usage of /etc/hosts over DNS queries.
|
# Create minimal nsswitch.conf file to prioritize the usage of /etc/hosts over DNS queries.
|
||||||
# https://github.com/gliderlabs/docker-alpine/issues/367#issuecomment-354316460
|
# https://github.com/gliderlabs/docker-alpine/issues/367#issuecomment-354316460
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ should.
|
|||||||
|
|
||||||
In alphabetical order:
|
In alphabetical order:
|
||||||
|
|
||||||
Aurel Canciu, Sortlist <aurel@sortlist.com> (github: @relu, slack: relu)
|
Aurel Canciu, NexHealth <aurel.canciu@nexhealth.com> (github: @relu, slack: relu)
|
||||||
Hidde Beydals, Weaveworks <hidde@weave.works> (github: @hiddeco, slack: hidde)
|
Hidde Beydals, Weaveworks <hidde@weave.works> (github: @hiddeco, slack: hidde)
|
||||||
|
Max Jonas Werner, D2iQ <max@e13.dev> (github: @makkes, slack: max)
|
||||||
Philip Laine, Xenit <philip.laine@xenit.se> (github: @phillebaba, slack: phillebaba)
|
Philip Laine, Xenit <philip.laine@xenit.se> (github: @phillebaba, slack: phillebaba)
|
||||||
Stefan Prodan, Weaveworks <stefan@weave.works> (github: @stefanprodan, slack: stefanprodan)
|
Stefan Prodan, Weaveworks <stefan@weave.works> (github: @stefanprodan, slack: stefanprodan)
|
||||||
|
Sunny, Weaveworks <sunny@weave.works> (github: @darkowlzz, slack: darkowlzz)
|
||||||
|
|||||||
74
Makefile
74
Makefile
@@ -1,5 +1,15 @@
|
|||||||
VERSION?=$(shell grep 'VERSION' cmd/flux/main.go | awk '{ print $$4 }' | tr -d '"')
|
VERSION?=$(shell grep 'VERSION' cmd/flux/main.go | awk '{ print $$4 }' | head -n 1 | tr -d '"')
|
||||||
EMBEDDED_MANIFESTS_TARGET=cmd/flux/manifests
|
EMBEDDED_MANIFESTS_TARGET=cmd/flux/.manifests.done
|
||||||
|
TEST_KUBECONFIG?=/tmp/flux-e2e-test-kubeconfig
|
||||||
|
# Architecture to use envtest with
|
||||||
|
ENVTEST_ARCH ?= amd64
|
||||||
|
|
||||||
|
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||||
|
ifeq (,$(shell go env GOBIN))
|
||||||
|
GOBIN=$(shell go env GOPATH)/bin
|
||||||
|
else
|
||||||
|
GOBIN=$(shell go env GOBIN)
|
||||||
|
endif
|
||||||
|
|
||||||
rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2)) $(filter $(subst *,%,$(2)),$(d)))
|
rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2)) $(filter $(subst *,%,$(2)),$(d)))
|
||||||
|
|
||||||
@@ -7,6 +17,7 @@ all: test build
|
|||||||
|
|
||||||
tidy:
|
tidy:
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
cd tests/azure && go mod tidy
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
go fmt ./...
|
go fmt ./...
|
||||||
@@ -14,17 +25,68 @@ fmt:
|
|||||||
vet:
|
vet:
|
||||||
go vet ./...
|
go vet ./...
|
||||||
|
|
||||||
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet
|
setup-kind:
|
||||||
go test ./... -coverprofile cover.out
|
kind create cluster --name=flux-e2e-test --kubeconfig=$(TEST_KUBECONFIG) --config=.github/kind/config.yaml
|
||||||
|
kubectl --kubeconfig=$(TEST_KUBECONFIG) apply -f https://docs.projectcalico.org/v3.16/manifests/calico.yaml
|
||||||
|
kubectl --kubeconfig=$(TEST_KUBECONFIG) -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
|
||||||
|
|
||||||
|
cleanup-kind:
|
||||||
|
kind delete cluster --name=flux-e2e-test
|
||||||
|
rm $(TEST_KUBECONFIG)
|
||||||
|
|
||||||
|
KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)"
|
||||||
|
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet install-envtest
|
||||||
|
KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test ./... -coverprofile cover.out --tags=unit
|
||||||
|
|
||||||
|
e2e: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet
|
||||||
|
TEST_KUBECONFIG=$(TEST_KUBECONFIG) go test ./cmd/flux/... -coverprofile e2e.cover.out --tags=e2e -v -failfast
|
||||||
|
|
||||||
|
test-with-kind: install-envtest
|
||||||
|
make setup-kind
|
||||||
|
make e2e
|
||||||
|
make cleanup-kind
|
||||||
|
|
||||||
$(EMBEDDED_MANIFESTS_TARGET): $(call rwildcard,manifests/,*.yaml *.json)
|
$(EMBEDDED_MANIFESTS_TARGET): $(call rwildcard,manifests/,*.yaml *.json)
|
||||||
./manifests/scripts/bundle.sh
|
./manifests/scripts/bundle.sh
|
||||||
|
touch $@
|
||||||
|
|
||||||
build: $(EMBEDDED_MANIFESTS_TARGET)
|
build: $(EMBEDDED_MANIFESTS_TARGET)
|
||||||
CGO_ENABLED=0 go build -o ./bin/flux ./cmd/flux
|
CGO_ENABLED=0 go build -ldflags="-s -w -X main.VERSION=$(VERSION)" -o ./bin/flux ./cmd/flux
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
install:
|
install:
|
||||||
go install cmd/flux
|
CGO_ENABLED=0 go install ./cmd/flux
|
||||||
|
|
||||||
install-dev:
|
install-dev:
|
||||||
CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/flux
|
CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/flux
|
||||||
|
|
||||||
|
setup-bootstrap-patch:
|
||||||
|
go run ./tests/bootstrap/main.go
|
||||||
|
|
||||||
|
setup-image-automation:
|
||||||
|
cd tests/image-automation && go run main.go
|
||||||
|
|
||||||
|
ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
|
||||||
|
ENVTEST_KUBERNETES_VERSION?=latest
|
||||||
|
install-envtest: setup-envtest
|
||||||
|
mkdir -p ${ENVTEST_ASSETS_DIR}
|
||||||
|
$(ENVTEST) use $(ENVTEST_KUBERNETES_VERSION) --arch=$(ENVTEST_ARCH) --bin-dir=$(ENVTEST_ASSETS_DIR)
|
||||||
|
|
||||||
|
ENVTEST = $(shell pwd)/bin/setup-envtest
|
||||||
|
.PHONY: envtest
|
||||||
|
setup-envtest: ## Download envtest-setup locally if necessary.
|
||||||
|
$(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest)
|
||||||
|
|
||||||
|
# go-install-tool will 'go install' any package $2 and install it to $1.
|
||||||
|
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
define go-install-tool
|
||||||
|
@[ -f $(1) ] || { \
|
||||||
|
set -e ;\
|
||||||
|
TMP_DIR=$$(mktemp -d) ;\
|
||||||
|
cd $$TMP_DIR ;\
|
||||||
|
go mod init tmp ;\
|
||||||
|
echo "Downloading $(2)" ;\
|
||||||
|
GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\
|
||||||
|
rm -rf $$TMP_DIR ;\
|
||||||
|
}
|
||||||
|
endef
|
||||||
|
|||||||
56
README.md
56
README.md
@@ -20,53 +20,15 @@ Flux v2 is constructed with the [GitOps Toolkit](#gitops-toolkit), a
|
|||||||
set of composable APIs and specialized tools for building Continuous
|
set of composable APIs and specialized tools for building Continuous
|
||||||
Delivery on top of Kubernetes.
|
Delivery on top of Kubernetes.
|
||||||
|
|
||||||
## Flux installation
|
Flux is a Cloud Native Computing Foundation ([CNCF](https://www.cncf.io/)) project.
|
||||||
|
|
||||||
With Homebrew:
|
## Quickstart and documentation
|
||||||
|
|
||||||
```sh
|
To get started check out this [guide](https://fluxcd.io/docs/get-started/)
|
||||||
brew install fluxcd/tap/flux
|
on how to bootstrap Flux on Kubernetes and deploy a sample application in a GitOps manner.
|
||||||
```
|
|
||||||
|
|
||||||
With Bash:
|
For more comprehensive documentation, see the following guides:
|
||||||
|
- [Ways of structuring your repositories](https://fluxcd.io/docs/guides/repository-structure/)
|
||||||
```sh
|
|
||||||
curl -s https://fluxcd.io/install.sh | sudo bash
|
|
||||||
|
|
||||||
# enable completions in ~/.bash_profile
|
|
||||||
. <(flux completion bash)
|
|
||||||
```
|
|
||||||
|
|
||||||
Arch Linux (AUR) packages:
|
|
||||||
|
|
||||||
- [flux-bin](https://aur.archlinux.org/packages/flux-bin): install the latest
|
|
||||||
stable version using a pre-build binary (recommended)
|
|
||||||
- [flux-go](https://aur.archlinux.org/packages/flux-go): build the latest
|
|
||||||
stable version from source code
|
|
||||||
- [flux-scm](https://aur.archlinux.org/packages/flux-scm): build the latest
|
|
||||||
(unstable) version from source code from our git `main` branch
|
|
||||||
|
|
||||||
Binaries for macOS, Windows and Linux AMD64/ARM are available to download on the
|
|
||||||
[release page](https://github.com/fluxcd/flux2/releases).
|
|
||||||
|
|
||||||
A container image with `kubectl` and `flux` is available on Docker Hub and GitHub:
|
|
||||||
|
|
||||||
* `docker.io/fluxcd/flux-cli:<version>`
|
|
||||||
* `ghcr.io/fluxcd/flux-cli:<version>`
|
|
||||||
|
|
||||||
Verify that your cluster satisfies the prerequisites with:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
flux check --pre
|
|
||||||
```
|
|
||||||
|
|
||||||
## Get started
|
|
||||||
|
|
||||||
To get started with Flux, start [browsing the
|
|
||||||
documentation](https://fluxcd.io/docs/) or get started with one of
|
|
||||||
the following guides:
|
|
||||||
|
|
||||||
- [Get started with Flux](https://fluxcd.io/docs/get-started/)
|
|
||||||
- [Manage Helm Releases](https://fluxcd.io/docs/guides/helmreleases/)
|
- [Manage Helm Releases](https://fluxcd.io/docs/guides/helmreleases/)
|
||||||
- [Automate image updates to Git](https://fluxcd.io/docs/guides/image-update/)
|
- [Automate image updates to Git](https://fluxcd.io/docs/guides/image-update/)
|
||||||
- [Manage Kubernetes secrets with Mozilla SOPS](https://fluxcd.io/docs/guides/mozilla-sops/)
|
- [Manage Kubernetes secrets with Mozilla SOPS](https://fluxcd.io/docs/guides/mozilla-sops/)
|
||||||
@@ -127,7 +89,9 @@ new contributors and there are a multitude of ways to get involved.
|
|||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
Check out our **[events calendar](https://fluxcd.io/community/#talks)**,
|
Check out our **[events calendar](https://fluxcd.io/#calendar)**,
|
||||||
both with upcoming talks you can attend or past events videos you can watch.
|
both with upcoming talks, events and meetings you can attend.
|
||||||
|
Or view the **[resources section](https://fluxcd.io/resources)**
|
||||||
|
with past events videos you can watch.
|
||||||
|
|
||||||
We look forward to seeing you with us!
|
We look forward to seeing you with us!
|
||||||
|
|||||||
@@ -10,11 +10,21 @@ Usage:
|
|||||||
run: flux -v
|
run: flux -v
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that this action can only be used on GitHub **Linux AMD64** runners.
|
|
||||||
The latest stable version of the `flux` binary is downloaded from
|
The latest stable version of the `flux` binary is downloaded from
|
||||||
GitHub [releases](https://github.com/fluxcd/flux2/releases)
|
GitHub [releases](https://github.com/fluxcd/flux2/releases)
|
||||||
and placed at `/usr/local/bin/flux`.
|
and placed at `/usr/local/bin/flux`.
|
||||||
|
|
||||||
|
Note that this action can only be used on GitHub **Linux** runners.
|
||||||
|
You can change the arch (defaults to `amd64`) with:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- name: Setup Flux CLI
|
||||||
|
uses: fluxcd/flux2/action@main
|
||||||
|
with:
|
||||||
|
arch: arm64 # can be amd64, arm64 or arm
|
||||||
|
```
|
||||||
|
|
||||||
You can download a specific version with:
|
You can download a specific version with:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -8,26 +8,40 @@ inputs:
|
|||||||
version:
|
version:
|
||||||
description: "Flux version e.g. 0.8.0 (defaults to latest stable release)"
|
description: "Flux version e.g. 0.8.0 (defaults to latest stable release)"
|
||||||
required: false
|
required: false
|
||||||
|
arch:
|
||||||
|
description: "arch can be amd64, arm64 or arm"
|
||||||
|
required: true
|
||||||
|
default: "amd64"
|
||||||
|
bindir:
|
||||||
|
description: "Optional location of the Flux binary. Will not use sudo if set. Updates System Path."
|
||||||
|
required: false
|
||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- name: "Download flux binary to tmp"
|
- name: "Download flux binary to tmp"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
ARCH=${{ inputs.arch }}
|
||||||
VERSION=${{ inputs.version }}
|
VERSION=${{ inputs.version }}
|
||||||
|
|
||||||
if [ -z $VERSION ]; then
|
if [ -z $VERSION ]; then
|
||||||
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
|
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_amd64.tar.gz"
|
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_${ARCH}.tar.gz"
|
||||||
curl -sL ${BIN_URL} -o /tmp/flux.tar.gz
|
curl -sL ${BIN_URL} -o /tmp/flux.tar.gz
|
||||||
mkdir -p /tmp/flux
|
mkdir -p /tmp/flux
|
||||||
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz
|
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz
|
||||||
- name: "Add flux binary to /usr/local/bin"
|
- name: "Copy Flux binary to execute location"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo cp /tmp/flux/flux /usr/local/bin
|
BINDIR=${{ inputs.bindir }}
|
||||||
|
if [ -z $BINDIR ]; then
|
||||||
|
sudo cp /tmp/flux/flux /usr/local/bin
|
||||||
|
else
|
||||||
|
cp /tmp/flux/flux "${BINDIR}"
|
||||||
|
echo "${BINDIR}" >> $GITHUB_PATH
|
||||||
|
fi
|
||||||
- name: "Cleanup tmp"
|
- name: "Cleanup tmp"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func (a alertAdapter) asClientObject() client.Object {
|
|||||||
return a.Alert
|
return a.Alert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a alertAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.Alert.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// notificationv1.Alert
|
// notificationv1.Alert
|
||||||
|
|
||||||
type alertListAdapter struct {
|
type alertListAdapter struct {
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func (a alertProviderAdapter) asClientObject() client.Object {
|
|||||||
return a.Provider
|
return a.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a alertProviderAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.Provider.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// notificationv1.Provider
|
// notificationv1.Provider
|
||||||
|
|
||||||
type alertProviderListAdapter struct {
|
type alertProviderListAdapter struct {
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
@@ -67,6 +68,10 @@ type bootstrapFlags struct {
|
|||||||
authorName string
|
authorName string
|
||||||
authorEmail string
|
authorEmail string
|
||||||
|
|
||||||
|
gpgKeyRingPath string
|
||||||
|
gpgPassphrase string
|
||||||
|
gpgKeyID string
|
||||||
|
|
||||||
commitMessageAppendix string
|
commitMessageAppendix string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +123,10 @@ func init() {
|
|||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.authorName, "author-name", "Flux", "author name for Git commits")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.authorName, "author-name", "Flux", "author name for Git commits")
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.authorEmail, "author-email", "", "author email for Git commits")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.authorEmail, "author-email", "", "author email for Git commits")
|
||||||
|
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.gpgKeyRingPath, "gpg-key-ring", "", "path to GPG key ring for signing commits")
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.gpgPassphrase, "gpg-passphrase", "", "passphrase for decrypting GPG private key")
|
||||||
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.gpgKeyID, "gpg-key-id", "", "key id for selecting a particular key")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.commitMessageAppendix, "commit-message-appendix", "", "string to add to the commit messages, e.g. '[ci skip]'")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.commitMessageAppendix, "commit-message-appendix", "", "string to add to the commit messages, e.g. '[ci skip]'")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description())
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description())
|
||||||
@@ -131,7 +140,7 @@ func NewBootstrapFlags() bootstrapFlags {
|
|||||||
return bootstrapFlags{
|
return bootstrapFlags{
|
||||||
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
|
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
|
||||||
requiredComponents: []string{"source-controller", "kustomize-controller"},
|
requiredComponents: []string{"source-controller", "kustomize-controller"},
|
||||||
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm),
|
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.ECDSAPrivateKeyAlgorithm),
|
||||||
keyRSABits: 2048,
|
keyRSABits: 2048,
|
||||||
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||||
}
|
}
|
||||||
@@ -145,7 +154,7 @@ func buildEmbeddedManifestBase() (string, error) {
|
|||||||
if !isEmbeddedVersion(bootstrapArgs.version) {
|
if !isEmbeddedVersion(bootstrapArgs.version) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
tmpBaseDir, err := ioutil.TempDir("", "flux-manifests-")
|
tmpBaseDir, err := os.MkdirTemp("", "flux-manifests-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -174,6 +183,10 @@ func mapTeamSlice(s []string, defaultPermission string) map[string]string {
|
|||||||
m := make(map[string]string, len(s))
|
m := make(map[string]string, len(s))
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
m[v] = defaultPermission
|
m[v] = defaultPermission
|
||||||
|
if s := strings.Split(v, ":"); len(s) == 2 {
|
||||||
|
m[s[0]] = s[1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|||||||
279
cmd/flux/bootstrap_bitbucket_server.go
Normal file
279
cmd/flux/bootstrap_bitbucket_server.go
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap"
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap/git/gogit"
|
||||||
|
"github.com/fluxcd/flux2/internal/bootstrap/provider"
|
||||||
|
"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/sourcesecret"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bootstrapBServerCmd = &cobra.Command{
|
||||||
|
Use: "bitbucket-server",
|
||||||
|
Short: "Bootstrap toolkit components in a Bitbucket Server repository",
|
||||||
|
Long: `The bootstrap bitbucket-server command creates the Bitbucket Server repository if it doesn't exists and
|
||||||
|
commits the toolkit components manifests to the master 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.`,
|
||||||
|
Example: ` # Create a Bitbucket Server API token and export it as an env var
|
||||||
|
export BITBUCKET_TOKEN=<my-token>
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository using HTTPS token authentication
|
||||||
|
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --hostname=<domain> --token-auth
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository using SSH authentication
|
||||||
|
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --hostname=<domain>
|
||||||
|
|
||||||
|
# Run bootstrap for a repository path
|
||||||
|
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --path=dev-cluster --hostname=<domain>
|
||||||
|
|
||||||
|
# Run bootstrap for a public repository on a personal account
|
||||||
|
flux bootstrap bitbucket-server --owner=<user> --repository=<repository name> --private=false --personal --hostname=<domain> --token-auth
|
||||||
|
|
||||||
|
# Run bootstrap for a an existing repository with a branch named main
|
||||||
|
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --branch=main --hostname=<domain> --token-auth`,
|
||||||
|
RunE: bootstrapBServerCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
bServerDefaultPermission = "push"
|
||||||
|
bServerTokenEnvVar = "BITBUCKET_TOKEN"
|
||||||
|
)
|
||||||
|
|
||||||
|
type bServerFlags struct {
|
||||||
|
owner string
|
||||||
|
repository string
|
||||||
|
interval time.Duration
|
||||||
|
personal bool
|
||||||
|
username string
|
||||||
|
private bool
|
||||||
|
hostname string
|
||||||
|
path flags.SafeRelativePath
|
||||||
|
teams []string
|
||||||
|
readWriteKey bool
|
||||||
|
reconcile bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var bServerArgs bServerFlags
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bootstrapBServerCmd.Flags().StringVar(&bServerArgs.owner, "owner", "", "Bitbucket Server user or project name")
|
||||||
|
bootstrapBServerCmd.Flags().StringVar(&bServerArgs.repository, "repository", "", "Bitbucket Server repository name")
|
||||||
|
bootstrapBServerCmd.Flags().StringSliceVar(&bServerArgs.teams, "group", []string{}, "Bitbucket Server groups to be given write access (also accepts comma-separated values)")
|
||||||
|
bootstrapBServerCmd.Flags().BoolVar(&bServerArgs.personal, "personal", false, "if true, the owner is assumed to be a Bitbucket Server user; otherwise a group")
|
||||||
|
bootstrapBServerCmd.Flags().StringVarP(&bServerArgs.username, "username", "u", "git", "authentication username")
|
||||||
|
bootstrapBServerCmd.Flags().BoolVar(&bServerArgs.private, "private", true, "if true, the repository is setup or configured as private")
|
||||||
|
bootstrapBServerCmd.Flags().DurationVar(&bServerArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
|
bootstrapBServerCmd.Flags().StringVar(&bServerArgs.hostname, "hostname", "", "Bitbucket Server hostname")
|
||||||
|
bootstrapBServerCmd.Flags().Var(&bServerArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
|
bootstrapBServerCmd.Flags().BoolVar(&bServerArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions")
|
||||||
|
bootstrapBServerCmd.Flags().BoolVar(&bServerArgs.reconcile, "reconcile", false, "if true, the configured options are also reconciled if the repository already exists")
|
||||||
|
|
||||||
|
bootstrapCmd.AddCommand(bootstrapBServerCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
bitbucketToken := os.Getenv(bServerTokenEnvVar)
|
||||||
|
if bitbucketToken == "" {
|
||||||
|
var err error
|
||||||
|
bitbucketToken, err = readPasswordFromStdin("Please enter your Bitbucket personal access token (PAT): ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read token: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bServerArgs.hostname == "" {
|
||||||
|
return fmt.Errorf("invalid hostname %q", bServerArgs.hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bootstrapValidate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manifest base
|
||||||
|
if ver, err := getVersion(bootstrapArgs.version); err == nil {
|
||||||
|
bootstrapArgs.version = ver
|
||||||
|
}
|
||||||
|
manifestsBase, err := buildEmbeddedManifestBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
|
user := bServerArgs.username
|
||||||
|
if bServerArgs.personal {
|
||||||
|
user = bServerArgs.owner
|
||||||
|
}
|
||||||
|
|
||||||
|
var caBundle []byte
|
||||||
|
if bootstrapArgs.caFile != "" {
|
||||||
|
var err error
|
||||||
|
caBundle, err = os.ReadFile(bootstrapArgs.caFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Bitbucket Server provider
|
||||||
|
providerCfg := provider.Config{
|
||||||
|
Provider: provider.GitProviderStash,
|
||||||
|
Hostname: bServerArgs.hostname,
|
||||||
|
Username: user,
|
||||||
|
Token: bitbucketToken,
|
||||||
|
CaBundle: caBundle,
|
||||||
|
}
|
||||||
|
|
||||||
|
providerClient, err := provider.BuildGitProvider(providerCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazy go-git repository
|
||||||
|
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
gitClient := gogit.New(tmpDir, &http.BasicAuth{
|
||||||
|
Username: user,
|
||||||
|
Password: bitbucketToken,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Install manifest config
|
||||||
|
installOptions := install.Options{
|
||||||
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
|
Version: bootstrapArgs.version,
|
||||||
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
|
Components: bootstrapComponents(),
|
||||||
|
Registry: bootstrapArgs.registry,
|
||||||
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
|
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||||
|
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||||
|
LogLevel: bootstrapArgs.logLevel.String(),
|
||||||
|
NotificationController: rootArgs.defaults.NotificationController,
|
||||||
|
ManifestFile: rootArgs.defaults.ManifestFile,
|
||||||
|
Timeout: rootArgs.timeout,
|
||||||
|
TargetPath: bServerArgs.path.ToSlash(),
|
||||||
|
ClusterDomain: bootstrapArgs.clusterDomain,
|
||||||
|
TolerationKeys: bootstrapArgs.tolerationKeys,
|
||||||
|
}
|
||||||
|
if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
|
||||||
|
installOptions.BaseURL = customBaseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source generation and secret config
|
||||||
|
secretOpts := sourcesecret.Options{
|
||||||
|
Name: bootstrapArgs.secretName,
|
||||||
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
|
TargetPath: bServerArgs.path.String(),
|
||||||
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
|
}
|
||||||
|
if bootstrapArgs.tokenAuth {
|
||||||
|
if bServerArgs.personal {
|
||||||
|
secretOpts.Username = bServerArgs.owner
|
||||||
|
} else {
|
||||||
|
secretOpts.Username = bServerArgs.username
|
||||||
|
}
|
||||||
|
secretOpts.Password = bitbucketToken
|
||||||
|
|
||||||
|
if bootstrapArgs.caFile != "" {
|
||||||
|
secretOpts.CAFilePath = bootstrapArgs.caFile
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
secretOpts.SSHHostname = bServerArgs.hostname
|
||||||
|
|
||||||
|
if bootstrapArgs.privateKeyFile != "" {
|
||||||
|
secretOpts.PrivateKeyPath = bootstrapArgs.privateKeyFile
|
||||||
|
}
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
|
secretOpts.SSHHostname = bootstrapArgs.sshHostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync manifest config
|
||||||
|
syncOpts := sync.Options{
|
||||||
|
Interval: bServerArgs.interval,
|
||||||
|
Name: *kubeconfigArgs.Namespace,
|
||||||
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
|
Branch: bootstrapArgs.branch,
|
||||||
|
Secret: bootstrapArgs.secretName,
|
||||||
|
TargetPath: bServerArgs.path.ToSlash(),
|
||||||
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
|
GitImplementation: sourceGitArgs.gitImplementation.String(),
|
||||||
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrap config
|
||||||
|
bootstrapOpts := []bootstrap.GitProviderOption{
|
||||||
|
bootstrap.WithProviderRepository(bServerArgs.owner, bServerArgs.repository, bServerArgs.personal),
|
||||||
|
bootstrap.WithBranch(bootstrapArgs.branch),
|
||||||
|
bootstrap.WithBootstrapTransportType("https"),
|
||||||
|
bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail),
|
||||||
|
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
||||||
|
bootstrap.WithProviderTeamPermissions(mapTeamSlice(bServerArgs.teams, bServerDefaultPermission)),
|
||||||
|
bootstrap.WithReadWriteKeyPermissions(bServerArgs.readWriteKey),
|
||||||
|
bootstrap.WithKubeconfig(kubeconfigArgs),
|
||||||
|
bootstrap.WithLogger(logger),
|
||||||
|
bootstrap.WithCABundle(caBundle),
|
||||||
|
}
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||||
|
}
|
||||||
|
if bootstrapArgs.tokenAuth {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||||
|
}
|
||||||
|
if !bServerArgs.private {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||||
|
}
|
||||||
|
if bServerArgs.reconcile {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithReconcile())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup bootstrapper with constructed configs
|
||||||
|
b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run
|
||||||
|
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -69,6 +68,7 @@ type gitFlags struct {
|
|||||||
path flags.SafeRelativePath
|
path flags.SafeRelativePath
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
|
silent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitArgs gitFlags
|
var gitArgs gitFlags
|
||||||
@@ -79,6 +79,7 @@ func init() {
|
|||||||
bootstrapGitCmd.Flags().Var(&gitArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
bootstrapGitCmd.Flags().Var(&gitArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
bootstrapGitCmd.Flags().StringVarP(&gitArgs.username, "username", "u", "git", "basic authentication username")
|
bootstrapGitCmd.Flags().StringVarP(&gitArgs.username, "username", "u", "git", "basic authentication username")
|
||||||
bootstrapGitCmd.Flags().StringVarP(&gitArgs.password, "password", "p", "", "basic authentication password")
|
bootstrapGitCmd.Flags().StringVarP(&gitArgs.password, "password", "p", "", "basic authentication password")
|
||||||
|
bootstrapGitCmd.Flags().BoolVarP(&gitArgs.silent, "silent", "s", false, "assumes the deploy key is already setup, skips confirmation")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitCmd)
|
bootstrapCmd.AddCommand(bootstrapGitCmd)
|
||||||
}
|
}
|
||||||
@@ -100,7 +101,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -116,7 +117,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
defer os.RemoveAll(manifestsBase)
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
// Lazy go-git repository
|
// Lazy go-git repository
|
||||||
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
@@ -127,7 +128,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
installOptions := install.Options{
|
installOptions := install.Options{
|
||||||
BaseURL: rootArgs.defaults.BaseURL,
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
Version: bootstrapArgs.version,
|
Version: bootstrapArgs.version,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Components: bootstrapComponents(),
|
Components: bootstrapComponents(),
|
||||||
Registry: bootstrapArgs.registry,
|
Registry: bootstrapArgs.registry,
|
||||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
@@ -148,7 +149,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
// Source generation and secret config
|
// Source generation and secret config
|
||||||
secretOpts := sourcesecret.Options{
|
secretOpts := sourcesecret.Options{
|
||||||
Name: bootstrapArgs.secretName,
|
Name: bootstrapArgs.secretName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
TargetPath: gitArgs.path.String(),
|
TargetPath: gitArgs.path.String(),
|
||||||
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
}
|
}
|
||||||
@@ -160,20 +161,30 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
secretOpts.CAFilePath = bootstrapArgs.caFile
|
secretOpts.CAFilePath = bootstrapArgs.caFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove port of the given host when not syncing over HTTP/S to not assume port for protocol
|
||||||
|
// This _might_ be overwritten later on by e.g. --ssh-hostname
|
||||||
|
if repositoryURL.Scheme != "https" && repositoryURL.Scheme != "http" {
|
||||||
|
repositoryURL.Host = repositoryURL.Hostname()
|
||||||
|
}
|
||||||
|
|
||||||
// Configure repository URL to match auth config for sync.
|
// Configure repository URL to match auth config for sync.
|
||||||
repositoryURL.User = nil
|
repositoryURL.User = nil
|
||||||
repositoryURL.Scheme = "https"
|
repositoryURL.Scheme = "https"
|
||||||
repositoryURL.Host = repositoryURL.Hostname()
|
|
||||||
} else {
|
} else {
|
||||||
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
secretOpts.Password = gitArgs.password
|
secretOpts.Password = gitArgs.password
|
||||||
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
|
||||||
// Configure repository URL to match auth config for sync.
|
// Configure repository URL to match auth config for sync
|
||||||
repositoryURL.User = url.User(gitArgs.username)
|
|
||||||
|
// Override existing user when user is not already set
|
||||||
|
// or when a username was passed in
|
||||||
|
if repositoryURL.User == nil || gitArgs.username != "git" {
|
||||||
|
repositoryURL.User = url.User(gitArgs.username)
|
||||||
|
}
|
||||||
|
|
||||||
repositoryURL.Scheme = "ssh"
|
repositoryURL.Scheme = "ssh"
|
||||||
repositoryURL.Host = repositoryURL.Hostname()
|
|
||||||
if bootstrapArgs.sshHostname != "" {
|
if bootstrapArgs.sshHostname != "" {
|
||||||
repositoryURL.Host = bootstrapArgs.sshHostname
|
repositoryURL.Host = bootstrapArgs.sshHostname
|
||||||
}
|
}
|
||||||
@@ -188,8 +199,8 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
// Sync manifest config
|
// Sync manifest config
|
||||||
syncOpts := sync.Options{
|
syncOpts := sync.Options{
|
||||||
Interval: gitArgs.interval,
|
Interval: gitArgs.interval,
|
||||||
Name: rootArgs.namespace,
|
Name: *kubeconfigArgs.Namespace,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
URL: repositoryURL.String(),
|
URL: repositoryURL.String(),
|
||||||
Branch: bootstrapArgs.branch,
|
Branch: bootstrapArgs.branch,
|
||||||
Secret: bootstrapArgs.secretName,
|
Secret: bootstrapArgs.secretName,
|
||||||
@@ -199,15 +210,26 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var caBundle []byte
|
||||||
|
if bootstrapArgs.caFile != "" {
|
||||||
|
var err error
|
||||||
|
caBundle, err = os.ReadFile(bootstrapArgs.caFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Bootstrap config
|
// Bootstrap config
|
||||||
bootstrapOpts := []bootstrap.GitOption{
|
bootstrapOpts := []bootstrap.GitOption{
|
||||||
bootstrap.WithRepositoryURL(gitArgs.url),
|
bootstrap.WithRepositoryURL(gitArgs.url),
|
||||||
bootstrap.WithBranch(bootstrapArgs.branch),
|
bootstrap.WithBranch(bootstrapArgs.branch),
|
||||||
bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail),
|
bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail),
|
||||||
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
||||||
bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
|
bootstrap.WithKubeconfig(kubeconfigArgs),
|
||||||
bootstrap.WithPostGenerateSecretFunc(promptPublicKey),
|
bootstrap.WithPostGenerateSecretFunc(promptPublicKey),
|
||||||
bootstrap.WithLogger(logger),
|
bootstrap.WithLogger(logger),
|
||||||
|
bootstrap.WithCABundle(caBundle),
|
||||||
|
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup bootstrapper with constructed configs
|
// Setup bootstrapper with constructed configs
|
||||||
@@ -248,13 +270,16 @@ func promptPublicKey(ctx context.Context, secret corev1.Secret, _ sourcesecret.O
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Successf("public key: %s", strings.TrimSpace(ppk))
|
logger.Successf("public key: %s", strings.TrimSpace(ppk))
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Please give the key access to your repository",
|
if !gitArgs.silent {
|
||||||
IsConfirm: true,
|
prompt := promptui.Prompt{
|
||||||
}
|
Label: "Please give the key access to your repository",
|
||||||
_, err := prompt.Run()
|
IsConfirm: true,
|
||||||
if err != nil {
|
}
|
||||||
return fmt.Errorf("aborting")
|
_, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("aborting")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -53,6 +52,9 @@ the bootstrap command will perform an upgrade if needed.`,
|
|||||||
# Run bootstrap for a private repository and assign organization teams to it
|
# Run bootstrap for a private repository and assign organization teams to it
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug> --team=<team2 slug>
|
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug> --team=<team2 slug>
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository and assign organization teams with their access level(e.g maintain, admin) to it
|
||||||
|
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug>:<access-level>
|
||||||
|
|
||||||
# Run bootstrap for a repository path
|
# Run bootstrap for a repository path
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name> --path=dev-cluster
|
flux bootstrap github --owner=<organization> --repository=<repository name> --path=dev-cluster
|
||||||
|
|
||||||
@@ -94,7 +96,7 @@ var githubArgs githubFlags
|
|||||||
func init() {
|
func init() {
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository name")
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository name")
|
||||||
bootstrapGitHubCmd.Flags().StringSliceVar(&githubArgs.teams, "team", []string{}, "GitHub team to be given maintainer access (also accepts comma-separated values)")
|
bootstrapGitHubCmd.Flags().StringSliceVar(&githubArgs.teams, "team", []string{}, "GitHub team and the access to be given to it(team:maintain). Defaults to maintainer access if no access level is specified (also accepts comma-separated values)")
|
||||||
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "if true, the owner is assumed to be a GitHub user; otherwise an org")
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "if true, the owner is assumed to be a GitHub user; otherwise an org")
|
||||||
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "if true, the repository is setup or configured as private")
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "if true, the repository is setup or configured as private")
|
||||||
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
|
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
@@ -109,7 +111,11 @@ func init() {
|
|||||||
func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
ghToken := os.Getenv(ghTokenEnvVar)
|
ghToken := os.Getenv(ghTokenEnvVar)
|
||||||
if ghToken == "" {
|
if ghToken == "" {
|
||||||
return fmt.Errorf("%s environment variable not found", ghTokenEnvVar)
|
var err error
|
||||||
|
ghToken, err = readPasswordFromStdin("Please enter your GitHub personal access token (PAT): ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read token: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bootstrapValidate(); err != nil {
|
if err := bootstrapValidate(); err != nil {
|
||||||
@@ -119,7 +125,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -134,11 +140,20 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(manifestsBase)
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
|
var caBundle []byte
|
||||||
|
if bootstrapArgs.caFile != "" {
|
||||||
|
var err error
|
||||||
|
caBundle, err = os.ReadFile(bootstrapArgs.caFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Build GitHub provider
|
// Build GitHub provider
|
||||||
providerCfg := provider.Config{
|
providerCfg := provider.Config{
|
||||||
Provider: provider.GitProviderGitHub,
|
Provider: provider.GitProviderGitHub,
|
||||||
Hostname: githubArgs.hostname,
|
Hostname: githubArgs.hostname,
|
||||||
Token: ghToken,
|
Token: ghToken,
|
||||||
|
CaBundle: caBundle,
|
||||||
}
|
}
|
||||||
providerClient, err := provider.BuildGitProvider(providerCfg)
|
providerClient, err := provider.BuildGitProvider(providerCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -146,7 +161,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lazy go-git repository
|
// Lazy go-git repository
|
||||||
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
@@ -160,7 +175,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
installOptions := install.Options{
|
installOptions := install.Options{
|
||||||
BaseURL: rootArgs.defaults.BaseURL,
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
Version: bootstrapArgs.version,
|
Version: bootstrapArgs.version,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Components: bootstrapComponents(),
|
Components: bootstrapComponents(),
|
||||||
Registry: bootstrapArgs.registry,
|
Registry: bootstrapArgs.registry,
|
||||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
@@ -181,7 +196,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
// Source generation and secret config
|
// Source generation and secret config
|
||||||
secretOpts := sourcesecret.Options{
|
secretOpts := sourcesecret.Options{
|
||||||
Name: bootstrapArgs.secretName,
|
Name: bootstrapArgs.secretName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
TargetPath: githubArgs.path.ToSlash(),
|
TargetPath: githubArgs.path.ToSlash(),
|
||||||
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
}
|
}
|
||||||
@@ -206,8 +221,8 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
// Sync manifest config
|
// Sync manifest config
|
||||||
syncOpts := sync.Options{
|
syncOpts := sync.Options{
|
||||||
Interval: githubArgs.interval,
|
Interval: githubArgs.interval,
|
||||||
Name: rootArgs.namespace,
|
Name: *kubeconfigArgs.Namespace,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Branch: bootstrapArgs.branch,
|
Branch: bootstrapArgs.branch,
|
||||||
Secret: bootstrapArgs.secretName,
|
Secret: bootstrapArgs.secretName,
|
||||||
TargetPath: githubArgs.path.ToSlash(),
|
TargetPath: githubArgs.path.ToSlash(),
|
||||||
@@ -225,8 +240,9 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
||||||
bootstrap.WithProviderTeamPermissions(mapTeamSlice(githubArgs.teams, ghDefaultPermission)),
|
bootstrap.WithProviderTeamPermissions(mapTeamSlice(githubArgs.teams, ghDefaultPermission)),
|
||||||
bootstrap.WithReadWriteKeyPermissions(githubArgs.readWriteKey),
|
bootstrap.WithReadWriteKeyPermissions(githubArgs.readWriteKey),
|
||||||
bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
|
bootstrap.WithKubeconfig(kubeconfigArgs),
|
||||||
bootstrap.WithLogger(logger),
|
bootstrap.WithLogger(logger),
|
||||||
|
bootstrap.WithCABundle(caBundle),
|
||||||
}
|
}
|
||||||
if bootstrapArgs.sshHostname != "" {
|
if bootstrapArgs.sshHostname != "" {
|
||||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -109,7 +108,11 @@ func init() {
|
|||||||
func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
glToken := os.Getenv(glTokenEnvVar)
|
glToken := os.Getenv(glTokenEnvVar)
|
||||||
if glToken == "" {
|
if glToken == "" {
|
||||||
return fmt.Errorf("%s environment variable not found", glTokenEnvVar)
|
var err error
|
||||||
|
glToken, err = readPasswordFromStdin("Please enter your GitLab personal access token (PAT): ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read token: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository); err != nil || !projectNameIsValid {
|
if projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository); err != nil || !projectNameIsValid {
|
||||||
@@ -126,7 +129,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -141,11 +144,21 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(manifestsBase)
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
|
var caBundle []byte
|
||||||
|
if bootstrapArgs.caFile != "" {
|
||||||
|
var err error
|
||||||
|
caBundle, err = os.ReadFile(bootstrapArgs.caFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build GitLab provider
|
// Build GitLab provider
|
||||||
providerCfg := provider.Config{
|
providerCfg := provider.Config{
|
||||||
Provider: provider.GitProviderGitLab,
|
Provider: provider.GitProviderGitLab,
|
||||||
Hostname: gitlabArgs.hostname,
|
Hostname: gitlabArgs.hostname,
|
||||||
Token: glToken,
|
Token: glToken,
|
||||||
|
CaBundle: caBundle,
|
||||||
}
|
}
|
||||||
// Workaround for: https://github.com/fluxcd/go-git-providers/issues/55
|
// Workaround for: https://github.com/fluxcd/go-git-providers/issues/55
|
||||||
if hostname := providerCfg.Hostname; hostname != glDefaultDomain &&
|
if hostname := providerCfg.Hostname; hostname != glDefaultDomain &&
|
||||||
@@ -159,7 +172,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lazy go-git repository
|
// Lazy go-git repository
|
||||||
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
@@ -173,7 +186,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
installOptions := install.Options{
|
installOptions := install.Options{
|
||||||
BaseURL: rootArgs.defaults.BaseURL,
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
Version: bootstrapArgs.version,
|
Version: bootstrapArgs.version,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Components: bootstrapComponents(),
|
Components: bootstrapComponents(),
|
||||||
Registry: bootstrapArgs.registry,
|
Registry: bootstrapArgs.registry,
|
||||||
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
@@ -194,7 +207,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
// Source generation and secret config
|
// Source generation and secret config
|
||||||
secretOpts := sourcesecret.Options{
|
secretOpts := sourcesecret.Options{
|
||||||
Name: bootstrapArgs.secretName,
|
Name: bootstrapArgs.secretName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
TargetPath: gitlabArgs.path.String(),
|
TargetPath: gitlabArgs.path.String(),
|
||||||
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
}
|
}
|
||||||
@@ -222,8 +235,8 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
// Sync manifest config
|
// Sync manifest config
|
||||||
syncOpts := sync.Options{
|
syncOpts := sync.Options{
|
||||||
Interval: gitlabArgs.interval,
|
Interval: gitlabArgs.interval,
|
||||||
Name: rootArgs.namespace,
|
Name: *kubeconfigArgs.Namespace,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Branch: bootstrapArgs.branch,
|
Branch: bootstrapArgs.branch,
|
||||||
Secret: bootstrapArgs.secretName,
|
Secret: bootstrapArgs.secretName,
|
||||||
TargetPath: gitlabArgs.path.ToSlash(),
|
TargetPath: gitlabArgs.path.ToSlash(),
|
||||||
@@ -241,8 +254,9 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
||||||
bootstrap.WithProviderTeamPermissions(mapTeamSlice(gitlabArgs.teams, glDefaultPermission)),
|
bootstrap.WithProviderTeamPermissions(mapTeamSlice(gitlabArgs.teams, glDefaultPermission)),
|
||||||
bootstrap.WithReadWriteKeyPermissions(gitlabArgs.readWriteKey),
|
bootstrap.WithReadWriteKeyPermissions(gitlabArgs.readWriteKey),
|
||||||
bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
|
bootstrap.WithKubeconfig(kubeconfigArgs),
|
||||||
bootstrap.WithLogger(logger),
|
bootstrap.WithLogger(logger),
|
||||||
|
bootstrap.WithCABundle(caBundle),
|
||||||
}
|
}
|
||||||
if bootstrapArgs.sshHostname != "" {
|
if bootstrapArgs.sshHostname != "" {
|
||||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||||
|
|||||||
@@ -18,21 +18,19 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
v1 "k8s.io/api/apps/v1"
|
v1 "k8s.io/api/apps/v1"
|
||||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/version"
|
"github.com/fluxcd/pkg/version"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||||
"github.com/fluxcd/flux2/pkg/status"
|
"github.com/fluxcd/flux2/pkg/status"
|
||||||
)
|
)
|
||||||
@@ -54,10 +52,14 @@ type checkFlags struct {
|
|||||||
pre bool
|
pre bool
|
||||||
components []string
|
components []string
|
||||||
extraComponents []string
|
extraComponents []string
|
||||||
|
pollInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type kubectlVersion struct {
|
var kubernetesConstraints = []string{
|
||||||
ClientVersion *apimachineryversion.Info `json:"clientVersion"`
|
">=1.19.0-0",
|
||||||
|
">=1.16.11-0 <=1.16.15-0",
|
||||||
|
">=1.17.7-0 <=1.17.17-0",
|
||||||
|
">=1.18.4-0 <=1.18.20-0",
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkArgs checkFlags
|
var checkArgs checkFlags
|
||||||
@@ -69,23 +71,18 @@ func init() {
|
|||||||
"list of components, accepts comma-separated values")
|
"list of components, accepts comma-separated values")
|
||||||
checkCmd.Flags().StringSliceVar(&checkArgs.extraComponents, "components-extra", nil,
|
checkCmd.Flags().StringSliceVar(&checkArgs.extraComponents, "components-extra", nil,
|
||||||
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
||||||
|
checkCmd.Flags().DurationVar(&checkArgs.pollInterval, "poll-interval", 5*time.Second,
|
||||||
|
"how often the health checker should poll the cluster for the latest state of the resources.")
|
||||||
rootCmd.AddCommand(checkCmd)
|
rootCmd.AddCommand(checkCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCheckCmd(cmd *cobra.Command, args []string) error {
|
func runCheckCmd(cmd *cobra.Command, args []string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
logger.Actionf("checking prerequisites")
|
logger.Actionf("checking prerequisites")
|
||||||
checkFailed := false
|
checkFailed := false
|
||||||
|
|
||||||
fluxCheck()
|
fluxCheck()
|
||||||
|
|
||||||
if !kubectlCheck(ctx, ">=1.18.0-0") {
|
if !kubernetesCheck(kubernetesConstraints) {
|
||||||
checkFailed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !kubernetesCheck(">=1.16.0-0") {
|
|
||||||
checkFailed = true
|
checkFailed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,44 +127,8 @@ func fluxCheck() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func kubectlCheck(ctx context.Context, constraint string) bool {
|
func kubernetesCheck(constraints []string) bool {
|
||||||
_, err := exec.LookPath("kubectl")
|
cfg, err := utils.KubeConfig(kubeconfigArgs)
|
||||||
if err != nil {
|
|
||||||
logger.Failuref("kubectl not found")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
kubectlArgs := []string{"version", "--client", "--output", "json"}
|
|
||||||
output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
|
|
||||||
if err != nil {
|
|
||||||
logger.Failuref("kubectl version can't be determined")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
kv := &kubectlVersion{}
|
|
||||||
if err = json.Unmarshal([]byte(output), kv); err != nil {
|
|
||||||
logger.Failuref("kubectl version output can't be unmarshalled")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := version.ParseVersion(kv.ClientVersion.GitVersion)
|
|
||||||
if err != nil {
|
|
||||||
logger.Failuref("kubectl version can't be parsed")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
c, _ := semver.NewConstraint(constraint)
|
|
||||||
if !c.Check(v) {
|
|
||||||
logger.Failuref("kubectl version %s < %s", v.Original(), constraint)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Successf("kubectl %s %s", v.String(), constraint)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func kubernetesCheck(constraint string) bool {
|
|
||||||
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||||
return false
|
return false
|
||||||
@@ -191,13 +152,23 @@ func kubernetesCheck(constraint string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
c, _ := semver.NewConstraint(constraint)
|
var valid bool
|
||||||
if !c.Check(v) {
|
var vrange string
|
||||||
logger.Failuref("Kubernetes version %s < %s", v.Original(), constraint)
|
for _, constraint := range constraints {
|
||||||
|
c, _ := semver.NewConstraint(constraint)
|
||||||
|
if c.Check(v) {
|
||||||
|
valid = true
|
||||||
|
vrange = constraint
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
logger.Failuref("Kubernetes version %s does not match %s", v.Original(), constraints[0])
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Successf("Kubernetes %s %s", v.String(), constraint)
|
logger.Successf("Kubernetes %s %s", v.String(), vrange)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,25 +176,25 @@ func componentsCheck() bool {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeConfig, err := utils.KubeConfig(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
statusChecker, err := status.NewStatusChecker(kubeConfig, time.Second, rootArgs.timeout, logger)
|
statusChecker, err := status.NewStatusChecker(kubeConfig, checkArgs.pollInterval, rootArgs.timeout, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
ok := true
|
ok := true
|
||||||
selector := client.MatchingLabels{"app.kubernetes.io/instance": rootArgs.namespace}
|
selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
|
||||||
var list v1.DeploymentList
|
var list v1.DeploymentList
|
||||||
if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace), selector); err == nil {
|
if err := kubeClient.List(ctx, &list, client.InNamespace(*kubeconfigArgs.Namespace), selector); err == nil {
|
||||||
for _, d := range list.Items {
|
for _, d := range list.Items {
|
||||||
if ref, err := buildComponentObjectRefs(d.Name); err == nil {
|
if ref, err := buildComponentObjectRefs(d.Name); err == nil {
|
||||||
if err := statusChecker.Assess(ref...); err != nil {
|
if err := statusChecker.Assess(ref...); err != nil {
|
||||||
|
|||||||
52
cmd/flux/check_test.go
Normal file
52
cmd/flux/check_test.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
//go:build e2e
|
||||||
|
// +build e2e
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
"k8s.io/apimachinery/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckPre(t *testing.T) {
|
||||||
|
jsonOutput, err := utils.ExecKubectlCommand(context.TODO(), utils.ModeCapture, *kubeconfigArgs.KubeConfig, *kubeconfigArgs.Context, "version", "--output", "json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error running utils.ExecKubectlCommand: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var versions map[string]version.Info
|
||||||
|
if err := json.Unmarshal([]byte(jsonOutput), &versions); err != nil {
|
||||||
|
t.Fatalf("Error unmarshalling: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
serverVersion := strings.TrimPrefix(versions["serverVersion"].GitVersion, "v")
|
||||||
|
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: "check --pre",
|
||||||
|
assert: assertGoldenTemplateFile("testdata/check/check_pre.golden", map[string]string{
|
||||||
|
"serverVersion": serverVersion,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
}
|
||||||
@@ -17,7 +17,15 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/client-go/dynamic"
|
||||||
)
|
)
|
||||||
|
|
||||||
var completionCmd = &cobra.Command{
|
var completionCmd = &cobra.Command{
|
||||||
@@ -29,3 +37,76 @@ var completionCmd = &cobra.Command{
|
|||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(completionCmd)
|
rootCmd.AddCommand(completionCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func contextsCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
rawConfig, err := kubeconfigArgs.ToRawKubeConfigLoader().RawConfig()
|
||||||
|
if err != nil {
|
||||||
|
return completionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var comps []string
|
||||||
|
|
||||||
|
for name := range rawConfig.Contexts {
|
||||||
|
if strings.HasPrefix(name, toComplete) {
|
||||||
|
comps = append(comps, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return comps, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceNamesCompletionFunc(gvk schema.GroupVersionKind) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfg, err := utils.KubeConfig(kubeconfigArgs)
|
||||||
|
if err != nil {
|
||||||
|
return completionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mapper, err := kubeconfigArgs.ToRESTMapper()
|
||||||
|
if err != nil {
|
||||||
|
return completionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||||
|
if err != nil {
|
||||||
|
return completionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := dynamic.NewForConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return completionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dr dynamic.ResourceInterface
|
||||||
|
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
|
||||||
|
dr = client.Resource(mapping.Resource).Namespace(*kubeconfigArgs.Namespace)
|
||||||
|
} else {
|
||||||
|
dr = client.Resource(mapping.Resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := dr.List(ctx, metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return completionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var comps []string
|
||||||
|
|
||||||
|
for _, item := range list.Items {
|
||||||
|
name := item.GetName()
|
||||||
|
|
||||||
|
if strings.HasPrefix(name, toComplete) {
|
||||||
|
comps = append(comps, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return comps, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func completionError(err error) ([]string, cobra.ShellCompDirective) {
|
||||||
|
cobra.CompError(err.Error())
|
||||||
|
return nil, cobra.ShellCompDirectiveError
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -27,12 +28,12 @@ var completionZshCmd = &cobra.Command{
|
|||||||
Short: "Generates zsh completion scripts",
|
Short: "Generates zsh completion scripts",
|
||||||
Example: `To load completion run
|
Example: `To load completion run
|
||||||
|
|
||||||
. <(flux completion zsh) && compdef _flux flux
|
. <(flux completion zsh)
|
||||||
|
|
||||||
To configure your zsh shell to load completions for each session add to your zshrc
|
To configure your zsh shell to load completions for each session add to your zshrc
|
||||||
|
|
||||||
# ~/.zshrc or ~/.profile
|
# ~/.zshrc or ~/.profile
|
||||||
command -v flux >/dev/null && . <(flux completion zsh) && compdef _flux flux
|
command -v flux >/dev/null && . <(flux completion zsh)
|
||||||
|
|
||||||
or write a cached file in one of the completion directories in your ${fpath}:
|
or write a cached file in one of the completion directories in your ${fpath}:
|
||||||
|
|
||||||
@@ -43,6 +44,8 @@ mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
|
|||||||
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto`,
|
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
rootCmd.GenZshCompletion(os.Stdout)
|
rootCmd.GenZshCompletion(os.Stdout)
|
||||||
|
// Cobra doesn't source zsh completion file, explicitly doing it here
|
||||||
|
fmt.Println("compdef _flux flux")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) // NB globals
|
kubeClient, err := utils.KubeClient(kubeconfigArgs) // NB globals
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
alert := notificationv1.Alert{
|
alert := notificationv1.Alert{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: notificationv1.AlertSpec{
|
Spec: notificationv1.AlertSpec{
|
||||||
@@ -122,7 +122,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
provider := notificationv1.Provider{
|
provider := notificationv1.Provider{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: notificationv1.ProviderSpec{
|
Spec: notificationv1.ProviderSpec{
|
||||||
@@ -118,7 +118,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
@@ -87,7 +87,8 @@ var createHelmReleaseCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Create a HelmRelease targeting another namespace than the resource
|
# Create a HelmRelease targeting another namespace than the resource
|
||||||
flux create hr podinfo \
|
flux create hr podinfo \
|
||||||
--target-namespace=default \
|
--target-namespace=test \
|
||||||
|
--create-target-namespace=true \
|
||||||
--source=HelmRepository/podinfo \
|
--source=HelmRepository/podinfo \
|
||||||
--chart=podinfo
|
--chart=podinfo
|
||||||
|
|
||||||
@@ -113,6 +114,7 @@ type helmReleaseFlags struct {
|
|||||||
chart string
|
chart string
|
||||||
chartVersion string
|
chartVersion string
|
||||||
targetNamespace string
|
targetNamespace string
|
||||||
|
createNamespace bool
|
||||||
valuesFiles []string
|
valuesFiles []string
|
||||||
valuesFrom flags.HelmReleaseValuesFrom
|
valuesFrom flags.HelmReleaseValuesFrom
|
||||||
saName string
|
saName string
|
||||||
@@ -128,6 +130,7 @@ func init() {
|
|||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
|
||||||
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
|
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
|
||||||
|
createHelmReleaseCmd.Flags().BoolVar(&helmReleaseArgs.createNamespace, "create-target-namespace", false, "create the target namespace if it does not exist")
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
|
||||||
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.valuesFiles, "values", nil, "local path to values.yaml files, also accepts comma-separated values")
|
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.valuesFiles, "values", nil, "local path to values.yaml files, also accepts comma-separated values")
|
||||||
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
|
||||||
@@ -157,7 +160,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
helmRelease := helmv2.HelmRelease{
|
helmRelease := helmv2.HelmRelease{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: helmv2.HelmReleaseSpec{
|
Spec: helmv2.HelmReleaseSpec{
|
||||||
@@ -167,6 +170,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Duration: createArgs.interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
TargetNamespace: helmReleaseArgs.targetNamespace,
|
TargetNamespace: helmReleaseArgs.targetNamespace,
|
||||||
|
|
||||||
Chart: helmv2.HelmChartTemplate{
|
Chart: helmv2.HelmChartTemplate{
|
||||||
Spec: helmv2.HelmChartTemplateSpec{
|
Spec: helmv2.HelmChartTemplateSpec{
|
||||||
Chart: helmReleaseArgs.chart,
|
Chart: helmReleaseArgs.chart,
|
||||||
@@ -182,19 +186,31 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if helmReleaseArgs.createNamespace {
|
||||||
|
if helmRelease.Spec.Install == nil {
|
||||||
|
helmRelease.Spec.Install = &helmv2.Install{}
|
||||||
|
}
|
||||||
|
|
||||||
|
helmRelease.Spec.Install.CreateNamespace = helmReleaseArgs.createNamespace
|
||||||
|
}
|
||||||
|
|
||||||
if helmReleaseArgs.saName != "" {
|
if helmReleaseArgs.saName != "" {
|
||||||
helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
|
helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
|
||||||
}
|
}
|
||||||
|
|
||||||
if helmReleaseArgs.crds != "" {
|
if helmReleaseArgs.crds != "" {
|
||||||
helmRelease.Spec.Install = &helmv2.Install{CRDs: helmv2.Create}
|
if helmRelease.Spec.Install == nil {
|
||||||
|
helmRelease.Spec.Install = &helmv2.Install{}
|
||||||
|
}
|
||||||
|
|
||||||
|
helmRelease.Spec.Install.CRDs = helmv2.Create
|
||||||
helmRelease.Spec.Upgrade = &helmv2.Upgrade{CRDs: helmv2.CRDsPolicy(helmReleaseArgs.crds.String())}
|
helmRelease.Spec.Upgrade = &helmv2.Upgrade{CRDs: helmv2.CRDsPolicy(helmReleaseArgs.crds.String())}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(helmReleaseArgs.valuesFiles) > 0 {
|
if len(helmReleaseArgs.valuesFiles) > 0 {
|
||||||
valuesMap := make(map[string]interface{})
|
valuesMap := make(map[string]interface{})
|
||||||
for _, v := range helmReleaseArgs.valuesFiles {
|
for _, v := range helmReleaseArgs.valuesFiles {
|
||||||
data, err := ioutil.ReadFile(v)
|
data, err := os.ReadFile(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("reading values from %s failed: %w", v, err)
|
return fmt.Errorf("reading values from %s failed: %w", v, err)
|
||||||
}
|
}
|
||||||
@@ -234,7 +250,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImagePolicyCmd = &cobra.Command{
|
var createImagePolicyCmd = &cobra.Command{
|
||||||
@@ -101,11 +101,11 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
|
|||||||
var policy = imagev1.ImagePolicy{
|
var policy = imagev1.ImagePolicy{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: objectName,
|
Name: objectName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Spec: imagev1.ImagePolicySpec{
|
Spec: imagev1.ImagePolicySpec{
|
||||||
ImageRepositoryRef: meta.LocalObjectReference{
|
ImageRepositoryRef: meta.NamespacedObjectReference{
|
||||||
Name: imagePolicyArgs.imageRef,
|
Name: imagePolicyArgs.imageRef,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImageRepositoryCmd = &cobra.Command{
|
var createImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -104,7 +104,7 @@ func createImageRepositoryRun(cmd *cobra.Command, args []string) error {
|
|||||||
var repo = imagev1.ImageRepository{
|
var repo = imagev1.ImageRepository{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: objectName,
|
Name: objectName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Spec: imagev1.ImageRepositorySpec{
|
Spec: imagev1.ImageRepositorySpec{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
|
|||||||
var update = autov1.ImageUpdateAutomation{
|
var update = autov1.ImageUpdateAutomation{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: objectName,
|
Name: objectName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Spec: autov1.ImageUpdateAutomationSpec{
|
Spec: autov1.ImageUpdateAutomationSpec{
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
@@ -49,7 +49,6 @@ var createKsCmd = &cobra.Command{
|
|||||||
--path="./examples/contour/" \
|
--path="./examples/contour/" \
|
||||||
--prune=true \
|
--prune=true \
|
||||||
--interval=10m \
|
--interval=10m \
|
||||||
--validation=client \
|
|
||||||
--health-check="Deployment/contour.projectcontour" \
|
--health-check="Deployment/contour.projectcontour" \
|
||||||
--health-check="DaemonSet/envoy.projectcontour" \
|
--health-check="DaemonSet/envoy.projectcontour" \
|
||||||
--health-check-timeout=3m
|
--health-check-timeout=3m
|
||||||
@@ -60,8 +59,7 @@ var createKsCmd = &cobra.Command{
|
|||||||
--source=GitRepository/webapp \
|
--source=GitRepository/webapp \
|
||||||
--path="./deploy/overlays/dev" \
|
--path="./deploy/overlays/dev" \
|
||||||
--prune=true \
|
--prune=true \
|
||||||
--interval=5m \
|
--interval=5m
|
||||||
--validation=client
|
|
||||||
|
|
||||||
# Create a Kustomization using a source from a different namespace
|
# Create a Kustomization using a source from a different namespace
|
||||||
flux create kustomization podinfo \
|
flux create kustomization podinfo \
|
||||||
@@ -69,8 +67,7 @@ var createKsCmd = &cobra.Command{
|
|||||||
--source=GitRepository/podinfo.flux-system \
|
--source=GitRepository/podinfo.flux-system \
|
||||||
--path="./deploy/overlays/dev" \
|
--path="./deploy/overlays/dev" \
|
||||||
--prune=true \
|
--prune=true \
|
||||||
--interval=5m \
|
--interval=5m
|
||||||
--validation=client
|
|
||||||
|
|
||||||
# Create a Kustomization resource that references a Bucket
|
# Create a Kustomization resource that references a Bucket
|
||||||
flux create kustomization secrets \
|
flux create kustomization secrets \
|
||||||
@@ -92,6 +89,7 @@ type kustomizationFlags struct {
|
|||||||
decryptionProvider flags.DecryptionProvider
|
decryptionProvider flags.DecryptionProvider
|
||||||
decryptionSecret string
|
decryptionSecret string
|
||||||
targetNamespace string
|
targetNamespace string
|
||||||
|
wait bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var kustomizationArgs = NewKustomizationFlags()
|
var kustomizationArgs = NewKustomizationFlags()
|
||||||
@@ -100,6 +98,7 @@ func init() {
|
|||||||
createKsCmd.Flags().Var(&kustomizationArgs.source, "source", kustomizationArgs.source.Description())
|
createKsCmd.Flags().Var(&kustomizationArgs.source, "source", kustomizationArgs.source.Description())
|
||||||
createKsCmd.Flags().Var(&kustomizationArgs.path, "path", "path to the directory containing a kustomization.yaml file")
|
createKsCmd.Flags().Var(&kustomizationArgs.path, "path", "path to the directory containing a kustomization.yaml file")
|
||||||
createKsCmd.Flags().BoolVar(&kustomizationArgs.prune, "prune", false, "enable garbage collection")
|
createKsCmd.Flags().BoolVar(&kustomizationArgs.prune, "prune", false, "enable garbage collection")
|
||||||
|
createKsCmd.Flags().BoolVar(&kustomizationArgs.wait, "wait", false, "enable health checking of all the applied resources")
|
||||||
createKsCmd.Flags().StringSliceVar(&kustomizationArgs.healthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
createKsCmd.Flags().StringSliceVar(&kustomizationArgs.healthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
||||||
createKsCmd.Flags().DurationVar(&kustomizationArgs.healthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
createKsCmd.Flags().DurationVar(&kustomizationArgs.healthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
||||||
createKsCmd.Flags().StringVar(&kustomizationArgs.validation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.validation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
|
||||||
@@ -108,6 +107,8 @@ func init() {
|
|||||||
createKsCmd.Flags().Var(&kustomizationArgs.decryptionProvider, "decryption-provider", kustomizationArgs.decryptionProvider.Description())
|
createKsCmd.Flags().Var(&kustomizationArgs.decryptionProvider, "decryption-provider", kustomizationArgs.decryptionProvider.Description())
|
||||||
createKsCmd.Flags().StringVar(&kustomizationArgs.decryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.decryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
|
||||||
createKsCmd.Flags().StringVar(&kustomizationArgs.targetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.targetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
|
||||||
|
createKsCmd.Flags().MarkDeprecated("validation", "this arg is no longer used, all resources are validated using server-side apply dry-run")
|
||||||
|
|
||||||
createCmd.AddCommand(createKsCmd)
|
createCmd.AddCommand(createKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +143,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
kustomization := kustomizev1.Kustomization{
|
kustomization := kustomizev1.Kustomization{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: kslabels,
|
Labels: kslabels,
|
||||||
},
|
},
|
||||||
Spec: kustomizev1.KustomizationSpec{
|
Spec: kustomizev1.KustomizationSpec{
|
||||||
@@ -158,12 +159,11 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Namespace: kustomizationArgs.source.Namespace,
|
Namespace: kustomizationArgs.source.Namespace,
|
||||||
},
|
},
|
||||||
Suspend: false,
|
Suspend: false,
|
||||||
Validation: kustomizationArgs.validation,
|
|
||||||
TargetNamespace: kustomizationArgs.targetNamespace,
|
TargetNamespace: kustomizationArgs.targetNamespace,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(kustomizationArgs.healthCheck) > 0 {
|
if len(kustomizationArgs.healthCheck) > 0 && !kustomizationArgs.wait {
|
||||||
healthChecks := make([]meta.NamespacedObjectKindReference, 0)
|
healthChecks := make([]meta.NamespacedObjectKindReference, 0)
|
||||||
for _, w := range kustomizationArgs.healthCheck {
|
for _, w := range kustomizationArgs.healthCheck {
|
||||||
kindObj := strings.Split(w, "/")
|
kindObj := strings.Split(w, "/")
|
||||||
@@ -204,6 +204,13 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if kustomizationArgs.wait {
|
||||||
|
kustomization.Spec.Wait = true
|
||||||
|
kustomization.Spec.Timeout = &metav1.Duration{
|
||||||
|
Duration: kustomizationArgs.healthTimeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if kustomizationArgs.saName != "" {
|
if kustomizationArgs.saName != "" {
|
||||||
kustomization.Spec.ServiceAccountName = kustomizationArgs.saName
|
kustomization.Spec.ServiceAccountName = kustomizationArgs.saName
|
||||||
}
|
}
|
||||||
@@ -225,7 +232,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
receiver := notificationv1.Receiver{
|
receiver := notificationv1.Receiver{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: notificationv1.ReceiverSpec{
|
Spec: notificationv1.ReceiverSpec{
|
||||||
@@ -130,7 +130,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,19 +63,15 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
|
|||||||
--username=username \
|
--username=username \
|
||||||
--password=password
|
--password=password
|
||||||
|
|
||||||
# Create a Git SSH secret on disk and print the deploy key
|
# Create a Git SSH secret on disk
|
||||||
flux create secret git podinfo-auth \
|
flux create secret git podinfo-auth \
|
||||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||||
--export > podinfo-auth.yaml
|
--export > podinfo-auth.yaml
|
||||||
|
|
||||||
yq read podinfo-auth.yaml 'data."identity.pub"' | base64 --decode
|
# Print the deploy key
|
||||||
|
yq eval '.stringData."identity.pub"' podinfo-auth.yaml
|
||||||
# Create a Git SSH secret on disk and encrypt it with Mozilla SOPS
|
|
||||||
flux create secret git podinfo-auth \
|
|
||||||
--namespace=apps \
|
|
||||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
|
||||||
--export > podinfo-auth.yaml
|
|
||||||
|
|
||||||
|
# Encrypt the secret on disk with Mozilla SOPS
|
||||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
--in-place podinfo-auth.yaml`,
|
--in-place podinfo-auth.yaml`,
|
||||||
RunE: createSecretGitCmdRun,
|
RunE: createSecretGitCmdRun,
|
||||||
@@ -109,7 +105,7 @@ func init() {
|
|||||||
|
|
||||||
func NewSecretGitFlags() secretGitFlags {
|
func NewSecretGitFlags() secretGitFlags {
|
||||||
return secretGitFlags{
|
return secretGitFlags{
|
||||||
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm),
|
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.ECDSAPrivateKeyAlgorithm),
|
||||||
rsaBits: 2048,
|
rsaBits: 2048,
|
||||||
ecdsaCurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
ecdsaCurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||||
}
|
}
|
||||||
@@ -136,7 +132,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
opts := sourcesecret.Options{
|
opts := sourcesecret.Options{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
}
|
}
|
||||||
@@ -165,7 +161,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
fmt.Println(secret.Content)
|
rootCmd.Println(secret.Content)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,14 +176,14 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := upsertSecret(ctx, kubeClient, s); err != nil {
|
if err := upsertSecret(ctx, kubeClient, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
logger.Actionf("git secret '%s' created in '%s' namespace", name, *kubeconfigArgs.Namespace)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
44
cmd/flux/create_secret_git_test.go
Normal file
44
cmd/flux/create_secret_git_test.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateGitSecret(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args string
|
||||||
|
assert assertFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no args",
|
||||||
|
args: "create secret git",
|
||||||
|
assert: assertError("secret name is required"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic secret",
|
||||||
|
args: "create secret git podinfo-auth --url=https://github.com/stefanprodan/podinfo --username=my-username --password=my-password --namespace=my-namespace --export",
|
||||||
|
assert: assertGoldenFile("./testdata/create_secret/git/secret-git-basic.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh key",
|
||||||
|
args: "create secret git podinfo-auth --url=ssh://git@github.com/stefanprodan/podinfo --private-key-file=./testdata/create_secret/git/ecdsa.private --namespace=my-namespace --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_secret/git/git-ssh-secret.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh key with password",
|
||||||
|
args: "create secret git podinfo-auth --url=ssh://git@github.com/stefanprodan/podinfo --private-key-file=./testdata/create_secret/git/ecdsa-password.private --password=password --namespace=my-namespace --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_secret/git/git-ssh-secret-password.yaml"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: tt.args,
|
||||||
|
assert: tt.assert,
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,7 +80,7 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
opts := sourcesecret.Options{
|
opts := sourcesecret.Options{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
Username: secretHelmArgs.username,
|
Username: secretHelmArgs.username,
|
||||||
Password: secretHelmArgs.password,
|
Password: secretHelmArgs.password,
|
||||||
@@ -94,13 +94,13 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
fmt.Println(secret.Content)
|
rootCmd.Println(secret.Content)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -112,5 +112,6 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Actionf("helm secret '%s' created in '%s' namespace", name, *kubeconfigArgs.Namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
31
cmd/flux/create_secret_helm_test.go
Normal file
31
cmd/flux/create_secret_helm_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateHelmSecret(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args string
|
||||||
|
assert assertFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
args: "create secret helm",
|
||||||
|
assert: assertError("secret name is required"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: "create secret helm helm-secret --username=my-username --password=my-password --namespace=my-namespace --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_secret/helm/secret-helm.yaml"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: tt.args,
|
||||||
|
assert: tt.assert,
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,7 +79,7 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
opts := sourcesecret.Options{
|
opts := sourcesecret.Options{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
CAFilePath: secretTLSArgs.caFile,
|
CAFilePath: secretTLSArgs.caFile,
|
||||||
CertFilePath: secretTLSArgs.certFile,
|
CertFilePath: secretTLSArgs.certFile,
|
||||||
@@ -91,13 +91,13 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
fmt.Println(secret.Content)
|
rootCmd.Print(secret.Content)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -109,5 +109,6 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Actionf("tls secret '%s' created in '%s' namespace", name, *kubeconfigArgs.Namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
31
cmd/flux/create_secret_tls_test.go
Normal file
31
cmd/flux/create_secret_tls_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateTlsSecretNoArgs(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args string
|
||||||
|
assert assertFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
args: "create secret tls",
|
||||||
|
assert: assertError("secret name is required"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: "create secret tls certs --namespace=my-namespace --cert-file=./testdata/create_secret/tls/test-cert.pem --key-file=./testdata/create_secret/tls/test-key.pem --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_secret/tls/secret-tls.yaml"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: tt.args,
|
||||||
|
assert: tt.assert,
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,6 +28,14 @@ var createSourceCmd = &cobra.Command{
|
|||||||
Long: "The create source sub-commands generate sources.",
|
Long: "The create source sub-commands generate sources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type createSourceFlags struct {
|
||||||
|
fetchTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
var createSourceArgs createSourceFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
createSourceCmd.PersistentFlags().DurationVar(&createSourceArgs.fetchTimeout, "fetch-timeout", createSourceArgs.fetchTimeout,
|
||||||
|
"set a timeout for fetch operations performed by source-controller (e.g. 'git clone' or 'helm repo update')")
|
||||||
createCmd.AddCommand(createSourceCmd)
|
createCmd.AddCommand(createSourceCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -112,7 +111,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", name)
|
tmpDir, err := os.MkdirTemp("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -121,7 +120,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
bucket := &sourcev1.Bucket{
|
bucket := &sourcev1.Bucket{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: sourcev1.BucketSpec{
|
Spec: sourcev1.BucketSpec{
|
||||||
@@ -135,7 +134,12 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if sourceHelmArgs.secretRef != "" {
|
|
||||||
|
if createSourceArgs.fetchTimeout > 0 {
|
||||||
|
bucket.Spec.Timeout = &metav1.Duration{Duration: createSourceArgs.fetchTimeout}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceBucketArgs.secretRef != "" {
|
||||||
bucket.Spec.SecretRef = &meta.LocalObjectReference{
|
bucket.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: sourceBucketArgs.secretRef,
|
Name: sourceBucketArgs.secretRef,
|
||||||
}
|
}
|
||||||
@@ -148,7 +152,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -161,7 +165,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{},
|
StringData: map[string]string{},
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -57,6 +56,7 @@ type sourceGitFlags struct {
|
|||||||
caFile string
|
caFile string
|
||||||
privateKeyFile string
|
privateKeyFile string
|
||||||
recurseSubmodules bool
|
recurseSubmodules bool
|
||||||
|
silent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var createSourceGitCmd = &cobra.Command{
|
var createSourceGitCmd = &cobra.Command{
|
||||||
@@ -136,13 +136,14 @@ func init() {
|
|||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
|
||||||
createSourceGitCmd.Flags().BoolVar(&sourceGitArgs.recurseSubmodules, "recurse-submodules", false,
|
createSourceGitCmd.Flags().BoolVar(&sourceGitArgs.recurseSubmodules, "recurse-submodules", false,
|
||||||
"when enabled, configures the GitRepository source to initialize and include Git submodules in the artifact it produces")
|
"when enabled, configures the GitRepository source to initialize and include Git submodules in the artifact it produces")
|
||||||
|
createSourceGitCmd.Flags().BoolVarP(&sourceGitArgs.silent, "silent", "s", false, "assumes the deploy key is already setup, skips confirmation")
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceGitCmd)
|
createSourceCmd.AddCommand(createSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSourceGitFlags() sourceGitFlags {
|
func newSourceGitFlags() sourceGitFlags {
|
||||||
return sourceGitFlags{
|
return sourceGitFlags{
|
||||||
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.RSAPrivateKeyAlgorithm),
|
keyAlgorithm: flags.PublicKeyAlgorithm(sourcesecret.ECDSAPrivateKeyAlgorithm),
|
||||||
keyRSABits: 2048,
|
keyRSABits: 2048,
|
||||||
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||||
}
|
}
|
||||||
@@ -178,7 +179,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("recurse submodules requires --git-implementation=%s", sourcev1.GoGitImplementation)
|
return fmt.Errorf("recurse submodules requires --git-implementation=%s", sourcev1.GoGitImplementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", name)
|
tmpDir, err := os.MkdirTemp("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -192,7 +193,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
gitRepository := sourcev1.GitRepository{
|
gitRepository := sourcev1.GitRepository{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: sourcev1.GitRepositorySpec{
|
Spec: sourcev1.GitRepositorySpec{
|
||||||
@@ -205,6 +206,10 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if createSourceArgs.fetchTimeout > 0 {
|
||||||
|
gitRepository.Spec.Timeout = &metav1.Duration{Duration: createSourceArgs.fetchTimeout}
|
||||||
|
}
|
||||||
|
|
||||||
if sourceGitArgs.gitImplementation != "" {
|
if sourceGitArgs.gitImplementation != "" {
|
||||||
gitRepository.Spec.GitImplementation = sourceGitArgs.gitImplementation.String()
|
gitRepository.Spec.GitImplementation = sourceGitArgs.gitImplementation.String()
|
||||||
}
|
}
|
||||||
@@ -230,7 +235,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -239,7 +244,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if sourceGitArgs.secretRef == "" {
|
if sourceGitArgs.secretRef == "" {
|
||||||
secretOpts := sourcesecret.Options{
|
secretOpts := sourcesecret.Options{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
}
|
}
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
@@ -273,12 +278,14 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
|
if ppk, ok := s.StringData[sourcesecret.PublicKeySecretKey]; ok {
|
||||||
logger.Generatef("deploy key: %s", ppk)
|
logger.Generatef("deploy key: %s", ppk)
|
||||||
prompt := promptui.Prompt{
|
if !sourceGitArgs.silent {
|
||||||
Label: "Have you added the deploy key to your repository",
|
prompt := promptui.Prompt{
|
||||||
IsConfirm: true,
|
Label: "Have you added the deploy key to your repository",
|
||||||
}
|
IsConfirm: true,
|
||||||
if _, err := prompt.Run(); err != nil {
|
}
|
||||||
return fmt.Errorf("aborting")
|
if _, err := prompt.Run(); err != nil {
|
||||||
|
return fmt.Errorf("aborting")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.Actionf("applying secret with repository credentials")
|
logger.Actionf("applying secret with repository credentials")
|
||||||
|
|||||||
148
cmd/flux/create_source_git_test.go
Normal file
148
cmd/flux/create_source_git_test.go
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
//go:build unit
|
||||||
|
// +build unit
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pollInterval = 50 * time.Millisecond
|
||||||
|
var testTimeout = 10 * time.Second
|
||||||
|
|
||||||
|
// Update the GitRepository once created to exercise test specific behavior
|
||||||
|
type reconcileFunc func(repo *sourcev1.GitRepository)
|
||||||
|
|
||||||
|
// reconciler waits for an object to be created, then invokes a test supplied
|
||||||
|
// function to mutate that object, simulating a controller.
|
||||||
|
// Test should invoke run() to run the background reconciler task which
|
||||||
|
// polls to wait for the object to exist before applying the update function.
|
||||||
|
// Any errors from the reconciler are asserted on test completion.
|
||||||
|
type reconciler struct {
|
||||||
|
client client.Client
|
||||||
|
name types.NamespacedName
|
||||||
|
reconcile reconcileFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the background task that waits for the object to exist then applies
|
||||||
|
// the update function.
|
||||||
|
func (r *reconciler) run(t *testing.T) {
|
||||||
|
result := make(chan error)
|
||||||
|
go func() {
|
||||||
|
defer close(result)
|
||||||
|
err := wait.PollImmediate(
|
||||||
|
pollInterval,
|
||||||
|
testTimeout,
|
||||||
|
r.conditionFunc)
|
||||||
|
result <- err
|
||||||
|
}()
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err := <-result; err != nil {
|
||||||
|
t.Errorf("Failure from test reconciler: '%v':", err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ConditionFunction that waits for the named GitRepository to be created,
|
||||||
|
// then sets the ready condition to true.
|
||||||
|
func (r *reconciler) conditionFunc() (bool, error) {
|
||||||
|
var repo sourcev1.GitRepository
|
||||||
|
if err := r.client.Get(context.Background(), r.name, &repo); err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return false, nil // Keep polling until object is created
|
||||||
|
}
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
r.reconcile(&repo)
|
||||||
|
err := r.client.Status().Update(context.Background(), &repo)
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSourceGit(t *testing.T) {
|
||||||
|
// Default command used for multiple tests
|
||||||
|
var command = "create source git podinfo --url=https://github.com/stefanprodan/podinfo --branch=master --timeout=" + testTimeout.String()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
args string
|
||||||
|
assert assertFunc
|
||||||
|
reconcile reconcileFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"NoArgs",
|
||||||
|
"create source git",
|
||||||
|
assertError("GitRepository source name is required"),
|
||||||
|
nil,
|
||||||
|
}, {
|
||||||
|
"Succeeded",
|
||||||
|
command,
|
||||||
|
assertGoldenFile("testdata/create_source_git/success.golden"),
|
||||||
|
func(repo *sourcev1.GitRepository) {
|
||||||
|
meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionTrue, sourcev1.GitOperationSucceedReason, "succeeded message")
|
||||||
|
repo.Status.Artifact = &sourcev1.Artifact{
|
||||||
|
Path: "some-path",
|
||||||
|
Revision: "v1",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"Failed",
|
||||||
|
command,
|
||||||
|
assertError("failed message"),
|
||||||
|
func(repo *sourcev1.GitRepository) {
|
||||||
|
meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionFalse, sourcev1.URLInvalidReason, "failed message")
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"NoArtifact",
|
||||||
|
command,
|
||||||
|
assertError("GitRepository source reconciliation completed but no artifact was found"),
|
||||||
|
func(repo *sourcev1.GitRepository) {
|
||||||
|
// Updated with no artifact
|
||||||
|
meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionTrue, sourcev1.GitOperationSucceedReason, "succeeded message")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
ns := allocateNamespace("podinfo")
|
||||||
|
setupTestNamespace(ns, t)
|
||||||
|
if tc.reconcile != nil {
|
||||||
|
r := reconciler{
|
||||||
|
client: testEnv.client,
|
||||||
|
name: types.NamespacedName{Namespace: ns, Name: "podinfo"},
|
||||||
|
reconcile: tc.reconcile,
|
||||||
|
}
|
||||||
|
r.run(t)
|
||||||
|
}
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: tc.args + " -n=" + ns,
|
||||||
|
assert: tc.assert,
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -66,13 +65,14 @@ For private Helm repositories, the basic authentication credentials are stored i
|
|||||||
}
|
}
|
||||||
|
|
||||||
type sourceHelmFlags struct {
|
type sourceHelmFlags struct {
|
||||||
url string
|
url string
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
certFile string
|
certFile string
|
||||||
keyFile string
|
keyFile string
|
||||||
caFile string
|
caFile string
|
||||||
secretRef string
|
secretRef string
|
||||||
|
passCredentials bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceHelmArgs sourceHelmFlags
|
var sourceHelmArgs sourceHelmFlags
|
||||||
@@ -85,6 +85,7 @@ func init() {
|
|||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
|
||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
|
||||||
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
|
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
|
||||||
|
createSourceHelmCmd.Flags().BoolVarP(&sourceHelmArgs.passCredentials, "pass-credentials", "", false, "pass credentials to all domains")
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceHelmCmd)
|
createSourceCmd.AddCommand(createSourceHelmCmd)
|
||||||
}
|
}
|
||||||
@@ -104,7 +105,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", name)
|
tmpDir, err := os.MkdirTemp("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -117,7 +118,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
helmRepository := &sourcev1.HelmRepository{
|
helmRepository := &sourcev1.HelmRepository{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: sourcev1.HelmRepositorySpec{
|
Spec: sourcev1.HelmRepositorySpec{
|
||||||
@@ -128,10 +129,15 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if createSourceArgs.fetchTimeout > 0 {
|
||||||
|
helmRepository.Spec.Timeout = &metav1.Duration{Duration: createSourceArgs.fetchTimeout}
|
||||||
|
}
|
||||||
|
|
||||||
if sourceHelmArgs.secretRef != "" {
|
if sourceHelmArgs.secretRef != "" {
|
||||||
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: sourceHelmArgs.secretRef,
|
Name: sourceHelmArgs.secretRef,
|
||||||
}
|
}
|
||||||
|
helmRepository.Spec.PassCredentials = sourceHelmArgs.passCredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
@@ -141,7 +147,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -151,7 +157,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
secretName := fmt.Sprintf("helm-%s", name)
|
secretName := fmt.Sprintf("helm-%s", name)
|
||||||
secretOpts := sourcesecret.Options{
|
secretOpts := sourcesecret.Options{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Username: sourceHelmArgs.username,
|
Username: sourceHelmArgs.username,
|
||||||
Password: sourceHelmArgs.password,
|
Password: sourceHelmArgs.password,
|
||||||
CertFilePath: sourceHelmArgs.certFile,
|
CertFilePath: sourceHelmArgs.certFile,
|
||||||
@@ -175,6 +181,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
}
|
}
|
||||||
|
helmRepository.Spec.PassCredentials = sourceHelmArgs.passCredentials
|
||||||
logger.Successf("authentication configured")
|
logger.Successf("authentication configured")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
for i, _ := range tenantArgs.namespaces {
|
for i := range tenantArgs.namespaces {
|
||||||
if err := exportTenant(namespaces[i], accounts[i], roleBindings[i]); err != nil {
|
if err := exportTenant(namespaces[i], accounts[i], roleBindings[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -159,12 +159,12 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, _ := range tenantArgs.namespaces {
|
for i := range tenantArgs.namespaces {
|
||||||
logger.Actionf("applying namespace %s", namespaces[i].Name)
|
logger.Actionf("applying namespace %s", namespaces[i].Name)
|
||||||
if err := upsertNamespace(ctx, kubeClient, namespaces[i]); err != nil {
|
if err := upsertNamespace(ctx, kubeClient, namespaces[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -60,13 +60,13 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, rootArgs.namespace)
|
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, *kubeconfigArgs.Namespace)
|
||||||
err = kubeClient.Delete(ctx, del.object.asClientObject())
|
err = kubeClient.Delete(ctx, del.object.asClientObject())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var deleteAlertCmd = &cobra.Command{
|
|||||||
Long: "The delete alert command removes the given Alert from the cluster.",
|
Long: "The delete alert command removes the given Alert from the cluster.",
|
||||||
Example: ` # Delete an Alert and the Kubernetes resources created by it
|
Example: ` # Delete an Alert and the Kubernetes resources created by it
|
||||||
flux delete alert main`,
|
flux delete alert main`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: alertType,
|
apiType: alertType,
|
||||||
object: universalAdapter{¬ificationv1.Alert{}},
|
object: universalAdapter{¬ificationv1.Alert{}},
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var deleteAlertProviderCmd = &cobra.Command{
|
|||||||
Long: "The delete alert-provider command removes the given Provider from the cluster.",
|
Long: "The delete alert-provider command removes the given Provider from the cluster.",
|
||||||
Example: ` # Delete a Provider and the Kubernetes resources created by it
|
Example: ` # Delete a Provider and the Kubernetes resources created by it
|
||||||
flux delete alert-provider slack`,
|
flux delete alert-provider slack`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: alertProviderType,
|
apiType: alertProviderType,
|
||||||
object: universalAdapter{¬ificationv1.Provider{}},
|
object: universalAdapter{¬ificationv1.Provider{}},
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ var deleteHelmReleaseCmd = &cobra.Command{
|
|||||||
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
|
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
|
||||||
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
||||||
flux delete hr podinfo`,
|
flux delete hr podinfo`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: helmReleaseType,
|
apiType: helmReleaseType,
|
||||||
object: universalAdapter{&helmv2.HelmRelease{}},
|
object: universalAdapter{&helmv2.HelmRelease{}},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImagePolicyCmd = &cobra.Command{
|
var deleteImagePolicyCmd = &cobra.Command{
|
||||||
@@ -28,6 +28,7 @@ var deleteImagePolicyCmd = &cobra.Command{
|
|||||||
Long: "The delete image policy command deletes the given ImagePolicy from the cluster.",
|
Long: "The delete image policy command deletes the given ImagePolicy from the cluster.",
|
||||||
Example: ` # Delete an image policy
|
Example: ` # Delete an image policy
|
||||||
flux delete image policy alpine3.x`,
|
flux delete image policy alpine3.x`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: imagePolicyType,
|
apiType: imagePolicyType,
|
||||||
object: universalAdapter{&imagev1.ImagePolicy{}},
|
object: universalAdapter{&imagev1.ImagePolicy{}},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImageRepositoryCmd = &cobra.Command{
|
var deleteImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -28,6 +28,7 @@ var deleteImageRepositoryCmd = &cobra.Command{
|
|||||||
Long: "The delete image repository command deletes the given ImageRepository from the cluster.",
|
Long: "The delete image repository command deletes the given ImageRepository from the cluster.",
|
||||||
Example: ` # Delete an image repository
|
Example: ` # Delete an image repository
|
||||||
flux delete image repository alpine`,
|
flux delete image repository alpine`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: imageRepositoryType,
|
apiType: imageRepositoryType,
|
||||||
object: universalAdapter{&imagev1.ImageRepository{}},
|
object: universalAdapter{&imagev1.ImageRepository{}},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImageUpdateCmd = &cobra.Command{
|
var deleteImageUpdateCmd = &cobra.Command{
|
||||||
@@ -28,6 +28,7 @@ var deleteImageUpdateCmd = &cobra.Command{
|
|||||||
Long: "The delete image update command deletes the given ImageUpdateAutomation from the cluster.",
|
Long: "The delete image update command deletes the given ImageUpdateAutomation from the cluster.",
|
||||||
Example: ` # Delete an image update automation
|
Example: ` # Delete an image update automation
|
||||||
flux delete image update latest-images`,
|
flux delete image update latest-images`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: imageUpdateAutomationType,
|
apiType: imageUpdateAutomationType,
|
||||||
object: universalAdapter{&autov1.ImageUpdateAutomation{}},
|
object: universalAdapter{&autov1.ImageUpdateAutomation{}},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteKsCmd = &cobra.Command{
|
var deleteKsCmd = &cobra.Command{
|
||||||
@@ -29,6 +29,7 @@ var deleteKsCmd = &cobra.Command{
|
|||||||
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
|
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
|
||||||
Example: ` # Delete a kustomization and the Kubernetes resources created by it
|
Example: ` # Delete a kustomization and the Kubernetes resources created by it
|
||||||
flux delete kustomization podinfo`,
|
flux delete kustomization podinfo`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: kustomizationType,
|
apiType: kustomizationType,
|
||||||
object: universalAdapter{&kustomizev1.Kustomization{}},
|
object: universalAdapter{&kustomizev1.Kustomization{}},
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var deleteReceiverCmd = &cobra.Command{
|
|||||||
Long: "The delete receiver command removes the given Receiver from the cluster.",
|
Long: "The delete receiver command removes the given Receiver from the cluster.",
|
||||||
Example: ` # Delete an Receiver and the Kubernetes resources created by it
|
Example: ` # Delete an Receiver and the Kubernetes resources created by it
|
||||||
flux delete receiver main`,
|
flux delete receiver main`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: receiverType,
|
apiType: receiverType,
|
||||||
object: universalAdapter{¬ificationv1.Receiver{}},
|
object: universalAdapter{¬ificationv1.Receiver{}},
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var deleteSourceBucketCmd = &cobra.Command{
|
|||||||
Long: "The delete source bucket command deletes the given Bucket from the cluster.",
|
Long: "The delete source bucket command deletes the given Bucket from the cluster.",
|
||||||
Example: ` # Delete a Bucket source
|
Example: ` # Delete a Bucket source
|
||||||
flux delete source bucket podinfo`,
|
flux delete source bucket podinfo`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: bucketType,
|
apiType: bucketType,
|
||||||
object: universalAdapter{&sourcev1.Bucket{}},
|
object: universalAdapter{&sourcev1.Bucket{}},
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var deleteSourceGitCmd = &cobra.Command{
|
|||||||
Long: "The delete source git command deletes the given GitRepository from the cluster.",
|
Long: "The delete source git command deletes the given GitRepository from the cluster.",
|
||||||
Example: ` # Delete a Git repository
|
Example: ` # Delete a Git repository
|
||||||
flux delete source git podinfo`,
|
flux delete source git podinfo`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: gitRepositoryType,
|
apiType: gitRepositoryType,
|
||||||
object: universalAdapter{&sourcev1.GitRepository{}},
|
object: universalAdapter{&sourcev1.GitRepository{}},
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var deleteSourceHelmCmd = &cobra.Command{
|
|||||||
Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
|
Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
|
||||||
Example: ` # Delete a Helm repository
|
Example: ` # Delete a Helm repository
|
||||||
flux delete source helm podinfo`,
|
flux delete source helm podinfo`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)),
|
||||||
RunE: deleteCommand{
|
RunE: deleteCommand{
|
||||||
apiType: helmRepositoryType,
|
apiType: helmRepositoryType,
|
||||||
object: universalAdapter{&sourcev1.HelmRepository{}},
|
object: universalAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
@@ -73,19 +74,19 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportArgs.all {
|
if exportArgs.all {
|
||||||
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(rootArgs.namespace))
|
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(*kubeconfigArgs.Namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if export.list.len() == 0 {
|
if export.list.len() == 0 {
|
||||||
return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
|
return fmt.Errorf("no objects found in %s namespace", *kubeconfigArgs.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < export.list.len(); i++ {
|
for i := 0; i < export.list.len(); i++ {
|
||||||
@@ -96,7 +97,7 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())
|
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())
|
||||||
@@ -113,8 +114,8 @@ func printExport(export interface{}) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("---")
|
rootCmd.Println("---")
|
||||||
fmt.Println(resourceToString(data))
|
rootCmd.Println(resourceToString(data))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ var exportAlertCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a Alert
|
# Export a Alert
|
||||||
flux export alert main > main.yaml`,
|
flux export alert main > main.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: alertAdapter{¬ificationv1.Alert{}},
|
object: alertAdapter{¬ificationv1.Alert{}},
|
||||||
list: alertListAdapter{¬ificationv1.AlertList{}},
|
list: alertListAdapter{¬ificationv1.AlertList{}},
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ var exportAlertProviderCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a Provider
|
# Export a Provider
|
||||||
flux export alert-provider slack > slack.yaml`,
|
flux export alert-provider slack > slack.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: alertProviderAdapter{¬ificationv1.Provider{}},
|
object: alertProviderAdapter{¬ificationv1.Provider{}},
|
||||||
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ var exportHelmReleaseCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a HelmRelease
|
# Export a HelmRelease
|
||||||
flux export hr my-app > app-release.yaml`,
|
flux export hr my-app > app-release.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
|
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
|
||||||
list: helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
list: helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImagePolicyCmd = &cobra.Command{
|
var exportImagePolicyCmd = &cobra.Command{
|
||||||
@@ -32,6 +32,7 @@ var exportImagePolicyCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a specific policy
|
# Export a specific policy
|
||||||
flux export image policy alpine1x > alpine1x.yaml`,
|
flux export image policy alpine1x > alpine1x.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
|
object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
|
||||||
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImageRepositoryCmd = &cobra.Command{
|
var exportImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -32,6 +32,7 @@ var exportImageRepositoryCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a specific ImageRepository resource
|
# Export a specific ImageRepository resource
|
||||||
flux export image repository alpine > alpine.yaml`,
|
flux export image repository alpine > alpine.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
||||||
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImageUpdateCmd = &cobra.Command{
|
var exportImageUpdateCmd = &cobra.Command{
|
||||||
@@ -32,6 +32,7 @@ var exportImageUpdateCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a specific automation
|
# Export a specific automation
|
||||||
flux export image update latest-images > latest.yaml`,
|
flux export image update latest-images > latest.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
||||||
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportKsCmd = &cobra.Command{
|
var exportKsCmd = &cobra.Command{
|
||||||
@@ -33,6 +33,7 @@ var exportKsCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a Kustomization
|
# Export a Kustomization
|
||||||
flux export kustomization my-app > kustomization.yaml`,
|
flux export kustomization my-app > kustomization.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
|
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
|
||||||
list: kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
list: kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ var exportReceiverCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a Receiver
|
# Export a Receiver
|
||||||
flux export receiver main > main.yaml`,
|
flux export receiver main > main.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
|
||||||
RunE: exportCommand{
|
RunE: exportCommand{
|
||||||
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
||||||
object: receiverAdapter{¬ificationv1.Receiver{}},
|
object: receiverAdapter{¬ificationv1.Receiver{}},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -58,19 +59,19 @@ func (export exportWithSecretCommand) run(cmd *cobra.Command, args []string) err
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportArgs.all {
|
if exportArgs.all {
|
||||||
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(rootArgs.namespace))
|
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(*kubeconfigArgs.Namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if export.list.len() == 0 {
|
if export.list.len() == 0 {
|
||||||
return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
|
return fmt.Errorf("no objects found in %s namespace", *kubeconfigArgs.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < export.list.len(); i++ {
|
for i := 0; i < export.list.len(); i++ {
|
||||||
@@ -88,7 +89,7 @@ func (export exportWithSecretCommand) run(cmd *cobra.Command, args []string) err
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: rootArgs.namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())
|
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ var exportSourceBucketCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a Bucket source including the static credentials
|
# Export a Bucket source including the static credentials
|
||||||
flux export source bucket my-bucket --with-credentials > source.yaml`,
|
flux export source bucket my-bucket --with-credentials > source.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)),
|
||||||
RunE: exportWithSecretCommand{
|
RunE: exportWithSecretCommand{
|
||||||
list: bucketListAdapter{&sourcev1.BucketList{}},
|
list: bucketListAdapter{&sourcev1.BucketList{}},
|
||||||
object: bucketAdapter{&sourcev1.Bucket{}},
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ var exportSourceGitCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a GitRepository source including the SSH key pair or basic auth credentials
|
# Export a GitRepository source including the SSH key pair or basic auth credentials
|
||||||
flux export source git my-private-repo --with-credentials > source.yaml`,
|
flux export source git my-private-repo --with-credentials > source.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)),
|
||||||
RunE: exportWithSecretCommand{
|
RunE: exportWithSecretCommand{
|
||||||
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
list: gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
list: gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ var exportSourceHelmCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Export a HelmRepository source including the basic auth credentials
|
# Export a HelmRepository source including the basic auth credentials
|
||||||
flux export source helm my-private-repo --with-credentials > source.yaml`,
|
flux export source helm my-private-repo --with-credentials > source.yaml`,
|
||||||
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)),
|
||||||
RunE: exportWithSecretCommand{
|
RunE: exportWithSecretCommand{
|
||||||
list: helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
list: helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
||||||
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
|||||||
89
cmd/flux/export_test.go
Normal file
89
cmd/flux/export_test.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
//go:build unit
|
||||||
|
// +build unit
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExport(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
arg string
|
||||||
|
goldenFile string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"alert-provider",
|
||||||
|
"export alert-provider slack",
|
||||||
|
"testdata/export/provider.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alert",
|
||||||
|
"export alert flux-system",
|
||||||
|
"testdata/export/alert.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image policy",
|
||||||
|
"export image policy flux-system",
|
||||||
|
"testdata/export/image-policy.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image repository",
|
||||||
|
"export image repository flux-system",
|
||||||
|
"testdata/export/image-repo.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image update",
|
||||||
|
"export image update flux-system",
|
||||||
|
"testdata/export/image-update.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source git",
|
||||||
|
"export source git flux-system",
|
||||||
|
"testdata/export/git-repo.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source helm",
|
||||||
|
"export source helm flux-system",
|
||||||
|
"testdata/export/helm-repo.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"receiver",
|
||||||
|
"export receiver flux-system",
|
||||||
|
"testdata/export/receiver.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kustomization",
|
||||||
|
"export kustomization flux-system",
|
||||||
|
"testdata/export/ks.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"helmrelease",
|
||||||
|
"export helmrelease flux-system",
|
||||||
|
"testdata/export/helm-release.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bucket",
|
||||||
|
"export source bucket flux-system",
|
||||||
|
"testdata/export/bucket.yaml",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
objectFile := "testdata/export/objects.yaml"
|
||||||
|
tmpl := map[string]string{
|
||||||
|
"fluxns": allocateNamespace("flux-system"),
|
||||||
|
}
|
||||||
|
testEnv.CreateObjectFile(objectFile, tmpl, t)
|
||||||
|
|
||||||
|
for _, tt := range cases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: tt.arg + " -n=" + tmpl["fluxns"],
|
||||||
|
assert: assertGoldenTemplateFile(tt.goldenFile, tmpl),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
157
cmd/flux/get.go
157
cmd/flux/get.go
@@ -25,6 +25,9 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
watchtools "k8s.io/client-go/tools/watch"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
@@ -32,6 +35,26 @@ import (
|
|||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type deriveType func(runtime.Object) (summarisable, error)
|
||||||
|
|
||||||
|
type typeMap map[string]deriveType
|
||||||
|
|
||||||
|
func (m typeMap) registerCommand(t string, f deriveType) error {
|
||||||
|
if _, ok := m[t]; ok {
|
||||||
|
return fmt.Errorf("duplicate type function %s", t)
|
||||||
|
}
|
||||||
|
m[t] = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m typeMap) execute(t string, obj runtime.Object) (summarisable, error) {
|
||||||
|
f, ok := m[t]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unsupported type %s", t)
|
||||||
|
}
|
||||||
|
return f(obj)
|
||||||
|
}
|
||||||
|
|
||||||
var getCmd = &cobra.Command{
|
var getCmd = &cobra.Command{
|
||||||
Use: "get",
|
Use: "get",
|
||||||
Short: "Get the resources and their status",
|
Short: "Get the resources and their status",
|
||||||
@@ -39,7 +62,10 @@ var getCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetFlags struct {
|
type GetFlags struct {
|
||||||
allNamespaces bool
|
allNamespaces bool
|
||||||
|
noHeader bool
|
||||||
|
statusSelector string
|
||||||
|
watch bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var getArgs GetFlags
|
var getArgs GetFlags
|
||||||
@@ -47,6 +73,10 @@ var getArgs GetFlags
|
|||||||
func init() {
|
func init() {
|
||||||
getCmd.PersistentFlags().BoolVarP(&getArgs.allNamespaces, "all-namespaces", "A", false,
|
getCmd.PersistentFlags().BoolVarP(&getArgs.allNamespaces, "all-namespaces", "A", false,
|
||||||
"list the requested object(s) across all namespaces")
|
"list the requested object(s) across all namespaces")
|
||||||
|
getCmd.PersistentFlags().BoolVarP(&getArgs.noHeader, "no-header", "", false, "skip the header when printing the results")
|
||||||
|
getCmd.PersistentFlags().BoolVarP(&getArgs.watch, "watch", "w", false, "After listing/getting the requested object, watch for changes.")
|
||||||
|
getCmd.PersistentFlags().StringVar(&getArgs.statusSelector, "status-selector", "",
|
||||||
|
"specify the status condition name and the desired state to filter the get result, e.g. ready=false")
|
||||||
rootCmd.AddCommand(getCmd)
|
rootCmd.AddCommand(getCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +84,7 @@ type summarisable interface {
|
|||||||
listAdapter
|
listAdapter
|
||||||
summariseItem(i int, includeNamespace bool, includeKind bool) []string
|
summariseItem(i int, includeNamespace bool, includeKind bool) []string
|
||||||
headers(includeNamespace bool) []string
|
headers(includeNamespace bool) []string
|
||||||
|
statusSelectorMatches(i int, conditionType, conditionStatus string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- these help with implementations of summarisable
|
// --- these help with implementations of summarisable
|
||||||
@@ -65,6 +96,20 @@ func statusAndMessage(conditions []metav1.Condition) (string, string) {
|
|||||||
return string(metav1.ConditionFalse), "waiting to be reconciled"
|
return string(metav1.ConditionFalse), "waiting to be reconciled"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func statusMatches(conditionType, conditionStatus string, conditions []metav1.Condition) bool {
|
||||||
|
// we don't use apimeta.FindStatusCondition because we'd like to use EqualFold to compare two strings
|
||||||
|
var c *metav1.Condition
|
||||||
|
for i := range conditions {
|
||||||
|
if strings.EqualFold(conditions[i].Type, conditionType) {
|
||||||
|
c = &conditions[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c != nil {
|
||||||
|
return strings.EqualFold(string(c.Status), conditionStatus)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func nameColumns(item named, includeNamespace bool, includeKind bool) []string {
|
func nameColumns(item named, includeNamespace bool, includeKind bool) []string {
|
||||||
name := item.GetName()
|
name := item.GetName()
|
||||||
if includeKind {
|
if includeKind {
|
||||||
@@ -82,51 +127,137 @@ var namespaceHeader = []string{"Namespace"}
|
|||||||
|
|
||||||
type getCommand struct {
|
type getCommand struct {
|
||||||
apiType
|
apiType
|
||||||
list summarisable
|
list summarisable
|
||||||
|
funcMap typeMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
kubeClient, err := utils.KubeClient(kubeconfigArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var listOpts []client.ListOption
|
var listOpts []client.ListOption
|
||||||
if !getArgs.allNamespaces {
|
if !getArgs.allNamespaces {
|
||||||
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
listOpts = append(listOpts, client.InNamespace(*kubeconfigArgs.Namespace))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
listOpts = append(listOpts, client.MatchingFields{"metadata.name": args[0]})
|
listOpts = append(listOpts, client.MatchingFields{"metadata.name": args[0]})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAll := cmd.Use == "all"
|
||||||
|
|
||||||
|
if getArgs.watch {
|
||||||
|
return get.watch(ctx, kubeClient, cmd, args, listOpts)
|
||||||
|
}
|
||||||
|
|
||||||
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
|
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll := cmd.Use == "all"
|
|
||||||
|
|
||||||
if get.list.len() == 0 {
|
if get.list.len() == 0 {
|
||||||
if !getAll {
|
if !getAll {
|
||||||
logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace)
|
logger.Failuref("no %s objects found in %s namespace", get.kind, *kubeconfigArgs.Namespace)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
header := get.list.headers(getArgs.allNamespaces)
|
var header []string
|
||||||
var rows [][]string
|
if !getArgs.noHeader {
|
||||||
for i := 0; i < get.list.len(); i++ {
|
header = get.list.headers(getArgs.allNamespaces)
|
||||||
row := get.list.summariseItem(i, getArgs.allNamespaces, getAll)
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
}
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
|
rows, err := getRowsToPrint(getAll, get.list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.PrintTable(cmd.OutOrStdout(), header, rows)
|
||||||
|
|
||||||
if getAll {
|
if getAll {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRowsToPrint(getAll bool, list summarisable) ([][]string, error) {
|
||||||
|
noFilter := true
|
||||||
|
var conditionType, conditionStatus string
|
||||||
|
if getArgs.statusSelector != "" {
|
||||||
|
parts := strings.SplitN(getArgs.statusSelector, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("expected status selector in type=status format, but found: %s", getArgs.statusSelector)
|
||||||
|
}
|
||||||
|
conditionType = parts[0]
|
||||||
|
conditionStatus = parts[1]
|
||||||
|
noFilter = false
|
||||||
|
}
|
||||||
|
var rows [][]string
|
||||||
|
for i := 0; i < list.len(); i++ {
|
||||||
|
if noFilter || list.statusSelectorMatches(i, conditionType, conditionStatus) {
|
||||||
|
row := list.summariseItem(i, getArgs.allNamespaces, getAll)
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// watch starts a client-side watch of one or more resources.
|
||||||
|
func (get *getCommand) watch(ctx context.Context, kubeClient client.WithWatch, cmd *cobra.Command, args []string, listOpts []client.ListOption) error {
|
||||||
|
w, err := kubeClient.Watch(ctx, get.list.asClientList(), listOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = watchUntil(ctx, w, get)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchUntil(ctx context.Context, w watch.Interface, get *getCommand) (bool, error) {
|
||||||
|
firstIteration := true
|
||||||
|
_, error := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
|
||||||
|
objToPrint := e.Object
|
||||||
|
sink, err := get.funcMap.execute(get.apiType.kind, objToPrint)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var header []string
|
||||||
|
if !getArgs.noHeader {
|
||||||
|
header = sink.headers(getArgs.allNamespaces)
|
||||||
|
}
|
||||||
|
rows, err := getRowsToPrint(false, sink)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if firstIteration {
|
||||||
|
utils.PrintTable(os.Stdout, header, rows)
|
||||||
|
firstIteration = false
|
||||||
|
} else {
|
||||||
|
utils.PrintTable(os.Stdout, []string{}, rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return false, error
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateWatchOption(cmd *cobra.Command, toMatch string) error {
|
||||||
|
w, _ := cmd.Flags().GetBool("watch")
|
||||||
|
if cmd.Use == toMatch && w {
|
||||||
|
return fmt.Errorf("expected a single resource type, but found %s", cmd.Use)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -32,10 +34,40 @@ var getAlertCmd = &cobra.Command{
|
|||||||
Long: "The get alert command prints the statuses of the resources.",
|
Long: "The get alert command prints the statuses of the resources.",
|
||||||
Example: ` # List all Alerts and their status
|
Example: ` # List all Alerts and their status
|
||||||
flux get alerts`,
|
flux get alerts`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
|
||||||
apiType: alertType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: &alertListAdapter{¬ificationv1.AlertList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: alertType,
|
||||||
|
list: &alertListAdapter{¬ificationv1.AlertList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*notificationv1.Alert)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v alert", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := alertListAdapter{
|
||||||
|
¬ificationv1.AlertList{
|
||||||
|
Items: []notificationv1.Alert{
|
||||||
|
*o,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -55,3 +87,8 @@ func (s alertListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s alertListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -29,10 +32,40 @@ var getAlertProviderCmd = &cobra.Command{
|
|||||||
Long: "The get alert-provider command prints the statuses of the resources.",
|
Long: "The get alert-provider command prints the statuses of the resources.",
|
||||||
Example: ` # List all Providers and their status
|
Example: ` # List all Providers and their status
|
||||||
flux get alert-providers`,
|
flux get alert-providers`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
|
||||||
apiType: alertProviderType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: alertProviderType,
|
||||||
|
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*notificationv1.Provider)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v alert-provider", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := alertProviderListAdapter{
|
||||||
|
¬ificationv1.ProviderList{
|
||||||
|
Items: []notificationv1.Provider{
|
||||||
|
*o,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -52,3 +85,8 @@ func (s alertProviderListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s alertProviderListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,7 +36,12 @@ var getAllCmd = &cobra.Command{
|
|||||||
# List all resources in all namespaces
|
# List all resources in all namespaces
|
||||||
flux get all --all-namespaces`,
|
flux get all --all-namespaces`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
err := getSourceAllCmd.RunE(cmd, args)
|
err := validateWatchOption(cmd, "all")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = getSourceAllCmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logError(err)
|
logError(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getHelmReleaseCmd = &cobra.Command{
|
var getHelmReleaseCmd = &cobra.Command{
|
||||||
@@ -31,10 +33,37 @@ var getHelmReleaseCmd = &cobra.Command{
|
|||||||
Long: "The get helmreleases command prints the statuses of the resources.",
|
Long: "The get helmreleases command prints the statuses of the resources.",
|
||||||
Example: ` # List all Helm releases and their status
|
Example: ` # List all Helm releases and their status
|
||||||
flux get helmreleases`,
|
flux get helmreleases`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
|
||||||
apiType: helmReleaseType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: helmReleaseType,
|
||||||
|
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*helmv2.HelmRelease)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v helmrelease", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := helmReleaseListAdapter{&helmv2.HelmReleaseList{
|
||||||
|
Items: []helmv2.HelmRelease{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -56,3 +85,8 @@ func (a helmReleaseListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a helmReleaseListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ var getImageCmd = &cobra.Command{
|
|||||||
Aliases: []string{"image"},
|
Aliases: []string{"image"},
|
||||||
Short: "Get image automation object status",
|
Short: "Get image automation object status",
|
||||||
Long: "The get image sub-commands print the status of image automation objects.",
|
Long: "The get image sub-commands print the status of image automation objects.",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return validateWatchOption(cmd, "images")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageAllCmd = &cobra.Command{
|
var getImageAllCmd = &cobra.Command{
|
||||||
@@ -35,6 +35,11 @@ var getImageAllCmd = &cobra.Command{
|
|||||||
# List all image objects in all namespaces
|
# List all image objects in all namespaces
|
||||||
flux get images all --all-namespaces`,
|
flux get images all --all-namespaces`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := validateWatchOption(cmd, "all")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var allImageCmd = []getCommand{
|
var allImageCmd = []getCommand{
|
||||||
{
|
{
|
||||||
apiType: imageRepositoryType,
|
apiType: imageRepositoryType,
|
||||||
|
|||||||
@@ -17,9 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"fmt"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImagePolicyCmd = &cobra.Command{
|
var getImagePolicyCmd = &cobra.Command{
|
||||||
@@ -31,10 +34,37 @@ var getImagePolicyCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List image policies from all namespaces
|
# List image policies from all namespaces
|
||||||
flux get image policy --all-namespaces`,
|
flux get image policy --all-namespaces`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
|
||||||
apiType: imagePolicyType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: imagePolicyType,
|
||||||
|
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*imagev1.ImagePolicy)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v policy", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := imagePolicyListAdapter{&imagev1.ImagePolicyList{
|
||||||
|
Items: []imagev1.ImagePolicy{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -54,3 +84,8 @@ func (s imagePolicyListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s imagePolicyListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageRepositoryCmd = &cobra.Command{
|
var getImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -35,10 +37,37 @@ var getImageRepositoryCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List image repositories from all namespaces
|
# List image repositories from all namespaces
|
||||||
flux get image repository --all-namespaces`,
|
flux get image repository --all-namespaces`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),
|
||||||
apiType: imageRepositoryType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: imageRepositoryType,
|
||||||
|
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*imagev1.ImageRepository)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v repository", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := imageRepositoryListAdapter{&imagev1.ImageRepositoryList{
|
||||||
|
Items: []imagev1.ImageRepository{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -63,3 +92,8 @@ func (s imageRepositoryListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s imageRepositoryListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageUpdateCmd = &cobra.Command{
|
var getImageUpdateCmd = &cobra.Command{
|
||||||
@@ -35,10 +37,37 @@ var getImageUpdateCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List image update automations from all namespaces
|
# List image update automations from all namespaces
|
||||||
flux get image update --all-namespaces`,
|
flux get image update --all-namespaces`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),
|
||||||
apiType: imageUpdateAutomationType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: imageUpdateAutomationType,
|
||||||
|
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*autov1.ImageUpdateAutomation)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v update", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{
|
||||||
|
Items: []autov1.ImageUpdateAutomation{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -62,3 +91,8 @@ func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []strin
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s imageUpdateAutomationListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getKsCmd = &cobra.Command{
|
var getKsCmd = &cobra.Command{
|
||||||
@@ -32,10 +34,40 @@ var getKsCmd = &cobra.Command{
|
|||||||
Long: "The get kustomizations command prints the statuses of the resources.",
|
Long: "The get kustomizations command prints the statuses of the resources.",
|
||||||
Example: ` # List all kustomizations and their status
|
Example: ` # List all kustomizations and their status
|
||||||
flux get kustomizations`,
|
flux get kustomizations`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||||
apiType: kustomizationType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: kustomizationType,
|
||||||
|
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*kustomizev1.Kustomization)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v kustomization", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := kustomizationListAdapter{
|
||||||
|
&kustomizev1.KustomizationList{
|
||||||
|
Items: []kustomizev1.Kustomization{
|
||||||
|
*o,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -57,3 +89,8 @@ func (a kustomizationListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a kustomizationListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -32,10 +34,37 @@ var getReceiverCmd = &cobra.Command{
|
|||||||
Long: "The get receiver command prints the statuses of the resources.",
|
Long: "The get receiver command prints the statuses of the resources.",
|
||||||
Example: ` # List all Receiver and their status
|
Example: ` # List all Receiver and their status
|
||||||
flux get receivers`,
|
flux get receivers`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
|
||||||
apiType: receiverType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: receiverType,
|
||||||
|
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*notificationv1.Receiver)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v receiver", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := receiverListAdapter{¬ificationv1.ReceiverList{
|
||||||
|
Items: []notificationv1.Receiver{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -55,3 +84,8 @@ func (s receiverListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s receiverListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ var getSourceCmd = &cobra.Command{
|
|||||||
Aliases: []string{"source"},
|
Aliases: []string{"source"},
|
||||||
Short: "Get source statuses",
|
Short: "Get source statuses",
|
||||||
Long: "The get source sub-commands print the statuses of the sources.",
|
Long: "The get source sub-commands print the statuses of the sources.",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
return validateWatchOption(cmd, "sources")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ var getSourceAllCmd = &cobra.Command{
|
|||||||
# List all sources in all namespaces
|
# List all sources in all namespaces
|
||||||
flux get sources all --all-namespaces`,
|
flux get sources all --all-namespaces`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := validateWatchOption(cmd, "all")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var allSourceCmd = []getCommand{
|
var allSourceCmd = []getCommand{
|
||||||
{
|
{
|
||||||
apiType: bucketType,
|
apiType: bucketType,
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -34,10 +36,37 @@ var getSourceBucketCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List buckets from all namespaces
|
# List buckets from all namespaces
|
||||||
flux get sources helm --all-namespaces`,
|
flux get sources helm --all-namespaces`,
|
||||||
RunE: getCommand{
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)),
|
||||||
apiType: bucketType,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
get := getCommand{
|
||||||
}.run,
|
apiType: bucketType,
|
||||||
|
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*sourcev1.Bucket)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v bucket", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := &bucketListAdapter{&sourcev1.BucketList{
|
||||||
|
Items: []sourcev1.Bucket{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -62,3 +91,8 @@ func (a bucketListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a bucketListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user