Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22df860eca | ||
|
|
f395044d65 | ||
|
|
afe0ddcd84 | ||
|
|
2c0323684c | ||
|
|
6d5ffdea57 | ||
|
|
648af6e645 | ||
|
|
e1895a4e21 | ||
|
|
d5f45800ae | ||
|
|
51f9d249ff | ||
|
|
6f525356cb | ||
|
|
5008f9064e | ||
|
|
cff96ed7ca | ||
|
|
4e8a600f34 | ||
|
|
4fd5684277 | ||
|
|
06bf469ba7 | ||
|
|
b8a215230c | ||
|
|
2460cfcf1c | ||
|
|
364242c857 | ||
|
|
29e2900f59 | ||
|
|
61e1fb770e | ||
|
|
2d3fcbdea3 | ||
|
|
47e15cee3d | ||
|
|
adeb3e3f42 | ||
|
|
fb1278285b | ||
|
|
e371610849 | ||
|
|
424de63bd1 | ||
|
|
832c925d39 | ||
|
|
378f118d51 | ||
|
|
d651777122 | ||
|
|
65d8ebabb8 | ||
|
|
9195ed9a1b | ||
|
|
5df8f7313c | ||
|
|
25ed6ca0a4 | ||
|
|
9f972995bd | ||
|
|
29c46a9892 | ||
|
|
ef579fe596 | ||
|
|
5b268f62a3 | ||
|
|
1f1c8286a5 | ||
|
|
5401e1ace4 | ||
|
|
69294ef56d | ||
|
|
a685ed8029 | ||
|
|
68d0be3818 | ||
|
|
84e2cb4c1f | ||
|
|
263c664acd | ||
|
|
b12c4c22fb | ||
|
|
9f39fadb9e | ||
|
|
4c29a1ca27 | ||
|
|
f4db124d50 | ||
|
|
8f8c7cccc6 | ||
|
|
e2097c28bd | ||
|
|
871eb444fc | ||
|
|
bc5fbb9fa5 | ||
|
|
3e9749c6b1 | ||
|
|
eaf08fbe90 | ||
|
|
df411cdb88 | ||
|
|
a83c26a864 | ||
|
|
6aa853491c | ||
|
|
3af1d7a5b8 | ||
|
|
c7c57f6717 | ||
|
|
cbb97768d1 | ||
|
|
2bb40d335b | ||
|
|
1b581d6f51 | ||
|
|
c219eb2883 | ||
|
|
65a2c87b5a | ||
|
|
13aa523c37 | ||
|
|
584f0eea58 | ||
|
|
b5ebdb16b2 | ||
|
|
a18f84c27b | ||
|
|
eb86505375 | ||
|
|
984f1c8669 | ||
|
|
c7378edabc | ||
|
|
ac6c6e3a30 | ||
|
|
1bca31f32c | ||
|
|
28d099a6b2 | ||
|
|
6a2b53fbf5 | ||
|
|
7568f301b8 | ||
|
|
6add511447 | ||
|
|
a7586e69fd |
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve Flux v2
|
||||||
|
title: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Find out more about your support options and getting help at
|
||||||
|
|
||||||
|
https://fluxcd.io/support/
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Describe the bug
|
||||||
|
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
### To Reproduce
|
||||||
|
|
||||||
|
Steps to reproduce the behaviour:
|
||||||
|
|
||||||
|
1. Provide Flux install instructions
|
||||||
|
2. Provide a GitHub repository with Kubernetes manifests
|
||||||
|
|
||||||
|
### Expected behavior
|
||||||
|
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
|
||||||
|
- Kubernetes version:
|
||||||
|
- Git provider:
|
||||||
|
- Container registry provider:
|
||||||
|
|
||||||
|
Below please provide the output of the following commands:
|
||||||
|
|
||||||
|
```cli
|
||||||
|
flux --version
|
||||||
|
flux check
|
||||||
|
kubectl -n <namespace> get all
|
||||||
|
kubectl -n <namespace> logs deploy/source-controller
|
||||||
|
kubectl -n <namespace> logs deploy/kustomize-controller
|
||||||
|
```
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: Ask a question
|
||||||
|
url: https://github.com/fluxcd/flux2/discussions
|
||||||
|
about: Please ask and answer questions here.
|
||||||
5
.github/kind/config.yaml
vendored
Normal file
5
.github/kind/config.yaml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kind: Cluster
|
||||||
|
apiVersion: kind.x-k8s.io/v1alpha4
|
||||||
|
networking:
|
||||||
|
disableDefaultCNI: true # disable kindnet
|
||||||
|
podSubnet: 192.168.0.0/16 # set to Calico's default subnet
|
||||||
12
.github/workflows/docs.yaml
vendored
12
.github/workflows/docs.yaml
vendored
@@ -54,6 +54,18 @@ jobs:
|
|||||||
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/receiver.md" > docs/components/notification/receiver.md
|
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/receiver.md" > docs/components/notification/receiver.md
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
# image-*-controller CRDs; these use the same API group
|
||||||
|
IMG_REFL_VER=$(controller_version image-reflector-controller)
|
||||||
|
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/api/image-reflector.md" > docs/components/image/reflector-api.md
|
||||||
|
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/spec/v1alpha1/imagerepositories.md" > docs/components/image/imagerepositories.md
|
||||||
|
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-reflector-controller/$IMG_REFL_VER/docs/spec/v1alpha1/imagepolicies.md" > docs/components/image/imagepolicies.md
|
||||||
|
|
||||||
|
IMG_AUTO_VER=$(controller_version image-automation-controller)
|
||||||
|
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-automation-controller/$IMG_AUTO_VER/docs/api/image-automation.md" > docs/components/image/automation-api.md
|
||||||
|
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/image-automation-controller/$IMG_AUTO_VER/docs/spec/v1alpha1/imageupdateautomations.md" > docs/components/image/imageupdateautomations.md
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
# install script
|
# install script
|
||||||
cp install/flux.sh docs/install.sh
|
cp install/flux.sh docs/install.sh
|
||||||
|
|||||||
17
.github/workflows/e2e.yaml
vendored
17
.github/workflows/e2e.yaml
vendored
@@ -26,7 +26,13 @@ jobs:
|
|||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: engineerd/setup-kind@v0.5.0
|
uses: engineerd/setup-kind@v0.5.0
|
||||||
with:
|
with:
|
||||||
image: kindest/node:v1.16.9
|
version: "v0.10.0"
|
||||||
|
image: kindest/node:v1.20.2@sha256:8f7ea6e7642c0da54f04a7ee10431549c0257315b3a634f6ef2fecaaedb19bab
|
||||||
|
config: .github/kind/config.yaml # disable KIND-net
|
||||||
|
- name: Setup Calico for network policy
|
||||||
|
run: |
|
||||||
|
kubectl apply -f https://docs.projectcalico.org/v3.16/manifests/calico.yaml
|
||||||
|
kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
|
||||||
- name: Run test
|
- name: Run test
|
||||||
run: make test
|
run: make test
|
||||||
- name: Check if working tree is dirty
|
- name: Check if working tree is dirty
|
||||||
@@ -44,6 +50,15 @@ jobs:
|
|||||||
- name: flux install --manifests
|
- name: flux install --manifests
|
||||||
run: |
|
run: |
|
||||||
./bin/flux install --manifests ./manifests/install/
|
./bin/flux install --manifests ./manifests/install/
|
||||||
|
- name: flux create secret
|
||||||
|
run: |
|
||||||
|
./bin/flux create secret git git-ssh-test \
|
||||||
|
--url ssh://git@github.com/stefanprodan/podinfo
|
||||||
|
./bin/flux create secret git git-https-test \
|
||||||
|
--url https://github.com/stefanprodan/podinfo \
|
||||||
|
--username=test --password=test
|
||||||
|
./bin/flux create secret helm helm-test \
|
||||||
|
--username=test --password=test
|
||||||
- name: flux create source git
|
- name: flux create source git
|
||||||
run: |
|
run: |
|
||||||
./bin/flux create source git podinfo \
|
./bin/flux create source git podinfo \
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -66,6 +66,8 @@ the following guides:
|
|||||||
- [Setup Notifications](https://toolkit.fluxcd.io/guides/notifications/)
|
- [Setup Notifications](https://toolkit.fluxcd.io/guides/notifications/)
|
||||||
- [Setup Webhook Receivers](https://toolkit.fluxcd.io/guides/webhook-receivers/)
|
- [Setup Webhook Receivers](https://toolkit.fluxcd.io/guides/webhook-receivers/)
|
||||||
|
|
||||||
|
If you should need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
||||||
|
|
||||||
## GitOps Toolkit
|
## GitOps Toolkit
|
||||||
|
|
||||||
The GitOps Toolkit is the set of APIs and controllers that make up the
|
The GitOps Toolkit is the set of APIs and controllers that make up the
|
||||||
@@ -97,15 +99,19 @@ guides](https://toolkit.fluxcd.io/dev-guides/source-watcher/).
|
|||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
The Flux project is always looking for new contributors and there are a multitude of ways to get involved.
|
Need help or want to contribute? Please see the links below. The Flux project is always looking for new contributors and there are a multitude of ways to get involved.
|
||||||
Depending on what you want to do, some of the following bits might be your first steps:
|
|
||||||
|
|
||||||
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
|
- Getting Started?
|
||||||
- Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
- Look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
|
||||||
- Ask questions and propose features on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
- Need help?
|
||||||
- And if you are completely new to Flux and the GitOps Toolkit, take a look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
|
- First: Ask questions on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||||
- To be part of the conversation about Flux's development, [join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
|
- Second: Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
||||||
- Check out [how to contribute](CONTRIBUTING.md) to the project
|
- Please follow our [Support Guidelines](https://fluxcd.io/support/) (in short: be nice, be respectful of volunteers' time, understand that maintainers and contributors cannot respond to all DMs, and keep discussions in the public #flux channel as much as possible).
|
||||||
|
- Have feature proposals or want to contribute?
|
||||||
|
- Propose features on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||||
|
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
|
||||||
|
- [Join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
|
||||||
|
- Check out [how to contribute](CONTRIBUTING.md) to the project
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Note that this action can only be used on GitHub **Linux AMD64** runners.
|
|||||||
|
|
||||||
### Automate Flux updates
|
### Automate Flux updates
|
||||||
|
|
||||||
Example workflow for updating Flux's components generated with `flux bootstrap --arch=amd64 --path=clusters/production`:
|
Example workflow for updating Flux's components generated with `flux bootstrap --path=clusters/production`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
name: update-flux
|
name: update-flux
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
- name: Check for updates
|
- name: Check for updates
|
||||||
id: update
|
id: update
|
||||||
run: |
|
run: |
|
||||||
flux install --arch=amd64 \
|
flux install \
|
||||||
--export > ./clusters/production/flux-system/gotk-components.yaml
|
--export > ./clusters/production/flux-system/gotk-components.yaml
|
||||||
|
|
||||||
VERSION="$(flux -v)"
|
VERSION="$(flux -v)"
|
||||||
|
|||||||
@@ -45,62 +45,71 @@ var bootstrapCmd = &cobra.Command{
|
|||||||
Long: "The bootstrap sub-commands bootstrap the toolkit components on the targeted Git provider.",
|
Long: "The bootstrap sub-commands bootstrap the toolkit components on the targeted Git provider.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type bootstrapFlags struct {
|
||||||
bootstrapVersion string
|
version string
|
||||||
bootstrapDefaultComponents []string
|
defaultComponents []string
|
||||||
bootstrapExtraComponents []string
|
extraComponents []string
|
||||||
bootstrapRegistry string
|
registry string
|
||||||
bootstrapImagePullSecret string
|
imagePullSecret string
|
||||||
bootstrapBranch string
|
branch string
|
||||||
bootstrapWatchAllNamespaces bool
|
watchAllNamespaces bool
|
||||||
bootstrapNetworkPolicy bool
|
networkPolicy bool
|
||||||
bootstrapManifestsPath string
|
manifestsPath string
|
||||||
bootstrapArch flags.Arch
|
arch flags.Arch
|
||||||
bootstrapLogLevel = flags.LogLevel(defaults.LogLevel)
|
logLevel flags.LogLevel
|
||||||
bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"}
|
requiredComponents []string
|
||||||
bootstrapTokenAuth bool
|
tokenAuth bool
|
||||||
bootstrapClusterDomain string
|
clusterDomain string
|
||||||
)
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bootstrapDefaultBranch = "main"
|
bootstrapDefaultBranch = "main"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var bootstrapArgs = NewBootstrapFlags()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapVersion, "version", "v", defaults.Version,
|
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapArgs.version, "version", "v", rootArgs.defaults.Version,
|
||||||
"toolkit version")
|
"toolkit version")
|
||||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapDefaultComponents, "components", defaults.Components,
|
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
|
||||||
"list of components, accepts comma-separated values")
|
"list of components, accepts comma-separated values")
|
||||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapExtraComponents, "components-extra", nil,
|
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
|
||||||
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapRegistry, "registry", "ghcr.io/fluxcd",
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
|
||||||
"container registry where the toolkit images are published")
|
"container registry where the toolkit images are published")
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapImagePullSecret, "image-pull-secret", "",
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
|
||||||
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArch, "arch", bootstrapArch.Description())
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description())
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapBranch, "branch", bootstrapDefaultBranch,
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch,
|
||||||
"default branch (for GitHub this must match the default branch setting for the organization)")
|
"default branch (for GitHub this must match the default branch setting for the organization)")
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapWatchAllNamespaces, "watch-all-namespaces", true,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.watchAllNamespaces, "watch-all-namespaces", true,
|
||||||
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
|
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapNetworkPolicy, "network-policy", true,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.networkPolicy, "network-policy", true,
|
||||||
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapTokenAuth, "token-auth", false,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false,
|
||||||
"when enabled, the personal access token will be used instead of SSH deploy key")
|
"when enabled, the personal access token will be used instead of SSH deploy key")
|
||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapLogLevel, "log-level", bootstrapLogLevel.Description())
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description())
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapClusterDomain, "cluster-domain", defaults.ClusterDomain, "internal cluster domain")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||||
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
||||||
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
||||||
rootCmd.AddCommand(bootstrapCmd)
|
rootCmd.AddCommand(bootstrapCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewBootstrapFlags() bootstrapFlags {
|
||||||
|
return bootstrapFlags{
|
||||||
|
logLevel: flags.LogLevel(rootArgs.defaults.LogLevel),
|
||||||
|
requiredComponents: []string{"source-controller", "kustomize-controller"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func bootstrapComponents() []string {
|
func bootstrapComponents() []string {
|
||||||
return append(bootstrapDefaultComponents, bootstrapExtraComponents...)
|
return append(bootstrapArgs.defaultComponents, bootstrapArgs.extraComponents...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bootstrapValidate() error {
|
func bootstrapValidate() error {
|
||||||
components := bootstrapComponents()
|
components := bootstrapComponents()
|
||||||
for _, component := range bootstrapRequiredComponents {
|
for _, component := range bootstrapArgs.requiredComponents {
|
||||||
if !utils.ContainsItemString(components, component) {
|
if !utils.ContainsItemString(components, component) {
|
||||||
return fmt.Errorf("component %s is required", component)
|
return fmt.Errorf("component %s is required", component)
|
||||||
}
|
}
|
||||||
@@ -116,23 +125,23 @@ func bootstrapValidate() error {
|
|||||||
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
|
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
|
||||||
opts := install.Options{
|
opts := install.Options{
|
||||||
BaseURL: localManifests,
|
BaseURL: localManifests,
|
||||||
Version: bootstrapVersion,
|
Version: bootstrapArgs.version,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Components: bootstrapComponents(),
|
Components: bootstrapComponents(),
|
||||||
Registry: bootstrapRegistry,
|
Registry: bootstrapArgs.registry,
|
||||||
ImagePullSecret: bootstrapImagePullSecret,
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
WatchAllNamespaces: bootstrapWatchAllNamespaces,
|
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||||
NetworkPolicy: bootstrapNetworkPolicy,
|
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||||
LogLevel: bootstrapLogLevel.String(),
|
LogLevel: bootstrapArgs.logLevel.String(),
|
||||||
NotificationController: defaults.NotificationController,
|
NotificationController: rootArgs.defaults.NotificationController,
|
||||||
ManifestFile: defaults.ManifestFile,
|
ManifestFile: rootArgs.defaults.ManifestFile,
|
||||||
Timeout: timeout,
|
Timeout: rootArgs.timeout,
|
||||||
TargetPath: targetPath,
|
TargetPath: targetPath,
|
||||||
ClusterDomain: bootstrapClusterDomain,
|
ClusterDomain: bootstrapArgs.clusterDomain,
|
||||||
}
|
}
|
||||||
|
|
||||||
if localManifests == "" {
|
if localManifests == "" {
|
||||||
opts.BaseURL = defaults.BaseURL
|
opts.BaseURL = rootArgs.defaults.BaseURL
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := install.Generate(opts)
|
output, err := install.Generate(opts)
|
||||||
@@ -149,13 +158,13 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
|||||||
|
|
||||||
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
|
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
|
||||||
kubectlArgs := []string{"apply", "-f", manifestPath}
|
kubectlArgs := []string{"apply", "-f", manifestPath}
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||||
return fmt.Errorf("install failed")
|
return fmt.Errorf("install failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, deployment := range components {
|
for _, deployment := range components {
|
||||||
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
|
kubectlArgs = []string{"-n", rootArgs.namespace, "rollout", "status", "deployment", deployment, "--timeout", rootArgs.timeout.String()}
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||||
return fmt.Errorf("install failed")
|
return fmt.Errorf("install failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,20 +200,20 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
|
|||||||
|
|
||||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error {
|
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error {
|
||||||
kubectlArgs := []string{"apply", "-k", manifestsPath}
|
kubectlArgs := []string{"apply", "-k", manifestsPath}
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for cluster sync")
|
logger.Waitingf("waiting for cluster sync")
|
||||||
|
|
||||||
var gitRepository sourcev1.GitRepository
|
var gitRepository sourcev1.GitRepository
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil {
|
isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var kustomization kustomizev1.Kustomization
|
var kustomization kustomizev1.Kustomization
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
|
isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -239,7 +248,7 @@ func shouldCreateDeployKey(ctx context.Context, kubeClient client.Client, namesp
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateDeployKey(ctx context.Context, kubeClient client.Client, url *url.URL, namespace string) (string, error) {
|
func generateDeployKey(ctx context.Context, kubeClient client.Client, url *url.URL, namespace string) (string, error) {
|
||||||
pair, err := generateKeyPair(ctx, sourceGitKeyAlgorithm, sourceGitRSABits, sourceGitECDSACurve)
|
pair, err := generateKeyPair(ctx, sourceArgs.GitKeyAlgorithm, sourceArgs.GitRSABits, sourceArgs.GitECDSACurve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,35 +71,37 @@ the bootstrap command will perform an upgrade if needed.`,
|
|||||||
RunE: bootstrapGitHubCmdRun,
|
RunE: bootstrapGitHubCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type githubFlags struct {
|
||||||
ghOwner string
|
owner string
|
||||||
ghRepository string
|
repository string
|
||||||
ghInterval time.Duration
|
interval time.Duration
|
||||||
ghPersonal bool
|
personal bool
|
||||||
ghPrivate bool
|
private bool
|
||||||
ghHostname string
|
hostname string
|
||||||
ghPath flags.SafeRelativePath
|
path flags.SafeRelativePath
|
||||||
ghTeams []string
|
teams []string
|
||||||
ghDelete bool
|
delete bool
|
||||||
ghSSHHostname string
|
sshHostname string
|
||||||
)
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ghDefaultPermission = "maintain"
|
ghDefaultPermission = "maintain"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
var githubArgs githubFlags
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&ghOwner, "owner", "", "GitHub user or organization name")
|
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&ghRepository, "repository", "", "GitHub repository name")
|
|
||||||
bootstrapGitHubCmd.Flags().StringArrayVar(&ghTeams, "team", []string{}, "GitHub team to be given maintainer access")
|
|
||||||
bootstrapGitHubCmd.Flags().BoolVar(&ghPersonal, "personal", false, "is personal repository")
|
|
||||||
bootstrapGitHubCmd.Flags().BoolVar(&ghPrivate, "private", true, "is private repository")
|
|
||||||
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
|
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&ghSSHHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
|
||||||
bootstrapGitHubCmd.Flags().Var(&ghPath, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
|
||||||
|
|
||||||
bootstrapGitHubCmd.Flags().BoolVar(&ghDelete, "delete", false, "delete repository (used for testing only)")
|
func init() {
|
||||||
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
|
||||||
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository name")
|
||||||
|
bootstrapGitHubCmd.Flags().StringArrayVar(&githubArgs.teams, "team", []string{}, "GitHub team to be given maintainer access")
|
||||||
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "is personal repository")
|
||||||
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "is private repository")
|
||||||
|
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
||||||
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||||
|
bootstrapGitHubCmd.Flags().Var(&githubArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
|
|
||||||
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.delete, "delete", false, "delete repository (used for testing only)")
|
||||||
bootstrapGitHubCmd.Flags().MarkHidden("delete")
|
bootstrapGitHubCmd.Flags().MarkHidden("delete")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitHubCmd)
|
bootstrapCmd.AddCommand(bootstrapGitHubCmd)
|
||||||
@@ -115,41 +117,41 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, namespace, filepath.ToSlash(ghPath.String()))
|
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(githubArgs.path.String()))
|
||||||
|
|
||||||
if bootstrapPathDiffers {
|
if bootstrapPathDiffers {
|
||||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
repository, err := git.NewRepository(ghRepository, ghOwner, ghHostname, ghToken, "flux", ghOwner+"@users.noreply.github.com")
|
repository, err := git.NewRepository(githubArgs.repository, githubArgs.owner, githubArgs.hostname, ghToken, "flux", githubArgs.owner+"@users.noreply.github.com")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ghSSHHostname != "" {
|
if githubArgs.sshHostname != "" {
|
||||||
repository.SSHHost = ghSSHHostname
|
repository.SSHHost = githubArgs.sshHostname
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := &git.GithubProvider{
|
provider := &git.GithubProvider{
|
||||||
IsPrivate: ghPrivate,
|
IsPrivate: githubArgs.private,
|
||||||
IsPersonal: ghPersonal,
|
IsPersonal: githubArgs.personal,
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", namespace)
|
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
if ghDelete {
|
if githubArgs.delete {
|
||||||
if err := provider.DeleteRepository(ctx, repository); err != nil {
|
if err := provider.DeleteRepository(ctx, repository); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -158,7 +160,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create GitHub repository if doesn't exists
|
// create GitHub repository if doesn't exists
|
||||||
logger.Actionf("connecting to %s", ghHostname)
|
logger.Actionf("connecting to %s", githubArgs.hostname)
|
||||||
changed, err := provider.CreateRepository(ctx, repository)
|
changed, err := provider.CreateRepository(ctx, repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -169,8 +171,8 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
withErrors := false
|
withErrors := false
|
||||||
// add teams to org repository
|
// add teams to org repository
|
||||||
if !ghPersonal {
|
if !githubArgs.personal {
|
||||||
for _, team := range ghTeams {
|
for _, team := range githubArgs.teams {
|
||||||
if changed, err := provider.AddTeam(ctx, repository, team, ghDefaultPermission); err != nil {
|
if changed, err := provider.AddTeam(ctx, repository, team, ghDefaultPermission); err != nil {
|
||||||
logger.Failuref(err.Error())
|
logger.Failuref(err.Error())
|
||||||
withErrors = true
|
withErrors = true
|
||||||
@@ -181,20 +183,20 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clone repository and checkout the main branch
|
// clone repository and checkout the main branch
|
||||||
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
|
if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("repository cloned")
|
logger.Successf("repository cloned")
|
||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logger.Generatef("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
installManifest, err := generateInstallManifests(ghPath.String(), namespace, tmpDir, bootstrapManifestsPath)
|
installManifest, err := generateInstallManifests(githubArgs.path.String(), rootArgs.namespace, tmpDir, bootstrapArgs.manifestsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage install manifests
|
// stage install manifests
|
||||||
changed, err = repository.Commit(ctx, path.Join(ghPath.String(), namespace), "Add manifests")
|
changed, err = repository.Commit(ctx, path.Join(githubArgs.path.String(), rootArgs.namespace), "Add manifests")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -210,11 +212,11 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// determine if repo synchronization is working
|
// determine if repo synchronization is working
|
||||||
isInstall := shouldInstallManifests(ctx, kubeClient, namespace)
|
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace)
|
||||||
|
|
||||||
if isInstall {
|
if isInstall {
|
||||||
// apply install manifests
|
// apply install manifests
|
||||||
logger.Actionf("installing components in %s namespace", namespace)
|
logger.Actionf("installing components in %s namespace", rootArgs.namespace)
|
||||||
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -223,12 +225,12 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
repoURL := repository.GetURL()
|
repoURL := repository.GetURL()
|
||||||
|
|
||||||
if bootstrapTokenAuth {
|
if bootstrapArgs.tokenAuth {
|
||||||
// setup HTTPS token auth
|
// setup HTTPS token auth
|
||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: namespace,
|
Name: rootArgs.namespace,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{
|
StringData: map[string]string{
|
||||||
"username": "git",
|
"username": "git",
|
||||||
@@ -241,21 +243,21 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
// setup SSH deploy key
|
// setup SSH deploy key
|
||||||
repoURL = repository.GetSSH()
|
repoURL = repository.GetSSH()
|
||||||
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
|
if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
|
||||||
logger.Actionf("configuring deploy key")
|
logger.Actionf("configuring deploy key")
|
||||||
u, err := url.Parse(repository.GetSSH())
|
u, err := url.Parse(repository.GetSSH())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
return fmt.Errorf("git URL parse failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
|
key, err := generateDeployKey(ctx, kubeClient, u, rootArgs.namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("generating deploy key failed: %w", err)
|
return fmt.Errorf("generating deploy key failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
keyName := "flux"
|
keyName := "flux"
|
||||||
if ghPath != "" {
|
if githubArgs.path != "" {
|
||||||
keyName = fmt.Sprintf("flux-%s", ghPath)
|
keyName = fmt.Sprintf("flux-%s", githubArgs.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
||||||
@@ -268,13 +270,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// configure repo synchronization
|
// configure repo synchronization
|
||||||
logger.Actionf("generating sync manifests")
|
logger.Actionf("generating sync manifests")
|
||||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, filepath.ToSlash(ghPath.String()), tmpDir, ghInterval)
|
syncManifests, err := generateSyncManifests(repoURL, bootstrapArgs.branch, rootArgs.namespace, rootArgs.namespace, filepath.ToSlash(githubArgs.path.String()), tmpDir, githubArgs.interval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// commit and push manifests
|
||||||
if changed, err = repository.Commit(ctx, path.Join(ghPath.String(), namespace), "Add manifests"); err != nil {
|
if changed, err = repository.Commit(ctx, path.Join(githubArgs.path.String(), rootArgs.namespace), "Add manifests"); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
@@ -285,7 +287,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// apply manifests and waiting for sync
|
// apply manifests and waiting for sync
|
||||||
logger.Actionf("applying sync manifests")
|
logger.Actionf("applying sync manifests")
|
||||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
|
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,26 +73,28 @@ const (
|
|||||||
gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z`
|
gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type gitlabFlags struct {
|
||||||
glOwner string
|
owner string
|
||||||
glRepository string
|
repository string
|
||||||
glInterval time.Duration
|
interval time.Duration
|
||||||
glPersonal bool
|
personal bool
|
||||||
glPrivate bool
|
private bool
|
||||||
glHostname string
|
hostname string
|
||||||
glSSHHostname string
|
sshHostname string
|
||||||
glPath flags.SafeRelativePath
|
path flags.SafeRelativePath
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var gitlabArgs gitlabFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&glOwner, "owner", "", "GitLab user or group name")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&glRepository, "repository", "", "GitLab repository name")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name")
|
||||||
bootstrapGitLabCmd.Flags().BoolVar(&glPersonal, "personal", false, "is personal repository")
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.personal, "personal", false, "is personal repository")
|
||||||
bootstrapGitLabCmd.Flags().BoolVar(&glPrivate, "private", true, "is private repository")
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.private, "private", true, "is private repository")
|
||||||
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
|
bootstrapGitLabCmd.Flags().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.sshHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||||
bootstrapGitLabCmd.Flags().Var(&glPath, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
||||||
}
|
}
|
||||||
@@ -103,54 +105,54 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("%s environment variable not found", git.GitLabTokenName)
|
return fmt.Errorf("%s environment variable not found", git.GitLabTokenName)
|
||||||
}
|
}
|
||||||
|
|
||||||
projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, glRepository)
|
projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, gitlabArgs.repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !projectNameIsValid {
|
if !projectNameIsValid {
|
||||||
return fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", glRepository)
|
return fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", gitlabArgs.repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bootstrapValidate(); err != nil {
|
if err := bootstrapValidate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, namespace, filepath.ToSlash(glPath.String()))
|
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String()))
|
||||||
|
|
||||||
if bootstrapPathDiffers {
|
if bootstrapPathDiffers {
|
||||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
repository, err := git.NewRepository(glRepository, glOwner, glHostname, glToken, "flux", glOwner+"@users.noreply.gitlab.com")
|
repository, err := git.NewRepository(gitlabArgs.repository, gitlabArgs.owner, gitlabArgs.hostname, glToken, "flux", gitlabArgs.owner+"@users.noreply.gitlab.com")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if glSSHHostname != "" {
|
if gitlabArgs.sshHostname != "" {
|
||||||
repository.SSHHost = glSSHHostname
|
repository.SSHHost = gitlabArgs.sshHostname
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", namespace)
|
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
provider := &git.GitLabProvider{
|
provider := &git.GitLabProvider{
|
||||||
IsPrivate: glPrivate,
|
IsPrivate: gitlabArgs.private,
|
||||||
IsPersonal: glPersonal,
|
IsPersonal: gitlabArgs.personal,
|
||||||
}
|
}
|
||||||
|
|
||||||
// create GitLab project if doesn't exists
|
// create GitLab project if doesn't exists
|
||||||
logger.Actionf("connecting to %s", glHostname)
|
logger.Actionf("connecting to %s", gitlabArgs.hostname)
|
||||||
changed, err := provider.CreateRepository(ctx, repository)
|
changed, err := provider.CreateRepository(ctx, repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -160,20 +162,20 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clone repository and checkout the master branch
|
// clone repository and checkout the master branch
|
||||||
if err := repository.Checkout(ctx, bootstrapBranch, tmpDir); err != nil {
|
if err := repository.Checkout(ctx, bootstrapArgs.branch, tmpDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("repository cloned")
|
logger.Successf("repository cloned")
|
||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logger.Generatef("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
installManifest, err := generateInstallManifests(glPath.String(), namespace, tmpDir, bootstrapManifestsPath)
|
installManifest, err := generateInstallManifests(gitlabArgs.path.String(), rootArgs.namespace, tmpDir, bootstrapArgs.manifestsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage install manifests
|
// stage install manifests
|
||||||
changed, err = repository.Commit(ctx, path.Join(glPath.String(), namespace), "Add manifests")
|
changed, err = repository.Commit(ctx, path.Join(gitlabArgs.path.String(), rootArgs.namespace), "Add manifests")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -189,11 +191,11 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// determine if repo synchronization is working
|
// determine if repo synchronization is working
|
||||||
isInstall := shouldInstallManifests(ctx, kubeClient, namespace)
|
isInstall := shouldInstallManifests(ctx, kubeClient, rootArgs.namespace)
|
||||||
|
|
||||||
if isInstall {
|
if isInstall {
|
||||||
// apply install manifests
|
// apply install manifests
|
||||||
logger.Actionf("installing components in %s namespace", namespace)
|
logger.Actionf("installing components in %s namespace", rootArgs.namespace)
|
||||||
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -202,12 +204,12 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
repoURL := repository.GetURL()
|
repoURL := repository.GetURL()
|
||||||
|
|
||||||
if bootstrapTokenAuth {
|
if bootstrapArgs.tokenAuth {
|
||||||
// setup HTTPS token auth
|
// setup HTTPS token auth
|
||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: namespace,
|
Name: rootArgs.namespace,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{
|
StringData: map[string]string{
|
||||||
"username": "git",
|
"username": "git",
|
||||||
@@ -220,21 +222,21 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
// setup SSH deploy key
|
// setup SSH deploy key
|
||||||
repoURL = repository.GetSSH()
|
repoURL = repository.GetSSH()
|
||||||
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
|
if shouldCreateDeployKey(ctx, kubeClient, rootArgs.namespace) {
|
||||||
logger.Actionf("configuring deploy key")
|
logger.Actionf("configuring deploy key")
|
||||||
u, err := url.Parse(repoURL)
|
u, err := url.Parse(repoURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
return fmt.Errorf("git URL parse failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
|
key, err := generateDeployKey(ctx, kubeClient, u, rootArgs.namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("generating deploy key failed: %w", err)
|
return fmt.Errorf("generating deploy key failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
keyName := "flux"
|
keyName := "flux"
|
||||||
if glPath != "" {
|
if gitlabArgs.path != "" {
|
||||||
keyName = fmt.Sprintf("flux-%s", glPath)
|
keyName = fmt.Sprintf("flux-%s", gitlabArgs.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
|
||||||
@@ -247,13 +249,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// configure repo synchronization
|
// configure repo synchronization
|
||||||
logger.Actionf("generating sync manifests")
|
logger.Actionf("generating sync manifests")
|
||||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, filepath.ToSlash(glPath.String()), tmpDir, glInterval)
|
syncManifests, err := generateSyncManifests(repoURL, bootstrapArgs.branch, rootArgs.namespace, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String()), tmpDir, gitlabArgs.interval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// commit and push manifests
|
||||||
if changed, err = repository.Commit(ctx, path.Join(glPath.String(), namespace), "Add manifests"); err != nil {
|
if changed, err = repository.Commit(ctx, path.Join(gitlabArgs.path.String(), rootArgs.namespace), "Add manifests"); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
@@ -264,7 +266,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// apply manifests and waiting for sync
|
// apply manifests and waiting for sync
|
||||||
logger.Actionf("applying sync manifests")
|
logger.Actionf("applying sync manifests")
|
||||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
|
if err := applySyncManifests(ctx, kubeClient, rootArgs.namespace, rootArgs.namespace, syncManifests); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,25 +44,27 @@ the local environment is configured correctly and if the installed components ar
|
|||||||
RunE: runCheckCmd,
|
RunE: runCheckCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type checkFlags struct {
|
||||||
checkPre bool
|
pre bool
|
||||||
checkComponents []string
|
components []string
|
||||||
)
|
}
|
||||||
|
|
||||||
type kubectlVersion struct {
|
type kubectlVersion struct {
|
||||||
ClientVersion *apimachineryversion.Info `json:"clientVersion"`
|
ClientVersion *apimachineryversion.Info `json:"clientVersion"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var checkArgs checkFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
checkCmd.Flags().BoolVarP(&checkPre, "pre", "", false,
|
checkCmd.Flags().BoolVarP(&checkArgs.pre, "pre", "", false,
|
||||||
"only run pre-installation checks")
|
"only run pre-installation checks")
|
||||||
checkCmd.Flags().StringSliceVar(&checkComponents, "components", defaults.Components,
|
checkCmd.Flags().StringSliceVar(&checkArgs.components, "components", rootArgs.defaults.Components,
|
||||||
"list of components, accepts comma-separated values")
|
"list of components, accepts comma-separated values")
|
||||||
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(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
logger.Actionf("checking prerequisites")
|
logger.Actionf("checking prerequisites")
|
||||||
@@ -76,7 +78,7 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
|||||||
checkFailed = true
|
checkFailed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if checkPre {
|
if checkArgs.pre {
|
||||||
if checkFailed {
|
if checkFailed {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@@ -103,7 +105,7 @@ func kubectlCheck(ctx context.Context, version string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kubectlArgs := []string{"version", "--client", "--output", "json"}
|
kubectlArgs := []string{"version", "--client", "--output", "json"}
|
||||||
output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...)
|
output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Failuref("kubectl version can't be determined")
|
logger.Failuref("kubectl version can't be determined")
|
||||||
return false
|
return false
|
||||||
@@ -132,7 +134,7 @@ func kubectlCheck(ctx context.Context, version string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func kubernetesCheck(version string) bool {
|
func kubernetesCheck(version string) bool {
|
||||||
cfg, err := utils.KubeConfig(kubeconfig, kubecontext)
|
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
|
||||||
@@ -167,20 +169,20 @@ func kubernetesCheck(version string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func componentsCheck() bool {
|
func componentsCheck() bool {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
ok := true
|
ok := true
|
||||||
for _, deployment := range checkComponents {
|
for _, deployment := range checkArgs.components {
|
||||||
kubectlArgs := []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
|
kubectlArgs := []string{"-n", rootArgs.namespace, "rollout", "status", "deployment", deployment, "--timeout", rootArgs.timeout.String()}
|
||||||
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||||
logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
|
logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
|
||||||
ok = false
|
ok = false
|
||||||
} else {
|
} else {
|
||||||
logger.Successf("%s is healthy", deployment)
|
logger.Successf("%s is healthy", deployment)
|
||||||
}
|
}
|
||||||
kubectlArgs = []string{"-n", namespace, "get", "deployment", deployment, "-o", "jsonpath=\"{..image}\""}
|
kubectlArgs = []string{"-n", rootArgs.namespace, "get", "deployment", deployment, "-o", "jsonpath=\"{..image}\""}
|
||||||
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubeconfig, kubecontext, kubectlArgs...); err == nil {
|
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err == nil {
|
||||||
logger.Actionf(strings.TrimPrefix(strings.TrimSuffix(output, "\""), "\""))
|
logger.Actionf(strings.TrimPrefix(strings.TrimSuffix(output, "\""), "\""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,16 +38,18 @@ var createCmd = &cobra.Command{
|
|||||||
Long: "The create sub-commands generate sources and resources.",
|
Long: "The create sub-commands generate sources and resources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type createFlags struct {
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
export bool
|
export bool
|
||||||
labels []string
|
labels []string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var createArgs createFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createCmd.PersistentFlags().DurationVarP(&interval, "interval", "", time.Minute, "source sync interval")
|
createCmd.PersistentFlags().DurationVarP(&createArgs.interval, "interval", "", time.Minute, "source sync interval")
|
||||||
createCmd.PersistentFlags().BoolVar(&export, "export", false, "export in YAML format to stdout")
|
createCmd.PersistentFlags().BoolVar(&createArgs.export, "export", false, "export in YAML format to stdout")
|
||||||
createCmd.PersistentFlags().StringSliceVar(&labels, "label", nil,
|
createCmd.PersistentFlags().StringSliceVar(&createArgs.labels, "label", nil,
|
||||||
"set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)")
|
"set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)")
|
||||||
rootCmd.AddCommand(createCmd)
|
rootCmd.AddCommand(createCmd)
|
||||||
}
|
}
|
||||||
@@ -99,10 +101,10 @@ type upsertWaitable interface {
|
|||||||
// resource, then waiting for it to reconcile. See the note on
|
// resource, then waiting for it to reconcile. See the note on
|
||||||
// `upsert` for how to work with the `mutate` argument.
|
// `upsert` for how to work with the `mutate` argument.
|
||||||
func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) error {
|
func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) // NB globals
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) // NB globals
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -116,7 +118,7 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for %s reconciliation", names.kind)
|
logger.Waitingf("waiting for %s reconciliation", names.kind)
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isReady(ctx, kubeClient, namespacedName, object)); err != nil {
|
isReady(ctx, kubeClient, namespacedName, object)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -126,7 +128,7 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
|
|||||||
|
|
||||||
func parseLabels() (map[string]string, error) {
|
func parseLabels() (map[string]string, error) {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
for _, label := range labels {
|
for _, label := range createArgs.labels {
|
||||||
// validate key value pair
|
// validate key value pair
|
||||||
parts := strings.Split(label, "=")
|
parts := strings.Split(label, "=")
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
|
|||||||
@@ -20,11 +20,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
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"
|
||||||
@@ -33,6 +29,9 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createAlertCmd = &cobra.Command{
|
var createAlertCmd = &cobra.Command{
|
||||||
@@ -49,16 +48,18 @@ var createAlertCmd = &cobra.Command{
|
|||||||
RunE: createAlertCmdRun,
|
RunE: createAlertCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type alertFlags struct {
|
||||||
aProviderRef string
|
providerRef string
|
||||||
aEventSeverity string
|
eventSeverity string
|
||||||
aEventSources []string
|
eventSources []string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var alertArgs alertFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createAlertCmd.Flags().StringVar(&aProviderRef, "provider-ref", "", "reference to provider")
|
createAlertCmd.Flags().StringVar(&alertArgs.providerRef, "provider-ref", "", "reference to provider")
|
||||||
createAlertCmd.Flags().StringVar(&aEventSeverity, "event-severity", "", "severity of events to send alerts for")
|
createAlertCmd.Flags().StringVar(&alertArgs.eventSeverity, "event-severity", "", "severity of events to send alerts for")
|
||||||
createAlertCmd.Flags().StringArrayVar(&aEventSources, "event-source", []string{}, "sources that should generate alerts (<kind>/<name>)")
|
createAlertCmd.Flags().StringArrayVar(&alertArgs.eventSources, "event-source", []string{}, "sources that should generate alerts (<kind>/<name>)")
|
||||||
createCmd.AddCommand(createAlertCmd)
|
createCmd.AddCommand(createAlertCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,12 +69,12 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if aProviderRef == "" {
|
if alertArgs.providerRef == "" {
|
||||||
return fmt.Errorf("provider ref is required")
|
return fmt.Errorf("provider ref is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
eventSources := []notificationv1.CrossNamespaceObjectReference{}
|
eventSources := []notificationv1.CrossNamespaceObjectReference{}
|
||||||
for _, eventSource := range aEventSources {
|
for _, eventSource := range alertArgs.eventSources {
|
||||||
kind, name := utils.ParseObjectKindName(eventSource)
|
kind, name := utils.ParseObjectKindName(eventSource)
|
||||||
if kind == "" {
|
if kind == "" {
|
||||||
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
|
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
|
||||||
@@ -94,34 +95,34 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !export {
|
if !createArgs.export {
|
||||||
logger.Generatef("generating Alert")
|
logger.Generatef("generating Alert")
|
||||||
}
|
}
|
||||||
|
|
||||||
alert := notificationv1.Alert{
|
alert := notificationv1.Alert{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: notificationv1.AlertSpec{
|
Spec: notificationv1.AlertSpec{
|
||||||
ProviderRef: corev1.LocalObjectReference{
|
ProviderRef: meta.LocalObjectReference{
|
||||||
Name: aProviderRef,
|
Name: alertArgs.providerRef,
|
||||||
},
|
},
|
||||||
EventSeverity: aEventSeverity,
|
EventSeverity: alertArgs.eventSeverity,
|
||||||
EventSources: eventSources,
|
EventSources: eventSources,
|
||||||
Suspend: false,
|
Suspend: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return exportAlert(alert)
|
return exportAlert(alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -133,7 +134,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Alert reconciliation")
|
logger.Waitingf("waiting for Alert reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
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"
|
||||||
@@ -29,9 +28,10 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createAlertProviderCmd = &cobra.Command{
|
var createAlertProviderCmd = &cobra.Command{
|
||||||
@@ -54,20 +54,22 @@ var createAlertProviderCmd = &cobra.Command{
|
|||||||
RunE: createAlertProviderCmdRun,
|
RunE: createAlertProviderCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type alertProviderFlags struct {
|
||||||
apType string
|
alertType string
|
||||||
apChannel string
|
channel string
|
||||||
apUsername string
|
username string
|
||||||
apAddress string
|
address string
|
||||||
apSecretRef string
|
secretRef string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var alertProviderArgs alertProviderFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createAlertProviderCmd.Flags().StringVar(&apType, "type", "", "type of provider")
|
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.alertType, "type", "", "type of provider")
|
||||||
createAlertProviderCmd.Flags().StringVar(&apChannel, "channel", "", "channel to send messages to in the case of a chat provider")
|
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.channel, "channel", "", "channel to send messages to in the case of a chat provider")
|
||||||
createAlertProviderCmd.Flags().StringVar(&apUsername, "username", "", "bot username used by the provider")
|
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.username, "username", "", "bot username used by the provider")
|
||||||
createAlertProviderCmd.Flags().StringVar(&apAddress, "address", "", "path to either the git repository, chat provider or webhook")
|
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.address, "address", "", "path to either the git repository, chat provider or webhook")
|
||||||
createAlertProviderCmd.Flags().StringVar(&apSecretRef, "secret-ref", "", "name of secret containing authentication token")
|
createAlertProviderCmd.Flags().StringVar(&alertProviderArgs.secretRef, "secret-ref", "", "name of secret containing authentication token")
|
||||||
createCmd.AddCommand(createAlertProviderCmd)
|
createCmd.AddCommand(createAlertProviderCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +79,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if apType == "" {
|
if alertProviderArgs.alertType == "" {
|
||||||
return fmt.Errorf("Provider type is required")
|
return fmt.Errorf("Provider type is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,38 +88,38 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !export {
|
if !createArgs.export {
|
||||||
logger.Generatef("generating Provider")
|
logger.Generatef("generating Provider")
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := notificationv1.Provider{
|
provider := notificationv1.Provider{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: notificationv1.ProviderSpec{
|
Spec: notificationv1.ProviderSpec{
|
||||||
Type: apType,
|
Type: alertProviderArgs.alertType,
|
||||||
Channel: apChannel,
|
Channel: alertProviderArgs.channel,
|
||||||
Username: apUsername,
|
Username: alertProviderArgs.username,
|
||||||
Address: apAddress,
|
Address: alertProviderArgs.address,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if apSecretRef != "" {
|
if alertProviderArgs.secretRef != "" {
|
||||||
provider.Spec.SecretRef = &corev1.LocalObjectReference{
|
provider.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: apSecretRef,
|
Name: alertProviderArgs.secretRef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return exportAlertProvider(provider)
|
return exportAlertProvider(provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -129,7 +131,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Provider reconciliation")
|
logger.Waitingf("waiting for Provider reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
|
isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,28 +98,30 @@ var createHelmReleaseCmd = &cobra.Command{
|
|||||||
RunE: createHelmReleaseCmdRun,
|
RunE: createHelmReleaseCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type helmReleaseFlags struct {
|
||||||
hrName string
|
name string
|
||||||
hrSource flags.HelmChartSource
|
source flags.HelmChartSource
|
||||||
hrDependsOn []string
|
dependsOn []string
|
||||||
hrChart string
|
chart string
|
||||||
hrChartVersion string
|
chartVersion string
|
||||||
hrTargetNamespace string
|
targetNamespace string
|
||||||
hrValuesFile string
|
valuesFile string
|
||||||
hrValuesFrom flags.HelmReleaseValuesFrom
|
valuesFrom flags.HelmReleaseValuesFrom
|
||||||
hrSAName string
|
saName string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var helmReleaseArgs helmReleaseFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.name, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
|
||||||
createHelmReleaseCmd.Flags().Var(&hrSource, "source", hrSource.Description())
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.source, "source", helmReleaseArgs.source.Description())
|
||||||
createHelmReleaseCmd.Flags().StringVar(&hrChart, "chart", "", "Helm chart name or path")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chart, "chart", "", "Helm chart name or path")
|
||||||
createHelmReleaseCmd.Flags().StringVar(&hrChartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
|
||||||
createHelmReleaseCmd.Flags().StringArrayVar(&hrDependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
|
createHelmReleaseCmd.Flags().StringArrayVar(&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(&hrTargetNamespace, "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().StringVar(&hrSAName, "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().StringVar(&hrValuesFile, "values", "", "local path to the values.yaml file")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.valuesFile, "values", "", "local path to the values.yaml file")
|
||||||
createHelmReleaseCmd.Flags().Var(&hrValuesFrom, "values-from", hrValuesFrom.Description())
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
|
||||||
createCmd.AddCommand(createHelmReleaseCmd)
|
createCmd.AddCommand(createHelmReleaseCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +131,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if hrChart == "" {
|
if helmReleaseArgs.chart == "" {
|
||||||
return fmt.Errorf("chart name or path is required")
|
return fmt.Errorf("chart name or path is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,30 +140,30 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !export {
|
if !createArgs.export {
|
||||||
logger.Generatef("generating HelmRelease")
|
logger.Generatef("generating HelmRelease")
|
||||||
}
|
}
|
||||||
|
|
||||||
helmRelease := helmv2.HelmRelease{
|
helmRelease := helmv2.HelmRelease{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: helmv2.HelmReleaseSpec{
|
Spec: helmv2.HelmReleaseSpec{
|
||||||
ReleaseName: hrName,
|
ReleaseName: helmReleaseArgs.name,
|
||||||
DependsOn: utils.MakeDependsOn(hrDependsOn),
|
DependsOn: utils.MakeDependsOn(helmReleaseArgs.dependsOn),
|
||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
TargetNamespace: hrTargetNamespace,
|
TargetNamespace: helmReleaseArgs.targetNamespace,
|
||||||
Chart: helmv2.HelmChartTemplate{
|
Chart: helmv2.HelmChartTemplate{
|
||||||
Spec: helmv2.HelmChartTemplateSpec{
|
Spec: helmv2.HelmChartTemplateSpec{
|
||||||
Chart: hrChart,
|
Chart: helmReleaseArgs.chart,
|
||||||
Version: hrChartVersion,
|
Version: helmReleaseArgs.chartVersion,
|
||||||
SourceRef: helmv2.CrossNamespaceObjectReference{
|
SourceRef: helmv2.CrossNamespaceObjectReference{
|
||||||
Kind: hrSource.Kind,
|
Kind: helmReleaseArgs.source.Kind,
|
||||||
Name: hrSource.Name,
|
Name: helmReleaseArgs.source.Name,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -169,39 +171,39 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if hrSAName != "" {
|
if helmReleaseArgs.saName != "" {
|
||||||
helmRelease.Spec.ServiceAccountName = hrSAName
|
helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
|
||||||
}
|
}
|
||||||
|
|
||||||
if hrValuesFile != "" {
|
if helmReleaseArgs.valuesFile != "" {
|
||||||
data, err := ioutil.ReadFile(hrValuesFile)
|
data, err := ioutil.ReadFile(helmReleaseArgs.valuesFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("reading values from %s failed: %w", hrValuesFile, err)
|
return fmt.Errorf("reading values from %s failed: %w", helmReleaseArgs.valuesFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
json, err := yaml.YAMLToJSON(data)
|
json, err := yaml.YAMLToJSON(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("converting values to JSON from %s failed: %w", hrValuesFile, err)
|
return fmt.Errorf("converting values to JSON from %s failed: %w", helmReleaseArgs.valuesFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
helmRelease.Spec.Values = &apiextensionsv1.JSON{Raw: json}
|
helmRelease.Spec.Values = &apiextensionsv1.JSON{Raw: json}
|
||||||
}
|
}
|
||||||
|
|
||||||
if hrValuesFrom.String() != "" {
|
if helmReleaseArgs.valuesFrom.String() != "" {
|
||||||
helmRelease.Spec.ValuesFrom = []helmv2.ValuesReference{{
|
helmRelease.Spec.ValuesFrom = []helmv2.ValuesReference{{
|
||||||
Kind: hrValuesFrom.Kind,
|
Kind: helmReleaseArgs.valuesFrom.Kind,
|
||||||
Name: hrValuesFrom.Name,
|
Name: helmReleaseArgs.valuesFrom.Name,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return exportHelmRelease(helmRelease)
|
return exportHelmRelease(helmRelease)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -213,7 +215,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmRelease reconciliation")
|
logger.Waitingf("waiting for HelmRelease reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
|
isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -78,11 +79,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: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Spec: imagev1.ImagePolicySpec{
|
Spec: imagev1.ImagePolicySpec{
|
||||||
ImageRepositoryRef: corev1.LocalObjectReference{
|
ImageRepositoryRef: meta.LocalObjectReference{
|
||||||
Name: imagePolicyArgs.imageRef,
|
Name: imagePolicyArgs.imageRef,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -103,7 +104,7 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return printExport(exportImagePolicy(&policy))
|
return printExport(exportImagePolicy(&policy))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,24 +78,24 @@ 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: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Spec: imagev1.ImageRepositorySpec{
|
Spec: imagev1.ImageRepositorySpec{
|
||||||
Image: imageRepoArgs.image,
|
Image: imageRepoArgs.image,
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: createArgs.interval},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if imageRepoArgs.timeout != 0 {
|
if imageRepoArgs.timeout != 0 {
|
||||||
repo.Spec.Timeout = &metav1.Duration{Duration: imageRepoArgs.timeout}
|
repo.Spec.Timeout = &metav1.Duration{Duration: imageRepoArgs.timeout}
|
||||||
}
|
}
|
||||||
if imageRepoArgs.secretRef != "" {
|
if imageRepoArgs.secretRef != "" {
|
||||||
repo.Spec.SecretRef = &corev1.LocalObjectReference{
|
repo.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: imageRepoArgs.secretRef,
|
Name: imageRepoArgs.secretRef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return printExport(exportImageRepository(&repo))
|
return printExport(exportImageRepository(&repo))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,20 +81,17 @@ 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: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Spec: autov1.ImageUpdateAutomationSpec{
|
Spec: autov1.ImageUpdateAutomationSpec{
|
||||||
Checkout: autov1.GitCheckoutSpec{
|
Checkout: autov1.GitCheckoutSpec{
|
||||||
GitRepositoryRef: corev1.LocalObjectReference{
|
GitRepositoryRef: meta.LocalObjectReference{
|
||||||
Name: imageUpdateArgs.gitRepoRef,
|
Name: imageUpdateArgs.gitRepoRef,
|
||||||
},
|
},
|
||||||
Branch: imageUpdateArgs.branch,
|
Branch: imageUpdateArgs.branch,
|
||||||
},
|
},
|
||||||
Interval: metav1.Duration{Duration: interval},
|
Interval: metav1.Duration{Duration: createArgs.interval},
|
||||||
Update: autov1.UpdateStrategy{
|
|
||||||
Setters: &autov1.SettersStrategy{},
|
|
||||||
},
|
|
||||||
Commit: autov1.CommitSpec{
|
Commit: autov1.CommitSpec{
|
||||||
AuthorName: imageUpdateArgs.authorName,
|
AuthorName: imageUpdateArgs.authorName,
|
||||||
AuthorEmail: imageUpdateArgs.authorEmail,
|
AuthorEmail: imageUpdateArgs.authorEmail,
|
||||||
@@ -102,7 +100,7 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return printExport(exportImageUpdate(&update))
|
return printExport(exportImageUpdate(&update))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
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"
|
||||||
@@ -31,11 +30,12 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createKsCmd = &cobra.Command{
|
var createKsCmd = &cobra.Command{
|
||||||
@@ -72,53 +72,61 @@ var createKsCmd = &cobra.Command{
|
|||||||
RunE: createKsCmdRun,
|
RunE: createKsCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type kustomizationFlags struct {
|
||||||
ksSource flags.KustomizationSource
|
source flags.KustomizationSource
|
||||||
ksPath flags.SafeRelativePath = "./"
|
path flags.SafeRelativePath
|
||||||
ksPrune bool
|
prune bool
|
||||||
ksDependsOn []string
|
dependsOn []string
|
||||||
ksValidation string
|
validation string
|
||||||
ksHealthCheck []string
|
healthCheck []string
|
||||||
ksHealthTimeout time.Duration
|
healthTimeout time.Duration
|
||||||
ksSAName string
|
saName string
|
||||||
ksDecryptionProvider flags.DecryptionProvider
|
decryptionProvider flags.DecryptionProvider
|
||||||
ksDecryptionSecret string
|
decryptionSecret string
|
||||||
ksTargetNamespace string
|
targetNamespace string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var kustomizationArgs = NewKustomizationFlags()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createKsCmd.Flags().Var(&ksSource, "source", ksSource.Description())
|
createKsCmd.Flags().Var(&kustomizationArgs.source, "source", kustomizationArgs.source.Description())
|
||||||
createKsCmd.Flags().Var(&ksPath, "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(&ksPrune, "prune", false, "enable garbage collection")
|
createKsCmd.Flags().BoolVar(&kustomizationArgs.prune, "prune", false, "enable garbage collection")
|
||||||
createKsCmd.Flags().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
createKsCmd.Flags().StringArrayVar(&kustomizationArgs.healthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
||||||
createKsCmd.Flags().DurationVar(&ksHealthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
createKsCmd.Flags().DurationVar(&kustomizationArgs.healthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
||||||
createKsCmd.Flags().StringVar(&ksValidation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.validation, "validation", "", "validate the manifests before applying them on the cluster, can be 'client' or 'server'")
|
||||||
createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'")
|
createKsCmd.Flags().StringArrayVar(&kustomizationArgs.dependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'")
|
||||||
createKsCmd.Flags().StringVar(&ksSAName, "service-account", "", "the name of the service account to impersonate when reconciling this Kustomization")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this Kustomization")
|
||||||
createKsCmd.Flags().Var(&ksDecryptionProvider, "decryption-provider", ksDecryptionProvider.Description())
|
createKsCmd.Flags().Var(&kustomizationArgs.decryptionProvider, "decryption-provider", kustomizationArgs.decryptionProvider.Description())
|
||||||
createKsCmd.Flags().StringVar(&ksDecryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.decryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
|
||||||
createKsCmd.Flags().StringVar(&ksTargetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.targetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
|
||||||
createCmd.AddCommand(createKsCmd)
|
createCmd.AddCommand(createKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewKustomizationFlags() kustomizationFlags {
|
||||||
|
return kustomizationFlags{
|
||||||
|
path: "./",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("Kustomization name is required")
|
return fmt.Errorf("Kustomization name is required")
|
||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if ksPath == "" {
|
if kustomizationArgs.path == "" {
|
||||||
return fmt.Errorf("path is required")
|
return fmt.Errorf("path is required")
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(ksPath.String(), "./") {
|
if !strings.HasPrefix(kustomizationArgs.path.String(), "./") {
|
||||||
return fmt.Errorf("path must begin with ./")
|
return fmt.Errorf("path must begin with ./")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !export {
|
if !createArgs.export {
|
||||||
logger.Generatef("generating Kustomization")
|
logger.Generatef("generating Kustomization")
|
||||||
}
|
}
|
||||||
|
|
||||||
ksLabels, err := parseLabels()
|
kslabels, err := parseLabels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -126,29 +134,29 @@ 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: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: ksLabels,
|
Labels: kslabels,
|
||||||
},
|
},
|
||||||
Spec: kustomizev1.KustomizationSpec{
|
Spec: kustomizev1.KustomizationSpec{
|
||||||
DependsOn: utils.MakeDependsOn(ksDependsOn),
|
DependsOn: utils.MakeDependsOn(kustomizationArgs.dependsOn),
|
||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
Path: ksPath.String(),
|
Path: kustomizationArgs.path.String(),
|
||||||
Prune: ksPrune,
|
Prune: kustomizationArgs.prune,
|
||||||
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
||||||
Kind: ksSource.Kind,
|
Kind: kustomizationArgs.source.Kind,
|
||||||
Name: ksSource.Name,
|
Name: kustomizationArgs.source.Name,
|
||||||
},
|
},
|
||||||
Suspend: false,
|
Suspend: false,
|
||||||
Validation: ksValidation,
|
Validation: kustomizationArgs.validation,
|
||||||
TargetNamespace: ksTargetNamespace,
|
TargetNamespace: kustomizationArgs.targetNamespace,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ksHealthCheck) > 0 {
|
if len(kustomizationArgs.healthCheck) > 0 {
|
||||||
healthChecks := make([]kustomizev1.CrossNamespaceObjectReference, 0)
|
healthChecks := make([]meta.NamespacedObjectKindReference, 0)
|
||||||
for _, w := range ksHealthCheck {
|
for _, w := range kustomizationArgs.healthCheck {
|
||||||
kindObj := strings.Split(w, "/")
|
kindObj := strings.Split(w, "/")
|
||||||
if len(kindObj) != 2 {
|
if len(kindObj) != 2 {
|
||||||
return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace' %v", w, kindObj)
|
return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace' %v", w, kindObj)
|
||||||
@@ -170,7 +178,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace'", w)
|
return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace'", w)
|
||||||
}
|
}
|
||||||
|
|
||||||
check := kustomizev1.CrossNamespaceObjectReference{
|
check := meta.NamespacedObjectKindReference{
|
||||||
Kind: kind,
|
Kind: kind,
|
||||||
Name: nameNs[0],
|
Name: nameNs[0],
|
||||||
Namespace: nameNs[1],
|
Namespace: nameNs[1],
|
||||||
@@ -183,32 +191,32 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
kustomization.Spec.HealthChecks = healthChecks
|
kustomization.Spec.HealthChecks = healthChecks
|
||||||
kustomization.Spec.Timeout = &metav1.Duration{
|
kustomization.Spec.Timeout = &metav1.Duration{
|
||||||
Duration: ksHealthTimeout,
|
Duration: kustomizationArgs.healthTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ksSAName != "" {
|
if kustomizationArgs.saName != "" {
|
||||||
kustomization.Spec.ServiceAccountName = ksSAName
|
kustomization.Spec.ServiceAccountName = kustomizationArgs.saName
|
||||||
}
|
}
|
||||||
|
|
||||||
if ksDecryptionProvider != "" {
|
if kustomizationArgs.decryptionProvider != "" {
|
||||||
kustomization.Spec.Decryption = &kustomizev1.Decryption{
|
kustomization.Spec.Decryption = &kustomizev1.Decryption{
|
||||||
Provider: ksDecryptionProvider.String(),
|
Provider: kustomizationArgs.decryptionProvider.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ksDecryptionSecret != "" {
|
if kustomizationArgs.decryptionSecret != "" {
|
||||||
kustomization.Spec.Decryption.SecretRef = &corev1.LocalObjectReference{Name: ksDecryptionSecret}
|
kustomization.Spec.Decryption.SecretRef = &meta.LocalObjectReference{Name: kustomizationArgs.decryptionSecret}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return exportKs(kustomization)
|
return exportKs(kustomization)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -220,7 +228,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Kustomization reconciliation")
|
logger.Waitingf("waiting for Kustomization reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
|
isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
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"
|
||||||
@@ -29,9 +28,10 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createReceiverCmd = &cobra.Command{
|
var createReceiverCmd = &cobra.Command{
|
||||||
@@ -50,18 +50,20 @@ var createReceiverCmd = &cobra.Command{
|
|||||||
RunE: createReceiverCmdRun,
|
RunE: createReceiverCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type receiverFlags struct {
|
||||||
rcvType string
|
receiverType string
|
||||||
rcvSecretRef string
|
secretRef string
|
||||||
rcvEvents []string
|
events []string
|
||||||
rcvResources []string
|
resources []string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var receiverArgs receiverFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createReceiverCmd.Flags().StringVar(&rcvType, "type", "", "")
|
createReceiverCmd.Flags().StringVar(&receiverArgs.receiverType, "type", "", "")
|
||||||
createReceiverCmd.Flags().StringVar(&rcvSecretRef, "secret-ref", "", "")
|
createReceiverCmd.Flags().StringVar(&receiverArgs.secretRef, "secret-ref", "", "")
|
||||||
createReceiverCmd.Flags().StringArrayVar(&rcvEvents, "event", []string{}, "")
|
createReceiverCmd.Flags().StringArrayVar(&receiverArgs.events, "event", []string{}, "")
|
||||||
createReceiverCmd.Flags().StringArrayVar(&rcvResources, "resource", []string{}, "")
|
createReceiverCmd.Flags().StringArrayVar(&receiverArgs.resources, "resource", []string{}, "")
|
||||||
createCmd.AddCommand(createReceiverCmd)
|
createCmd.AddCommand(createReceiverCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,16 +73,16 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if rcvType == "" {
|
if receiverArgs.receiverType == "" {
|
||||||
return fmt.Errorf("Receiver type is required")
|
return fmt.Errorf("Receiver type is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if rcvSecretRef == "" {
|
if receiverArgs.secretRef == "" {
|
||||||
return fmt.Errorf("secret ref is required")
|
return fmt.Errorf("secret ref is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
resources := []notificationv1.CrossNamespaceObjectReference{}
|
resources := []notificationv1.CrossNamespaceObjectReference{}
|
||||||
for _, resource := range rcvResources {
|
for _, resource := range receiverArgs.resources {
|
||||||
kind, name := utils.ParseObjectKindName(resource)
|
kind, name := utils.ParseObjectKindName(resource)
|
||||||
if kind == "" {
|
if kind == "" {
|
||||||
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", resource)
|
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", resource)
|
||||||
@@ -101,35 +103,35 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !export {
|
if !createArgs.export {
|
||||||
logger.Generatef("generating Receiver")
|
logger.Generatef("generating Receiver")
|
||||||
}
|
}
|
||||||
|
|
||||||
receiver := notificationv1.Receiver{
|
receiver := notificationv1.Receiver{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: notificationv1.ReceiverSpec{
|
Spec: notificationv1.ReceiverSpec{
|
||||||
Type: rcvType,
|
Type: receiverArgs.receiverType,
|
||||||
Events: rcvEvents,
|
Events: receiverArgs.events,
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
SecretRef: corev1.LocalObjectReference{
|
SecretRef: meta.LocalObjectReference{
|
||||||
Name: rcvSecretRef,
|
Name: receiverArgs.secretRef,
|
||||||
},
|
},
|
||||||
Suspend: false,
|
Suspend: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return exportReceiver(receiver)
|
return exportReceiver(receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -141,7 +143,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Receiver reconciliation")
|
logger.Waitingf("waiting for Receiver reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
|
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,23 @@ func init() {
|
|||||||
createCmd.AddCommand(createSecretCmd)
|
createCmd.AddCommand(createSecretCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeSecret(name string) (corev1.Secret, error) {
|
||||||
|
secretLabels, err := parseLabels()
|
||||||
|
if err != nil {
|
||||||
|
return corev1.Secret{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: rootArgs.namespace,
|
||||||
|
Labels: secretLabels,
|
||||||
|
},
|
||||||
|
StringData: map[string]string{},
|
||||||
|
Data: nil,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.Secret) error {
|
func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.Secret) error {
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: secret.GetNamespace(),
|
Namespace: secret.GetNamespace(),
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
@@ -71,60 +69,61 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
|
|||||||
RunE: createSecretGitCmdRun,
|
RunE: createSecretGitCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type secretGitFlags struct {
|
||||||
secretGitURL string
|
url string
|
||||||
secretGitUsername string
|
username string
|
||||||
secretGitPassword string
|
password string
|
||||||
secretGitKeyAlgorithm flags.PublicKeyAlgorithm = "rsa"
|
keyAlgorithm flags.PublicKeyAlgorithm
|
||||||
secretGitRSABits flags.RSAKeyBits = 2048
|
rsaBits flags.RSAKeyBits
|
||||||
secretGitECDSACurve = flags.ECDSACurve{Curve: elliptic.P384()}
|
ecdsaCurve flags.ECDSACurve
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var secretGitArgs = NewSecretGitFlags()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createSecretGitCmd.Flags().StringVar(&secretGitURL, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
createSecretGitCmd.Flags().StringVar(&secretGitArgs.url, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
||||||
createSecretGitCmd.Flags().StringVarP(&secretGitUsername, "username", "u", "", "basic authentication username")
|
createSecretGitCmd.Flags().StringVarP(&secretGitArgs.username, "username", "u", "", "basic authentication username")
|
||||||
createSecretGitCmd.Flags().StringVarP(&secretGitPassword, "password", "p", "", "basic authentication password")
|
createSecretGitCmd.Flags().StringVarP(&secretGitArgs.password, "password", "p", "", "basic authentication password")
|
||||||
createSecretGitCmd.Flags().Var(&secretGitKeyAlgorithm, "ssh-key-algorithm", secretGitKeyAlgorithm.Description())
|
createSecretGitCmd.Flags().Var(&secretGitArgs.keyAlgorithm, "ssh-key-algorithm", secretGitArgs.keyAlgorithm.Description())
|
||||||
createSecretGitCmd.Flags().Var(&secretGitRSABits, "ssh-rsa-bits", secretGitRSABits.Description())
|
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
|
||||||
createSecretGitCmd.Flags().Var(&secretGitECDSACurve, "ssh-ecdsa-curve", secretGitECDSACurve.Description())
|
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
|
||||||
|
|
||||||
createSecretCmd.AddCommand(createSecretGitCmd)
|
createSecretCmd.AddCommand(createSecretGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSecretGitFlags() secretGitFlags {
|
||||||
|
return secretGitFlags{
|
||||||
|
keyAlgorithm: "rsa",
|
||||||
|
rsaBits: 2048,
|
||||||
|
ecdsaCurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("secret name is required")
|
return fmt.Errorf("secret name is required")
|
||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
secret, err := makeSecret(name)
|
||||||
if secretGitURL == "" {
|
|
||||||
return fmt.Errorf("url is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := url.Parse(secretGitURL)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secretLabels, err := parseLabels()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
if secretGitArgs.url == "" {
|
||||||
defer cancel()
|
return fmt.Errorf("url is required")
|
||||||
|
|
||||||
secret := corev1.Secret{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: name,
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: secretLabels,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(secretGitArgs.url)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("git URL parse failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "ssh":
|
case "ssh":
|
||||||
pair, err := generateKeyPair(ctx, secretGitKeyAlgorithm, secretGitRSABits, secretGitECDSACurve)
|
pair, err := generateKeyPair(ctx, secretGitArgs.keyAlgorithm, secretGitArgs.rsaBits, secretGitArgs.ecdsaCurve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -134,34 +133,34 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
secret.Data = map[string][]byte{
|
secret.StringData = map[string]string{
|
||||||
"identity": pair.PrivateKey,
|
"identity": string(pair.PrivateKey),
|
||||||
"identity.pub": pair.PublicKey,
|
"identity.pub": string(pair.PublicKey),
|
||||||
"known_hosts": hostKey,
|
"known_hosts": string(hostKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !export {
|
if !createArgs.export {
|
||||||
logger.Generatef("deploy key: %s", string(pair.PublicKey))
|
logger.Generatef("deploy key: %s", string(pair.PublicKey))
|
||||||
}
|
}
|
||||||
case "http", "https":
|
case "http", "https":
|
||||||
if secretGitUsername == "" || secretGitPassword == "" {
|
if secretGitArgs.username == "" || secretGitArgs.password == "" {
|
||||||
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add cert data when it's implemented in source-controller
|
// TODO: add cert data when it's implemented in source-controller
|
||||||
secret.Data = map[string][]byte{
|
secret.StringData = map[string]string{
|
||||||
"username": []byte(secretGitUsername),
|
"username": secretGitArgs.username,
|
||||||
"password": []byte(secretGitPassword),
|
"password": secretGitArgs.password,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return exportSecret(secret)
|
return exportSecret(secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -169,7 +168,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Actionf("secret '%s' created in '%s' namespace", name, namespace)
|
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
@@ -55,21 +52,18 @@ The create secret helm command generates a Kubernetes secret with basic authenti
|
|||||||
RunE: createSecretHelmCmdRun,
|
RunE: createSecretHelmCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type secretHelmFlags struct {
|
||||||
secretHelmUsername string
|
username string
|
||||||
secretHelmPassword string
|
password string
|
||||||
secretHelmCertFile string
|
secretTLSFlags
|
||||||
secretHelmKeyFile string
|
}
|
||||||
secretHelmCAFile string
|
|
||||||
)
|
var secretHelmArgs secretHelmFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmUsername, "username", "u", "", "basic authentication username")
|
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.username, "username", "u", "", "basic authentication username")
|
||||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmPassword, "password", "p", "", "basic authentication password")
|
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.password, "password", "p", "", "basic authentication password")
|
||||||
createSecretHelmCmd.Flags().StringVar(&secretHelmCertFile, "cert-file", "", "TLS authentication cert file path")
|
initSecretTLSFlags(createSecretHelmCmd.Flags(), &secretHelmArgs.secretTLSFlags)
|
||||||
createSecretHelmCmd.Flags().StringVar(&secretHelmKeyFile, "key-file", "", "TLS authentication key file path")
|
|
||||||
createSecretHelmCmd.Flags().StringVar(&secretHelmCAFile, "ca-file", "", "TLS authentication CA file path")
|
|
||||||
|
|
||||||
createSecretCmd.AddCommand(createSecretHelmCmd)
|
createSecretCmd.AddCommand(createSecretHelmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,56 +72,28 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("secret name is required")
|
return fmt.Errorf("secret name is required")
|
||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
secret, err := makeSecret(name)
|
||||||
secretLabels, err := parseLabels()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
secret := corev1.Secret{
|
if secretHelmArgs.username != "" && secretHelmArgs.password != "" {
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
secret.StringData["username"] = secretHelmArgs.username
|
||||||
Name: name,
|
secret.StringData["password"] = secretHelmArgs.password
|
||||||
Namespace: namespace,
|
|
||||||
Labels: secretLabels,
|
|
||||||
},
|
|
||||||
StringData: map[string]string{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if secretHelmUsername != "" && secretHelmPassword != "" {
|
if err = populateSecretTLS(&secret, secretHelmArgs.secretTLSFlags); err != nil {
|
||||||
secret.StringData["username"] = secretHelmUsername
|
return err
|
||||||
secret.StringData["password"] = secretHelmPassword
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if secretHelmCertFile != "" && secretHelmKeyFile != "" {
|
if createArgs.export {
|
||||||
cert, err := ioutil.ReadFile(secretHelmCertFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read repository cert file '%s': %w", secretHelmCertFile, err)
|
|
||||||
}
|
|
||||||
secret.StringData["certFile"] = string(cert)
|
|
||||||
|
|
||||||
key, err := ioutil.ReadFile(secretHelmKeyFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read repository key file '%s': %w", secretHelmKeyFile, err)
|
|
||||||
}
|
|
||||||
secret.StringData["keyFile"] = string(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if secretHelmCAFile != "" {
|
|
||||||
ca, err := ioutil.ReadFile(secretHelmCAFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read repository CA file '%s': %w", secretHelmCAFile, err)
|
|
||||||
}
|
|
||||||
secret.StringData["caFile"] = string(ca)
|
|
||||||
}
|
|
||||||
|
|
||||||
if export {
|
|
||||||
return exportSecret(secret)
|
return exportSecret(secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -135,7 +101,7 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Actionf("secret '%s' created in '%s' namespace", name, namespace)
|
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
128
cmd/flux/create_secret_tls.go
Normal file
128
cmd/flux/create_secret_tls.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020, 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var createSecretTLSCmd = &cobra.Command{
|
||||||
|
Use: "tls [name]",
|
||||||
|
Short: "Create or update a Kubernetes secret with TLS certificates",
|
||||||
|
Long: `
|
||||||
|
The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`,
|
||||||
|
Example: `
|
||||||
|
# Create a TLS secret on disk and encrypt it with Mozilla SOPS.
|
||||||
|
# Files are expected to be PEM-encoded.
|
||||||
|
flux create secret tls certs \
|
||||||
|
--namespace=my-namespace \
|
||||||
|
--cert-file=./client.crt \
|
||||||
|
--key-file=./client.key \
|
||||||
|
--export > certs.yaml
|
||||||
|
|
||||||
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
|
--in-place certs.yaml
|
||||||
|
`,
|
||||||
|
RunE: createSecretTLSCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type secretTLSFlags struct {
|
||||||
|
certFile string
|
||||||
|
keyFile string
|
||||||
|
caFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
var secretTLSArgs secretTLSFlags
|
||||||
|
|
||||||
|
func initSecretTLSFlags(flags *pflag.FlagSet, args *secretTLSFlags) {
|
||||||
|
flags.StringVar(&args.certFile, "cert-file", "", "TLS authentication cert file path")
|
||||||
|
flags.StringVar(&args.keyFile, "key-file", "", "TLS authentication key file path")
|
||||||
|
flags.StringVar(&args.caFile, "ca-file", "", "TLS authentication CA file path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flags := createSecretTLSCmd.Flags()
|
||||||
|
initSecretTLSFlags(flags, &secretTLSArgs)
|
||||||
|
createSecretCmd.AddCommand(createSecretTLSCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateSecretTLS(secret *corev1.Secret, args secretTLSFlags) error {
|
||||||
|
if args.certFile != "" && args.keyFile != "" {
|
||||||
|
cert, err := ioutil.ReadFile(args.certFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read repository cert file '%s': %w", args.certFile, err)
|
||||||
|
}
|
||||||
|
secret.StringData["certFile"] = string(cert)
|
||||||
|
|
||||||
|
key, err := ioutil.ReadFile(args.keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read repository key file '%s': %w", args.keyFile, err)
|
||||||
|
}
|
||||||
|
secret.StringData["keyFile"] = string(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.caFile != "" {
|
||||||
|
ca, err := ioutil.ReadFile(args.caFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read repository CA file '%s': %w", args.caFile, err)
|
||||||
|
}
|
||||||
|
secret.StringData["caFile"] = string(ca)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("secret name is required")
|
||||||
|
}
|
||||||
|
name := args[0]
|
||||||
|
secret, err := makeSecret(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = populateSecretTLS(&secret, secretTLSArgs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if createArgs.export {
|
||||||
|
return exportSecret(secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -30,9 +30,11 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSourceBucketCmd = &cobra.Command{
|
var createSourceBucketCmd = &cobra.Command{
|
||||||
@@ -61,41 +63,49 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
|
|||||||
RunE: createSourceBucketCmdRun,
|
RunE: createSourceBucketCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type sourceBucketFlags struct {
|
||||||
sourceBucketName string
|
name string
|
||||||
sourceBucketProvider = flags.SourceBucketProvider(sourcev1.GenericBucketProvider)
|
provider flags.SourceBucketProvider
|
||||||
sourceBucketEndpoint string
|
endpoint string
|
||||||
sourceBucketAccessKey string
|
accessKey string
|
||||||
sourceBucketSecretKey string
|
secretKey string
|
||||||
sourceBucketRegion string
|
region string
|
||||||
sourceBucketInsecure bool
|
insecure bool
|
||||||
sourceBucketSecretRef string
|
secretRef string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var sourceBucketArgs = NewSourceBucketFlags()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createSourceBucketCmd.Flags().Var(&sourceBucketProvider, "provider", sourceBucketProvider.Description())
|
createSourceBucketCmd.Flags().Var(&sourceBucketArgs.provider, "provider", sourceBucketArgs.provider.Description())
|
||||||
createSourceBucketCmd.Flags().StringVar(&sourceBucketName, "bucket-name", "", "the bucket name")
|
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.name, "bucket-name", "", "the bucket name")
|
||||||
createSourceBucketCmd.Flags().StringVar(&sourceBucketEndpoint, "endpoint", "", "the bucket endpoint address")
|
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.endpoint, "endpoint", "", "the bucket endpoint address")
|
||||||
createSourceBucketCmd.Flags().StringVar(&sourceBucketAccessKey, "access-key", "", "the bucket access key")
|
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.accessKey, "access-key", "", "the bucket access key")
|
||||||
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretKey, "secret-key", "", "the bucket secret key")
|
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.secretKey, "secret-key", "", "the bucket secret key")
|
||||||
createSourceBucketCmd.Flags().StringVar(&sourceBucketRegion, "region", "", "the bucket region")
|
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.region, "region", "", "the bucket region")
|
||||||
createSourceBucketCmd.Flags().BoolVar(&sourceBucketInsecure, "insecure", false, "for when connecting to a non-TLS S3 HTTP endpoint")
|
createSourceBucketCmd.Flags().BoolVar(&sourceBucketArgs.insecure, "insecure", false, "for when connecting to a non-TLS S3 HTTP endpoint")
|
||||||
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretRef, "secret-ref", "", "the name of an existing secret containing credentials")
|
createSourceBucketCmd.Flags().StringVar(&sourceBucketArgs.secretRef, "secret-ref", "", "the name of an existing secret containing credentials")
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceBucketCmd)
|
createSourceCmd.AddCommand(createSourceBucketCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSourceBucketFlags() sourceBucketFlags {
|
||||||
|
return sourceBucketFlags{
|
||||||
|
provider: flags.SourceBucketProvider(sourcev1.GenericBucketProvider),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("Bucket source name is required")
|
return fmt.Errorf("Bucket source name is required")
|
||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if sourceBucketName == "" {
|
if sourceBucketArgs.name == "" {
|
||||||
return fmt.Errorf("bucket-name is required")
|
return fmt.Errorf("bucket-name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceBucketEndpoint == "" {
|
if sourceBucketArgs.endpoint == "" {
|
||||||
return fmt.Errorf("endpoint is required")
|
return fmt.Errorf("endpoint is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,55 +123,55 @@ 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: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: sourcev1.BucketSpec{
|
Spec: sourcev1.BucketSpec{
|
||||||
BucketName: sourceBucketName,
|
BucketName: sourceBucketArgs.name,
|
||||||
Provider: sourceBucketProvider.String(),
|
Provider: sourceBucketArgs.provider.String(),
|
||||||
Insecure: sourceBucketInsecure,
|
Insecure: sourceBucketArgs.insecure,
|
||||||
Endpoint: sourceBucketEndpoint,
|
Endpoint: sourceBucketArgs.endpoint,
|
||||||
Region: sourceBucketRegion,
|
Region: sourceBucketArgs.region,
|
||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if sourceHelmSecretRef != "" {
|
if sourceHelmArgs.secretRef != "" {
|
||||||
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
|
bucket.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: sourceBucketSecretRef,
|
Name: sourceBucketArgs.secretRef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return exportBucket(*bucket)
|
return exportBucket(*bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Generatef("generating Bucket source")
|
logger.Generatef("generating Bucket source")
|
||||||
|
|
||||||
if sourceBucketSecretRef == "" {
|
if sourceBucketArgs.secretRef == "" {
|
||||||
secretName := fmt.Sprintf("bucket-%s", name)
|
secretName := fmt.Sprintf("bucket-%s", name)
|
||||||
|
|
||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{},
|
StringData: map[string]string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceBucketAccessKey != "" && sourceBucketSecretKey != "" {
|
if sourceBucketArgs.accessKey != "" && sourceBucketArgs.secretKey != "" {
|
||||||
secret.StringData["accesskey"] = sourceBucketAccessKey
|
secret.StringData["accesskey"] = sourceBucketArgs.accessKey
|
||||||
secret.StringData["secretkey"] = sourceBucketSecretKey
|
secret.StringData["secretkey"] = sourceBucketArgs.secretKey
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(secret.StringData) > 0 {
|
if len(secret.StringData) > 0 {
|
||||||
@@ -169,7 +179,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
|
bucket.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
}
|
}
|
||||||
logger.Successf("authentication configured")
|
logger.Successf("authentication configured")
|
||||||
@@ -183,7 +193,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Bucket source reconciliation")
|
logger.Waitingf("waiting for Bucket source reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
|
isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,28 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SourceGitFlags struct {
|
||||||
|
GitURL string
|
||||||
|
GitBranch string
|
||||||
|
GitTag string
|
||||||
|
GitSemver string
|
||||||
|
GitUsername string
|
||||||
|
GitPassword string
|
||||||
|
|
||||||
|
GitKeyAlgorithm flags.PublicKeyAlgorithm
|
||||||
|
GitRSABits flags.RSAKeyBits
|
||||||
|
GitECDSACurve flags.ECDSACurve
|
||||||
|
GitSecretRef string
|
||||||
|
GitImplementation flags.GitImplementation
|
||||||
|
}
|
||||||
|
|
||||||
var createSourceGitCmd = &cobra.Command{
|
var createSourceGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Create or update a GitRepository source",
|
Short: "Create or update a GitRepository source",
|
||||||
@@ -84,44 +100,39 @@ For private Git repositories, the basic authentication credentials are stored in
|
|||||||
RunE: createSourceGitCmdRun,
|
RunE: createSourceGitCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var sourceArgs = NewSourceGitFlags()
|
||||||
sourceGitURL string
|
|
||||||
sourceGitBranch string
|
|
||||||
sourceGitTag string
|
|
||||||
sourceGitSemver string
|
|
||||||
sourceGitUsername string
|
|
||||||
sourceGitPassword string
|
|
||||||
|
|
||||||
sourceGitKeyAlgorithm flags.PublicKeyAlgorithm = "rsa"
|
|
||||||
sourceGitRSABits flags.RSAKeyBits = 2048
|
|
||||||
sourceGitECDSACurve = flags.ECDSACurve{Curve: elliptic.P384()}
|
|
||||||
sourceGitSecretRef string
|
|
||||||
sourceGitImplementation flags.GitImplementation
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitURL, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitURL, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitBranch, "branch", "master", "git branch")
|
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitBranch, "branch", "master", "git branch")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitTag, "tag", "", "git tag")
|
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitTag, "tag", "", "git tag")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitSemver, "tag-semver", "", "git tag semver range")
|
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitSemver, "tag-semver", "", "git tag semver range")
|
||||||
createSourceGitCmd.Flags().StringVarP(&sourceGitUsername, "username", "u", "", "basic authentication username")
|
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitUsername, "username", "u", "", "basic authentication username")
|
||||||
createSourceGitCmd.Flags().StringVarP(&sourceGitPassword, "password", "p", "", "basic authentication password")
|
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitPassword, "password", "p", "", "basic authentication password")
|
||||||
createSourceGitCmd.Flags().Var(&sourceGitKeyAlgorithm, "ssh-key-algorithm", sourceGitKeyAlgorithm.Description())
|
createSourceGitCmd.Flags().Var(&sourceArgs.GitKeyAlgorithm, "ssh-key-algorithm", sourceArgs.GitKeyAlgorithm.Description())
|
||||||
createSourceGitCmd.Flags().Var(&sourceGitRSABits, "ssh-rsa-bits", sourceGitRSABits.Description())
|
createSourceGitCmd.Flags().Var(&sourceArgs.GitRSABits, "ssh-rsa-bits", sourceArgs.GitRSABits.Description())
|
||||||
createSourceGitCmd.Flags().Var(&sourceGitECDSACurve, "ssh-ecdsa-curve", sourceGitECDSACurve.Description())
|
createSourceGitCmd.Flags().Var(&sourceArgs.GitECDSACurve, "ssh-ecdsa-curve", sourceArgs.GitECDSACurve.Description())
|
||||||
createSourceGitCmd.Flags().StringVarP(&sourceGitSecretRef, "secret-ref", "", "", "the name of an existing secret containing SSH or basic credentials")
|
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitSecretRef, "secret-ref", "", "", "the name of an existing secret containing SSH or basic credentials")
|
||||||
createSourceGitCmd.Flags().Var(&sourceGitImplementation, "git-implementation", sourceGitImplementation.Description())
|
createSourceGitCmd.Flags().Var(&sourceArgs.GitImplementation, "git-implementation", sourceArgs.GitImplementation.Description())
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceGitCmd)
|
createSourceCmd.AddCommand(createSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSourceGitFlags() SourceGitFlags {
|
||||||
|
return SourceGitFlags{
|
||||||
|
GitKeyAlgorithm: "rsa",
|
||||||
|
GitRSABits: 2048,
|
||||||
|
GitECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("GitRepository source name is required")
|
return fmt.Errorf("GitRepository source name is required")
|
||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if sourceGitURL == "" {
|
if sourceArgs.GitURL == "" {
|
||||||
return fmt.Errorf("url is required")
|
return fmt.Errorf("url is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +142,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
u, err := url.Parse(sourceGitURL)
|
u, err := url.Parse(sourceArgs.GitURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
return fmt.Errorf("git URL parse failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -144,54 +155,54 @@ 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: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: sourcev1.GitRepositorySpec{
|
Spec: sourcev1.GitRepositorySpec{
|
||||||
URL: sourceGitURL,
|
URL: sourceArgs.GitURL,
|
||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
Reference: &sourcev1.GitRepositoryRef{},
|
Reference: &sourcev1.GitRepositoryRef{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceGitImplementation != "" {
|
if sourceArgs.GitImplementation != "" {
|
||||||
gitRepository.Spec.GitImplementation = sourceGitImplementation.String()
|
gitRepository.Spec.GitImplementation = sourceArgs.GitImplementation.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceGitSemver != "" {
|
if sourceArgs.GitSemver != "" {
|
||||||
gitRepository.Spec.Reference.SemVer = sourceGitSemver
|
gitRepository.Spec.Reference.SemVer = sourceArgs.GitSemver
|
||||||
} else if sourceGitTag != "" {
|
} else if sourceArgs.GitTag != "" {
|
||||||
gitRepository.Spec.Reference.Tag = sourceGitTag
|
gitRepository.Spec.Reference.Tag = sourceArgs.GitTag
|
||||||
} else {
|
} else {
|
||||||
gitRepository.Spec.Reference.Branch = sourceGitBranch
|
gitRepository.Spec.Reference.Branch = sourceArgs.GitBranch
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
if sourceGitSecretRef != "" {
|
if sourceArgs.GitSecretRef != "" {
|
||||||
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
|
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: sourceGitSecretRef,
|
Name: sourceArgs.GitSecretRef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return exportGit(gitRepository)
|
return exportGit(gitRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
withAuth := false
|
withAuth := false
|
||||||
// TODO(hidde): move all auth prep to separate func?
|
// TODO(hidde): move all auth prep to separate func?
|
||||||
if sourceGitSecretRef != "" {
|
if sourceArgs.GitSecretRef != "" {
|
||||||
withAuth = true
|
withAuth = true
|
||||||
} else if u.Scheme == "ssh" {
|
} else if u.Scheme == "ssh" {
|
||||||
logger.Generatef("generating deploy key pair")
|
logger.Generatef("generating deploy key pair")
|
||||||
pair, err := generateKeyPair(ctx, sourceGitKeyAlgorithm, sourceGitRSABits, sourceGitECDSACurve)
|
pair, err := generateKeyPair(ctx, sourceArgs.GitKeyAlgorithm, sourceArgs.GitRSABits, sourceArgs.GitECDSACurve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -216,7 +227,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{
|
StringData: map[string]string{
|
||||||
@@ -229,17 +240,17 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
withAuth = true
|
withAuth = true
|
||||||
} else if sourceGitUsername != "" && sourceGitPassword != "" {
|
} else if sourceArgs.GitUsername != "" && sourceArgs.GitPassword != "" {
|
||||||
logger.Actionf("applying secret with basic auth credentials")
|
logger.Actionf("applying secret with basic auth credentials")
|
||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{
|
StringData: map[string]string{
|
||||||
"username": sourceGitUsername,
|
"username": sourceArgs.GitUsername,
|
||||||
"password": sourceGitPassword,
|
"password": sourceArgs.GitPassword,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||||
@@ -256,10 +267,10 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
if withAuth {
|
if withAuth {
|
||||||
secretName := name
|
secretName := name
|
||||||
if sourceGitSecretRef != "" {
|
if sourceArgs.GitSecretRef != "" {
|
||||||
secretName = sourceGitSecretRef
|
secretName = sourceArgs.GitSecretRef
|
||||||
}
|
}
|
||||||
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
|
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,7 +282,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for GitRepository source reconciliation")
|
logger.Waitingf("waiting for GitRepository source reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
|
isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSourceHelmCmd = &cobra.Command{
|
var createSourceHelmCmd = &cobra.Command{
|
||||||
@@ -64,24 +65,26 @@ For private Helm repositories, the basic authentication credentials are stored i
|
|||||||
RunE: createSourceHelmCmdRun,
|
RunE: createSourceHelmCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type sourceHelmFlags struct {
|
||||||
sourceHelmURL string
|
url string
|
||||||
sourceHelmUsername string
|
username string
|
||||||
sourceHelmPassword string
|
password string
|
||||||
sourceHelmCertFile string
|
certFile string
|
||||||
sourceHelmKeyFile string
|
keyFile string
|
||||||
sourceHelmCAFile string
|
caFile string
|
||||||
sourceHelmSecretRef string
|
secretRef string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var sourceHelmArgs sourceHelmFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmURL, "url", "", "Helm repository address")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.url, "url", "", "Helm repository address")
|
||||||
createSourceHelmCmd.Flags().StringVarP(&sourceHelmUsername, "username", "u", "", "basic authentication username")
|
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.username, "username", "u", "", "basic authentication username")
|
||||||
createSourceHelmCmd.Flags().StringVarP(&sourceHelmPassword, "password", "p", "", "basic authentication password")
|
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.password, "password", "p", "", "basic authentication password")
|
||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmCertFile, "cert-file", "", "TLS authentication cert file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.certFile, "cert-file", "", "TLS authentication cert file path")
|
||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmKeyFile, "key-file", "", "TLS authentication key file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
|
||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmCAFile, "ca-file", "", "TLS authentication CA file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
|
||||||
createSourceHelmCmd.Flags().StringVarP(&sourceHelmSecretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
|
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceHelmCmd)
|
createSourceCmd.AddCommand(createSourceHelmCmd)
|
||||||
}
|
}
|
||||||
@@ -92,7 +95,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if sourceHelmURL == "" {
|
if sourceHelmArgs.url == "" {
|
||||||
return fmt.Errorf("url is required")
|
return fmt.Errorf("url is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,78 +110,78 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
if _, err := url.Parse(sourceHelmURL); err != nil {
|
if _, err := url.Parse(sourceHelmArgs.url); err != nil {
|
||||||
return fmt.Errorf("url parse failed: %w", err)
|
return fmt.Errorf("url parse failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
helmRepository := &sourcev1.HelmRepository{
|
helmRepository := &sourcev1.HelmRepository{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: sourcev1.HelmRepositorySpec{
|
Spec: sourcev1.HelmRepositorySpec{
|
||||||
URL: sourceHelmURL,
|
URL: sourceHelmArgs.url,
|
||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceHelmSecretRef != "" {
|
if sourceHelmArgs.secretRef != "" {
|
||||||
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
|
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: sourceHelmSecretRef,
|
Name: sourceHelmArgs.secretRef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
return exportHelmRepository(*helmRepository)
|
return exportHelmRepository(*helmRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Generatef("generating HelmRepository source")
|
logger.Generatef("generating HelmRepository source")
|
||||||
if sourceHelmSecretRef == "" {
|
if sourceHelmArgs.secretRef == "" {
|
||||||
secretName := fmt.Sprintf("helm-%s", name)
|
secretName := fmt.Sprintf("helm-%s", name)
|
||||||
|
|
||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{},
|
StringData: map[string]string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceHelmUsername != "" && sourceHelmPassword != "" {
|
if sourceHelmArgs.username != "" && sourceHelmArgs.password != "" {
|
||||||
secret.StringData["username"] = sourceHelmUsername
|
secret.StringData["username"] = sourceHelmArgs.username
|
||||||
secret.StringData["password"] = sourceHelmPassword
|
secret.StringData["password"] = sourceHelmArgs.password
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceHelmCertFile != "" && sourceHelmKeyFile != "" {
|
if sourceHelmArgs.certFile != "" && sourceHelmArgs.keyFile != "" {
|
||||||
cert, err := ioutil.ReadFile(sourceHelmCertFile)
|
cert, err := ioutil.ReadFile(sourceHelmArgs.certFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read repository cert file '%s': %w", sourceHelmCertFile, err)
|
return fmt.Errorf("failed to read repository cert file '%s': %w", sourceHelmArgs.certFile, err)
|
||||||
}
|
}
|
||||||
secret.StringData["certFile"] = string(cert)
|
secret.StringData["certFile"] = string(cert)
|
||||||
|
|
||||||
key, err := ioutil.ReadFile(sourceHelmKeyFile)
|
key, err := ioutil.ReadFile(sourceHelmArgs.keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read repository key file '%s': %w", sourceHelmKeyFile, err)
|
return fmt.Errorf("failed to read repository key file '%s': %w", sourceHelmArgs.keyFile, err)
|
||||||
}
|
}
|
||||||
secret.StringData["keyFile"] = string(key)
|
secret.StringData["keyFile"] = string(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceHelmCAFile != "" {
|
if sourceHelmArgs.caFile != "" {
|
||||||
ca, err := ioutil.ReadFile(sourceHelmCAFile)
|
ca, err := ioutil.ReadFile(sourceHelmArgs.caFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read repository CA file '%s': %w", sourceHelmCAFile, err)
|
return fmt.Errorf("failed to read repository CA file '%s': %w", sourceHelmArgs.caFile, err)
|
||||||
}
|
}
|
||||||
secret.StringData["caFile"] = string(ca)
|
secret.StringData["caFile"] = string(ca)
|
||||||
}
|
}
|
||||||
@@ -188,7 +191,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
|
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
}
|
}
|
||||||
logger.Successf("authentication configured")
|
logger.Successf("authentication configured")
|
||||||
@@ -202,7 +205,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmRepository source reconciliation")
|
logger.Waitingf("waiting for HelmRepository source reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
|
isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,14 +58,16 @@ const (
|
|||||||
tenantLabel = "toolkit.fluxcd.io/tenant"
|
tenantLabel = "toolkit.fluxcd.io/tenant"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type tenantFlags struct {
|
||||||
tenantNamespaces []string
|
namespaces []string
|
||||||
tenantClusterRole string
|
clusterRole string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var tenantArgs tenantFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createTenantCmd.Flags().StringSliceVar(&tenantNamespaces, "with-namespace", nil, "namespace belonging to this tenant")
|
createTenantCmd.Flags().StringSliceVar(&tenantArgs.namespaces, "with-namespace", nil, "namespace belonging to this tenant")
|
||||||
createTenantCmd.Flags().StringVar(&tenantClusterRole, "cluster-role", "cluster-admin", "cluster role of the tenant role binding")
|
createTenantCmd.Flags().StringVar(&tenantArgs.clusterRole, "cluster-role", "cluster-admin", "cluster role of the tenant role binding")
|
||||||
createCmd.AddCommand(createTenantCmd)
|
createCmd.AddCommand(createTenantCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,11 +80,11 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("invalid tenant name '%s': %v", tenant, err)
|
return fmt.Errorf("invalid tenant name '%s': %v", tenant, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tenantClusterRole == "" {
|
if tenantArgs.clusterRole == "" {
|
||||||
return fmt.Errorf("cluster-role is required")
|
return fmt.Errorf("cluster-role is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if tenantNamespaces == nil {
|
if tenantArgs.namespaces == nil {
|
||||||
return fmt.Errorf("with-namespace is required")
|
return fmt.Errorf("with-namespace is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +92,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
var accounts []corev1.ServiceAccount
|
var accounts []corev1.ServiceAccount
|
||||||
var roleBindings []rbacv1.RoleBinding
|
var roleBindings []rbacv1.RoleBinding
|
||||||
|
|
||||||
for _, ns := range tenantNamespaces {
|
for _, ns := range tenantArgs.namespaces {
|
||||||
if err := validation.IsQualifiedName(ns); len(err) > 0 {
|
if err := validation.IsQualifiedName(ns); len(err) > 0 {
|
||||||
return fmt.Errorf("invalid namespace '%s': %v", ns, err)
|
return fmt.Errorf("invalid namespace '%s': %v", ns, err)
|
||||||
}
|
}
|
||||||
@@ -141,14 +143,14 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
RoleRef: rbacv1.RoleRef{
|
RoleRef: rbacv1.RoleRef{
|
||||||
APIGroup: "rbac.authorization.k8s.io",
|
APIGroup: "rbac.authorization.k8s.io",
|
||||||
Kind: "ClusterRole",
|
Kind: "ClusterRole",
|
||||||
Name: tenantClusterRole,
|
Name: tenantArgs.clusterRole,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
roleBindings = append(roleBindings, roleBinding)
|
roleBindings = append(roleBindings, roleBinding)
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if createArgs.export {
|
||||||
for i, _ := range tenantNamespaces {
|
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
|
||||||
}
|
}
|
||||||
@@ -156,15 +158,15 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, _ := range tenantNamespaces {
|
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
|
||||||
|
|||||||
@@ -33,12 +33,14 @@ var deleteCmd = &cobra.Command{
|
|||||||
Long: "The delete sub-commands delete sources and resources.",
|
Long: "The delete sub-commands delete sources and resources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type deleteFlags struct {
|
||||||
deleteSilent bool
|
silent bool
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var deleteArgs deleteFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteCmd.PersistentFlags().BoolVarP(&deleteSilent, "silent", "s", false,
|
deleteCmd.PersistentFlags().BoolVarP(&deleteArgs.silent, "silent", "s", false,
|
||||||
"delete resource without asking for confirmation")
|
"delete resource without asking for confirmation")
|
||||||
|
|
||||||
rootCmd.AddCommand(deleteCmd)
|
rootCmd.AddCommand(deleteCmd)
|
||||||
@@ -55,16 +57,16 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !deleteSilent {
|
if !deleteArgs.silent {
|
||||||
prompt := promptui.Prompt{
|
prompt := promptui.Prompt{
|
||||||
Label: "Are you sure you want to delete this " + del.humanKind,
|
Label: "Are you sure you want to delete this " + del.humanKind,
|
||||||
IsConfirm: true,
|
IsConfirm: true,
|
||||||
@@ -83,7 +85,7 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, namespace)
|
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, rootArgs.namespace)
|
||||||
err = kubeClient.Delete(ctx, del.object.asClientObject())
|
err = kubeClient.Delete(ctx, del.object.asClientObject())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -48,16 +48,16 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !deleteSilent {
|
if !deleteArgs.silent {
|
||||||
prompt := promptui.Prompt{
|
prompt := promptui.Prompt{
|
||||||
Label: "Are you sure you want to delete this Alert",
|
Label: "Are you sure you want to delete this Alert",
|
||||||
IsConfirm: true,
|
IsConfirm: true,
|
||||||
@@ -77,7 +77,7 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("deleting alert %s in %s namespace", name, namespace)
|
logger.Actionf("deleting alert %s in %s namespace", name, rootArgs.namespace)
|
||||||
err = kubeClient.Delete(ctx, &alert)
|
err = kubeClient.Delete(ctx, &alert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -48,16 +48,16 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !deleteSilent {
|
if !deleteArgs.silent {
|
||||||
prompt := promptui.Prompt{
|
prompt := promptui.Prompt{
|
||||||
Label: "Are you sure you want to delete this Provider",
|
Label: "Are you sure you want to delete this Provider",
|
||||||
IsConfirm: true,
|
IsConfirm: true,
|
||||||
@@ -77,7 +77,7 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("deleting provider %s in %s namespace", name, namespace)
|
logger.Actionf("deleting provider %s in %s namespace", name, rootArgs.namespace)
|
||||||
err = kubeClient.Delete(ctx, &alertProvider)
|
err = kubeClient.Delete(ctx, &alertProvider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,15 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/manifoldco/promptui"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteHelmReleaseCmd = &cobra.Command{
|
var deleteHelmReleaseCmd = &cobra.Command{
|
||||||
@@ -36,57 +29,12 @@ var deleteHelmReleaseCmd = &cobra.Command{
|
|||||||
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
||||||
flux delete hr podinfo
|
flux delete hr podinfo
|
||||||
`,
|
`,
|
||||||
RunE: deleteHelmReleaseCmdRun,
|
RunE: deleteCommand{
|
||||||
|
apiType: helmReleaseType,
|
||||||
|
object: universalAdapter{&helmv2.HelmRelease{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteCmd.AddCommand(deleteHelmReleaseCmd)
|
deleteCmd.AddCommand(deleteHelmReleaseCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("release name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
var helmRelease helmv2.HelmRelease
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !deleteSilent {
|
|
||||||
if !helmRelease.Spec.Suspend {
|
|
||||||
logger.Waitingf("This action will remove the Kubernetes objects previously applied by the %s Helm release!", name)
|
|
||||||
}
|
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Are you sure you want to delete this Helm release",
|
|
||||||
IsConfirm: true,
|
|
||||||
}
|
|
||||||
if _, err := prompt.Run(); err != nil {
|
|
||||||
return fmt.Errorf("aborting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("deleting release %s in %s namespace", name, namespace)
|
|
||||||
err = kubeClient.Delete(ctx, &helmRelease)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("release deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,14 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
"github.com/manifoldco/promptui"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteKsCmd = &cobra.Command{
|
var deleteKsCmd = &cobra.Command{
|
||||||
@@ -35,57 +29,12 @@ var deleteKsCmd = &cobra.Command{
|
|||||||
Example: ` # Delete a kustomization and the Kubernetes resources created by it
|
Example: ` # Delete a kustomization and the Kubernetes resources created by it
|
||||||
flux delete kustomization podinfo
|
flux delete kustomization podinfo
|
||||||
`,
|
`,
|
||||||
RunE: deleteKsCmdRun,
|
RunE: deleteCommand{
|
||||||
|
apiType: kustomizationType,
|
||||||
|
object: universalAdapter{&kustomizev1.Kustomization{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteCmd.AddCommand(deleteKsCmd)
|
deleteCmd.AddCommand(deleteKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("kustomization name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !deleteSilent {
|
|
||||||
if !kustomization.Spec.Suspend {
|
|
||||||
logger.Waitingf("This action will remove the Kubernetes objects previously applied by the %s kustomization!", name)
|
|
||||||
}
|
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Are you sure you want to delete this kustomization",
|
|
||||||
IsConfirm: true,
|
|
||||||
}
|
|
||||||
if _, err := prompt.Run(); err != nil {
|
|
||||||
return fmt.Errorf("aborting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("deleting kustomization %s in %s namespace", name, namespace)
|
|
||||||
err = kubeClient.Delete(ctx, &kustomization)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("kustomization deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -48,16 +48,16 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !deleteSilent {
|
if !deleteArgs.silent {
|
||||||
prompt := promptui.Prompt{
|
prompt := promptui.Prompt{
|
||||||
Label: "Are you sure you want to delete this Receiver",
|
Label: "Are you sure you want to delete this Receiver",
|
||||||
IsConfirm: true,
|
IsConfirm: true,
|
||||||
@@ -77,7 +77,7 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("deleting receiver %s in %s namespace", name, namespace)
|
logger.Actionf("deleting receiver %s in %s namespace", name, rootArgs.namespace)
|
||||||
err = kubeClient.Delete(ctx, &receiver)
|
err = kubeClient.Delete(ctx, &receiver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,14 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
"github.com/manifoldco/promptui"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteSourceBucketCmd = &cobra.Command{
|
var deleteSourceBucketCmd = &cobra.Command{
|
||||||
@@ -34,54 +28,12 @@ var deleteSourceBucketCmd = &cobra.Command{
|
|||||||
Example: ` # Delete a Bucket source
|
Example: ` # Delete a Bucket source
|
||||||
flux delete source bucket podinfo
|
flux delete source bucket podinfo
|
||||||
`,
|
`,
|
||||||
RunE: deleteSourceBucketCmdRun,
|
RunE: deleteCommand{
|
||||||
|
apiType: bucketType,
|
||||||
|
object: universalAdapter{&sourcev1.Bucket{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteSourceCmd.AddCommand(deleteSourceBucketCmd)
|
deleteSourceCmd.AddCommand(deleteSourceBucketCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
var bucket sourcev1.Bucket
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !deleteSilent {
|
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Are you sure you want to delete this source",
|
|
||||||
IsConfirm: true,
|
|
||||||
}
|
|
||||||
if _, err := prompt.Run(); err != nil {
|
|
||||||
return fmt.Errorf("aborting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("deleting source %s in %s namespace", name, namespace)
|
|
||||||
err = kubeClient.Delete(ctx, &bucket)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,14 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
"github.com/manifoldco/promptui"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteSourceGitCmd = &cobra.Command{
|
var deleteSourceGitCmd = &cobra.Command{
|
||||||
@@ -34,54 +28,12 @@ var deleteSourceGitCmd = &cobra.Command{
|
|||||||
Example: ` # Delete a Git repository
|
Example: ` # Delete a Git repository
|
||||||
flux delete source git podinfo
|
flux delete source git podinfo
|
||||||
`,
|
`,
|
||||||
RunE: deleteSourceGitCmdRun,
|
RunE: deleteCommand{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
object: universalAdapter{&sourcev1.GitRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteSourceCmd.AddCommand(deleteSourceGitCmd)
|
deleteSourceCmd.AddCommand(deleteSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("git name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
var git sourcev1.GitRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &git)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !deleteSilent {
|
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Are you sure you want to delete this source",
|
|
||||||
IsConfirm: true,
|
|
||||||
}
|
|
||||||
if _, err := prompt.Run(); err != nil {
|
|
||||||
return fmt.Errorf("aborting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("deleting source %s in %s namespace", name, namespace)
|
|
||||||
err = kubeClient.Delete(ctx, &git)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source deleted")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ var deleteSourceHelmCmd = &cobra.Command{
|
|||||||
Example: ` # Delete a Helm repository
|
Example: ` # Delete a Helm repository
|
||||||
flux delete source helm podinfo
|
flux delete source helm podinfo
|
||||||
`,
|
`,
|
||||||
RunE: deleteSourceHelmCmdRun,
|
RunE: deleteCommand{
|
||||||
|
apiType: helmRepositoryType,
|
||||||
|
object: universalAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -47,16 +50,16 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +69,7 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !deleteSilent {
|
if !deleteArgs.silent {
|
||||||
prompt := promptui.Prompt{
|
prompt := promptui.Prompt{
|
||||||
Label: "Are you sure you want to delete this source",
|
Label: "Are you sure you want to delete this source",
|
||||||
IsConfirm: true,
|
IsConfirm: true,
|
||||||
@@ -76,7 +79,7 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("deleting source %s in %s namespace", name, namespace)
|
logger.Actionf("deleting source %s in %s namespace", name, rootArgs.namespace)
|
||||||
err = kubeClient.Delete(ctx, &helmRepository)
|
err = kubeClient.Delete(ctx, &helmRepository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -35,12 +35,14 @@ var exportCmd = &cobra.Command{
|
|||||||
Long: "The export sub-commands export resources in YAML format.",
|
Long: "The export sub-commands export resources in YAML format.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type exportFlags struct {
|
||||||
exportAll bool
|
all bool
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var exportArgs exportFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
exportCmd.PersistentFlags().BoolVar(&exportAll, "all", false, "select all resources")
|
exportCmd.PersistentFlags().BoolVar(&exportArgs.all, "all", false, "select all resources")
|
||||||
|
|
||||||
rootCmd.AddCommand(exportCmd)
|
rootCmd.AddCommand(exportCmd)
|
||||||
}
|
}
|
||||||
@@ -65,26 +67,26 @@ type exportCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (export exportCommand) run(cmd *cobra.Command, args []string) error {
|
func (export exportCommand) run(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("name is required")
|
return fmt.Errorf("name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(namespace))
|
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if export.list.len() == 0 {
|
if export.list.len() == 0 {
|
||||||
logger.Failuref("no objects found in %s namespace", namespace)
|
logger.Failuref("no objects found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +98,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: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())
|
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())
|
||||||
|
|||||||
@@ -48,27 +48,27 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
|
func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("name is required")
|
return fmt.Errorf("name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
var list notificationv1.AlertList
|
var list notificationv1.AlertList
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
|
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no alerts found in %s namespace", namespace)
|
logger.Failuref("no alerts found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var alert notificationv1.Alert
|
var alert notificationv1.Alert
|
||||||
|
|||||||
@@ -48,27 +48,27 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("name is required")
|
return fmt.Errorf("name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
var list notificationv1.ProviderList
|
var list notificationv1.ProviderList
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
|
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no alertproviders found in %s namespace", namespace)
|
logger.Failuref("no alertproviders found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var alertProvider notificationv1.Provider
|
var alertProvider notificationv1.Provider
|
||||||
|
|||||||
@@ -49,27 +49,27 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("name is required")
|
return fmt.Errorf("name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
var list helmv2.HelmReleaseList
|
var list helmv2.HelmReleaseList
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
|
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no helmrelease found in %s namespace", namespace)
|
logger.Failuref("no helmrelease found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var helmRelease helmv2.HelmRelease
|
var helmRelease helmv2.HelmRelease
|
||||||
|
|||||||
@@ -49,27 +49,27 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exportKsCmdRun(cmd *cobra.Command, args []string) error {
|
func exportKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("kustomization name is required")
|
return fmt.Errorf("kustomization name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
var list kustomizev1.KustomizationList
|
var list kustomizev1.KustomizationList
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
|
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no kustomizations found in %s namespace", namespace)
|
logger.Failuref("no kustomizations found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ func exportKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var kustomization kustomizev1.Kustomization
|
var kustomization kustomizev1.Kustomization
|
||||||
|
|||||||
@@ -48,27 +48,27 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("name is required")
|
return fmt.Errorf("name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
var list notificationv1.ReceiverList
|
var list notificationv1.ReceiverList
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
|
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no receivers found in %s namespace", namespace)
|
logger.Failuref("no receivers found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var receiver notificationv1.Receiver
|
var receiver notificationv1.Receiver
|
||||||
|
|||||||
@@ -49,27 +49,27 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("name is required")
|
return fmt.Errorf("name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
var list sourcev1.BucketList
|
var list sourcev1.BucketList
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
|
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no source found in %s namespace", namespace)
|
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var bucket sourcev1.Bucket
|
var bucket sourcev1.Bucket
|
||||||
|
|||||||
@@ -49,27 +49,27 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("name is required")
|
return fmt.Errorf("name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
var list sourcev1.GitRepositoryList
|
var list sourcev1.GitRepositoryList
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
|
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no source found in %s namespace", namespace)
|
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var repository sourcev1.GitRepository
|
var repository sourcev1.GitRepository
|
||||||
|
|||||||
@@ -49,27 +49,27 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
if !exportAll && len(args) < 1 {
|
if !exportArgs.all && len(args) < 1 {
|
||||||
return fmt.Errorf("name is required")
|
return fmt.Errorf("name is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exportAll {
|
if exportArgs.all {
|
||||||
var list sourcev1.HelmRepositoryList
|
var list sourcev1.HelmRepositoryList
|
||||||
err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
|
err = kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no source found in %s namespace", namespace)
|
logger.Failuref("no source found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
} else {
|
} else {
|
||||||
name := args[0]
|
name := args[0]
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var repository sourcev1.HelmRepository
|
var repository sourcev1.HelmRepository
|
||||||
|
|||||||
@@ -36,10 +36,14 @@ var getCmd = &cobra.Command{
|
|||||||
Long: "The get sub-commands print the statuses of sources and resources.",
|
Long: "The get sub-commands print the statuses of sources and resources.",
|
||||||
}
|
}
|
||||||
|
|
||||||
var allNamespaces bool
|
type GetFlags struct {
|
||||||
|
allNamespaces bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var getArgs GetFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getCmd.PersistentFlags().BoolVarP(&allNamespaces, "all-namespaces", "A", false,
|
getCmd.PersistentFlags().BoolVarP(&getArgs.allNamespaces, "all-namespaces", "A", false,
|
||||||
"list the requested object(s) across all namespaces")
|
"list the requested object(s) across all namespaces")
|
||||||
rootCmd.AddCommand(getCmd)
|
rootCmd.AddCommand(getCmd)
|
||||||
}
|
}
|
||||||
@@ -74,17 +78,17 @@ type getCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var listOpts []client.ListOption
|
var listOpts []client.ListOption
|
||||||
if !allNamespaces {
|
if !getArgs.allNamespaces {
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
||||||
}
|
}
|
||||||
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
|
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,14 +96,14 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if get.list.len() == 0 {
|
if get.list.len() == 0 {
|
||||||
logger.Failuref("no %s objects found in %s namespace", get.kind, namespace)
|
logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
header := get.list.headers(allNamespaces)
|
header := get.list.headers(getArgs.allNamespaces)
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
for i := 0; i < get.list.len(); i++ {
|
for i := 0; i < get.list.len(); i++ {
|
||||||
row := get.list.summariseItem(i, allNamespaces)
|
row := get.list.summariseItem(i, getArgs.allNamespaces)
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
}
|
}
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
utils.PrintTable(os.Stdout, header, rows)
|
||||||
|
|||||||
@@ -47,17 +47,17 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAlertCmdRun(cmd *cobra.Command, args []string) error {
|
func getAlertCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var listOpts []client.ListOption
|
var listOpts []client.ListOption
|
||||||
if !allNamespaces {
|
if !getArgs.allNamespaces {
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
||||||
}
|
}
|
||||||
var list notificationv1.AlertList
|
var list notificationv1.AlertList
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
err = kubeClient.List(ctx, &list, listOpts...)
|
||||||
@@ -66,12 +66,12 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no alerts found in %s namespace", namespace)
|
logger.Failuref("no alerts found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Suspended"}
|
header := []string{"Name", "Ready", "Message", "Suspended"}
|
||||||
if allNamespaces {
|
if getArgs.allNamespaces {
|
||||||
header = append([]string{"Namespace"}, header...)
|
header = append([]string{"Namespace"}, header...)
|
||||||
}
|
}
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
@@ -92,7 +92,7 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
|
strings.Title(strconv.FormatBool(alert.Spec.Suspend)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allNamespaces {
|
if getArgs.allNamespaces {
|
||||||
row = append([]string{alert.Namespace}, row...)
|
row = append([]string{alert.Namespace}, row...)
|
||||||
}
|
}
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
|
|||||||
@@ -45,17 +45,17 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var listOpts []client.ListOption
|
var listOpts []client.ListOption
|
||||||
if !allNamespaces {
|
if !getArgs.allNamespaces {
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
||||||
}
|
}
|
||||||
var list notificationv1.ProviderList
|
var list notificationv1.ProviderList
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
err = kubeClient.List(ctx, &list, listOpts...)
|
||||||
@@ -64,12 +64,12 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no providers found in %s namespace", namespace)
|
logger.Failuref("no providers found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message"}
|
header := []string{"Name", "Ready", "Message"}
|
||||||
if allNamespaces {
|
if getArgs.allNamespaces {
|
||||||
header = append([]string{"Namespace"}, header...)
|
header = append([]string{"Namespace"}, header...)
|
||||||
}
|
}
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
@@ -88,7 +88,7 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
"waiting to be reconciled",
|
"waiting to be reconciled",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allNamespaces {
|
if getArgs.allNamespaces {
|
||||||
row = append([]string{provider.Namespace}, row...)
|
row = append([]string{provider.Namespace}, row...)
|
||||||
}
|
}
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
|
|||||||
@@ -17,20 +17,11 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getHelmReleaseCmd = &cobra.Command{
|
var getHelmReleaseCmd = &cobra.Command{
|
||||||
@@ -41,66 +32,28 @@ var getHelmReleaseCmd = &cobra.Command{
|
|||||||
Example: ` # List all Helm releases and their status
|
Example: ` # List all Helm releases and their status
|
||||||
flux get helmreleases
|
flux get helmreleases
|
||||||
`,
|
`,
|
||||||
RunE: getHelmReleaseCmdRun,
|
RunE: getCommand{
|
||||||
|
apiType: helmReleaseType,
|
||||||
|
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getCmd.AddCommand(getHelmReleaseCmd)
|
getCmd.AddCommand(getHelmReleaseCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
func (a helmReleaseListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
item := a.Items[i]
|
||||||
defer cancel()
|
revision := item.Status.LastAppliedRevision
|
||||||
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
return append(nameColumns(&item, includeNamespace),
|
||||||
if err != nil {
|
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
func (a helmReleaseListAdapter) headers(includeNamespace bool) []string {
|
||||||
var listOpts []client.ListOption
|
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
||||||
if !allNamespaces {
|
if includeNamespace {
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
headers = append([]string{"Namespace"}, headers...)
|
||||||
}
|
}
|
||||||
var list helmv2.HelmReleaseList
|
return headers
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no releases found in %s namespace", namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
|
||||||
if allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, helmRelease := range list.Items {
|
|
||||||
row := []string{}
|
|
||||||
if c := apimeta.FindStatusCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
helmRelease.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
helmRelease.Status.LastAppliedRevision,
|
|
||||||
strings.Title(strconv.FormatBool(helmRelease.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
helmRelease.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
helmRelease.Status.LastAppliedRevision,
|
|
||||||
strings.Title(strconv.FormatBool(helmRelease.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allNamespaces {
|
|
||||||
row = append([]string{helmRelease.Namespace}, row...)
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,11 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var getKsCmd = &cobra.Command{
|
var getKsCmd = &cobra.Command{
|
||||||
@@ -40,66 +32,28 @@ var getKsCmd = &cobra.Command{
|
|||||||
Example: ` # List all kustomizations and their status
|
Example: ` # List all kustomizations and their status
|
||||||
flux get kustomizations
|
flux get kustomizations
|
||||||
`,
|
`,
|
||||||
RunE: getKsCmdRun,
|
RunE: getCommand{
|
||||||
|
apiType: kustomizationType,
|
||||||
|
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getCmd.AddCommand(getKsCmd)
|
getCmd.AddCommand(getKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKsCmdRun(cmd *cobra.Command, args []string) error {
|
func (a kustomizationListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
item := a.Items[i]
|
||||||
defer cancel()
|
revision := item.Status.LastAppliedRevision
|
||||||
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
return append(nameColumns(&item, includeNamespace),
|
||||||
if err != nil {
|
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
func (a kustomizationListAdapter) headers(includeNamespace bool) []string {
|
||||||
var listOpts []client.ListOption
|
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
||||||
if !allNamespaces {
|
if includeNamespace {
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
headers = append([]string{"Namespace"}, headers...)
|
||||||
}
|
}
|
||||||
var list kustomizev1.KustomizationList
|
return headers
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no kustomizations found in %s namespace", namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
|
||||||
if allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, kustomization := range list.Items {
|
|
||||||
row := []string{}
|
|
||||||
if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
kustomization.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
kustomization.Status.LastAppliedRevision,
|
|
||||||
strings.Title(strconv.FormatBool(kustomization.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
kustomization.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
kustomization.Status.LastAppliedRevision,
|
|
||||||
strings.Title(strconv.FormatBool(kustomization.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allNamespaces {
|
|
||||||
row = append([]string{kustomization.Namespace}, row...)
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,17 +47,17 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var listOpts []client.ListOption
|
var listOpts []client.ListOption
|
||||||
if !allNamespaces {
|
if !getArgs.allNamespaces {
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
|
||||||
}
|
}
|
||||||
var list notificationv1.ReceiverList
|
var list notificationv1.ReceiverList
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
err = kubeClient.List(ctx, &list, listOpts...)
|
||||||
@@ -66,12 +66,12 @@ func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(list.Items) == 0 {
|
if len(list.Items) == 0 {
|
||||||
logger.Failuref("no receivers found in %s namespace", namespace)
|
logger.Failuref("no receivers found in %s namespace", rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Suspended"}
|
header := []string{"Name", "Ready", "Message", "Suspended"}
|
||||||
if allNamespaces {
|
if getArgs.allNamespaces {
|
||||||
header = append([]string{"Namespace"}, header...)
|
header = append([]string{"Namespace"}, header...)
|
||||||
}
|
}
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
|
|||||||
@@ -17,19 +17,11 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceBucketCmd = &cobra.Command{
|
var getSourceBucketCmd = &cobra.Command{
|
||||||
@@ -42,70 +34,31 @@ 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: getSourceBucketCmdRun,
|
RunE: getCommand{
|
||||||
|
apiType: bucketType,
|
||||||
|
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getSourceCmd.AddCommand(getSourceBucketCmd)
|
getSourceCmd.AddCommand(getSourceBucketCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
func (a *bucketListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
item := a.Items[i]
|
||||||
defer cancel()
|
var revision string
|
||||||
|
if item.GetArtifact() != nil {
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
revision = item.GetArtifact().Revision
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
var listOpts []client.ListOption
|
return append(nameColumns(&item, includeNamespace),
|
||||||
if !allNamespaces {
|
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
}
|
||||||
}
|
|
||||||
var list sourcev1.BucketList
|
func (a bucketListAdapter) headers(includeNamespace bool) []string {
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
||||||
if err != nil {
|
if includeNamespace {
|
||||||
return err
|
headers = append([]string{"Namespace"}, headers...)
|
||||||
}
|
}
|
||||||
|
return headers
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no bucket sources found in %s namespace", namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
|
||||||
if allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, source := range list.Items {
|
|
||||||
var row []string
|
|
||||||
var revision string
|
|
||||||
if source.GetArtifact() != nil {
|
|
||||||
revision = source.GetArtifact().Revision
|
|
||||||
}
|
|
||||||
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
source.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
revision,
|
|
||||||
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
source.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
revision,
|
|
||||||
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allNamespaces {
|
|
||||||
row = append([]string{source.Namespace}, row...)
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,11 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceHelmChartCmd = &cobra.Command{
|
var getSourceHelmChartCmd = &cobra.Command{
|
||||||
@@ -42,70 +34,31 @@ var getSourceHelmChartCmd = &cobra.Command{
|
|||||||
# List Helm charts from all namespaces
|
# List Helm charts from all namespaces
|
||||||
flux get sources chart --all-namespaces
|
flux get sources chart --all-namespaces
|
||||||
`,
|
`,
|
||||||
RunE: getSourceHelmChartCmdRun,
|
RunE: getCommand{
|
||||||
|
apiType: helmChartType,
|
||||||
|
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getSourceCmd.AddCommand(getSourceHelmChartCmd)
|
getSourceCmd.AddCommand(getSourceHelmChartCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error {
|
func (a *helmChartListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
item := a.Items[i]
|
||||||
defer cancel()
|
var revision string
|
||||||
|
if item.GetArtifact() != nil {
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
revision = item.GetArtifact().Revision
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
var listOpts []client.ListOption
|
return append(nameColumns(&item, includeNamespace),
|
||||||
if !allNamespaces {
|
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
}
|
||||||
}
|
|
||||||
var list sourcev1.HelmChartList
|
func (a helmChartListAdapter) headers(includeNamespace bool) []string {
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
||||||
if err != nil {
|
if includeNamespace {
|
||||||
return err
|
headers = append([]string{"Namespace"}, headers...)
|
||||||
}
|
}
|
||||||
|
return headers
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no chart sources found in %s namespace", namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
|
||||||
if allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, source := range list.Items {
|
|
||||||
var row []string
|
|
||||||
var revision string
|
|
||||||
if source.GetArtifact() != nil {
|
|
||||||
revision = source.GetArtifact().Revision
|
|
||||||
}
|
|
||||||
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
source.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
revision,
|
|
||||||
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
source.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
revision,
|
|
||||||
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allNamespaces {
|
|
||||||
row = append([]string{source.Namespace}, row...)
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,11 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceGitCmd = &cobra.Command{
|
var getSourceGitCmd = &cobra.Command{
|
||||||
@@ -42,70 +34,31 @@ var getSourceGitCmd = &cobra.Command{
|
|||||||
# List Git repositories from all namespaces
|
# List Git repositories from all namespaces
|
||||||
flux get sources git --all-namespaces
|
flux get sources git --all-namespaces
|
||||||
`,
|
`,
|
||||||
RunE: getSourceGitCmdRun,
|
RunE: getCommand{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getSourceCmd.AddCommand(getSourceGitCmd)
|
getSourceCmd.AddCommand(getSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
func (a *gitRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
item := a.Items[i]
|
||||||
defer cancel()
|
var revision string
|
||||||
|
if item.GetArtifact() != nil {
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
revision = item.GetArtifact().Revision
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
var listOpts []client.ListOption
|
return append(nameColumns(&item, includeNamespace),
|
||||||
if !allNamespaces {
|
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
}
|
||||||
}
|
|
||||||
var list sourcev1.GitRepositoryList
|
func (a gitRepositoryListAdapter) headers(includeNamespace bool) []string {
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
||||||
if err != nil {
|
if includeNamespace {
|
||||||
return err
|
headers = append([]string{"Namespace"}, headers...)
|
||||||
}
|
}
|
||||||
|
return headers
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no git sources found in %s namespace", namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
|
||||||
if allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, source := range list.Items {
|
|
||||||
var row []string
|
|
||||||
var revision string
|
|
||||||
if source.GetArtifact() != nil {
|
|
||||||
revision = source.GetArtifact().Revision
|
|
||||||
}
|
|
||||||
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
source.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
revision,
|
|
||||||
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
source.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
revision,
|
|
||||||
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allNamespaces {
|
|
||||||
row = append([]string{source.Namespace}, row...)
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,11 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var getSourceHelmCmd = &cobra.Command{
|
var getSourceHelmCmd = &cobra.Command{
|
||||||
@@ -42,70 +34,31 @@ var getSourceHelmCmd = &cobra.Command{
|
|||||||
# List Helm repositories from all namespaces
|
# List Helm repositories from all namespaces
|
||||||
flux get sources helm --all-namespaces
|
flux get sources helm --all-namespaces
|
||||||
`,
|
`,
|
||||||
RunE: getSourceHelmCmdRun,
|
RunE: getCommand{
|
||||||
|
apiType: helmRepositoryType,
|
||||||
|
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
getSourceCmd.AddCommand(getSourceHelmCmd)
|
getSourceCmd.AddCommand(getSourceHelmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
func (a *helmRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
item := a.Items[i]
|
||||||
defer cancel()
|
var revision string
|
||||||
|
if item.GetArtifact() != nil {
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
revision = item.GetArtifact().Revision
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
status, msg := statusAndMessage(item.Status.Conditions)
|
||||||
var listOpts []client.ListOption
|
return append(nameColumns(&item, includeNamespace),
|
||||||
if !allNamespaces {
|
status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
}
|
||||||
}
|
|
||||||
var list sourcev1.HelmRepositoryList
|
func (a helmRepositoryListAdapter) headers(includeNamespace bool) []string {
|
||||||
err = kubeClient.List(ctx, &list, listOpts...)
|
headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
||||||
if err != nil {
|
if includeNamespace {
|
||||||
return err
|
headers = append([]string{"Namespace"}, headers...)
|
||||||
}
|
}
|
||||||
|
return headers
|
||||||
if len(list.Items) == 0 {
|
|
||||||
logger.Failuref("no helm sources found in %s namespace", namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
|
|
||||||
if allNamespaces {
|
|
||||||
header = append([]string{"Namespace"}, header...)
|
|
||||||
}
|
|
||||||
var rows [][]string
|
|
||||||
for _, source := range list.Items {
|
|
||||||
var row []string
|
|
||||||
var revision string
|
|
||||||
if source.GetArtifact() != nil {
|
|
||||||
revision = source.GetArtifact().Revision
|
|
||||||
}
|
|
||||||
if c := apimeta.FindStatusCondition(source.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
row = []string{
|
|
||||||
source.GetName(),
|
|
||||||
string(c.Status),
|
|
||||||
c.Message,
|
|
||||||
revision,
|
|
||||||
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row = []string{
|
|
||||||
source.GetName(),
|
|
||||||
string(metav1.ConditionFalse),
|
|
||||||
"waiting to be reconciled",
|
|
||||||
revision,
|
|
||||||
strings.Title(strconv.FormatBool(source.Spec.Suspend)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allNamespaces {
|
|
||||||
row = append([]string{source.Namespace}, row...)
|
|
||||||
}
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
51
cmd/flux/helmrelease.go
Normal file
51
cmd/flux/helmrelease.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// helmv2.HelmRelease
|
||||||
|
|
||||||
|
var helmReleaseType = apiType{
|
||||||
|
kind: helmv2.HelmReleaseKind,
|
||||||
|
humanKind: "helmreleases",
|
||||||
|
}
|
||||||
|
|
||||||
|
type helmReleaseAdapter struct {
|
||||||
|
*helmv2.HelmRelease
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h helmReleaseAdapter) asClientObject() client.Object {
|
||||||
|
return h.HelmRelease
|
||||||
|
}
|
||||||
|
|
||||||
|
// helmv2.HelmReleaseList
|
||||||
|
|
||||||
|
type helmReleaseListAdapter struct {
|
||||||
|
*helmv2.HelmReleaseList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h helmReleaseListAdapter) asClientList() client.ObjectList {
|
||||||
|
return h.HelmReleaseList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h helmReleaseListAdapter) len() int {
|
||||||
|
return len(h.HelmReleaseList.Items)
|
||||||
|
}
|
||||||
@@ -63,7 +63,7 @@ var (
|
|||||||
installWatchAllNamespaces bool
|
installWatchAllNamespaces bool
|
||||||
installNetworkPolicy bool
|
installNetworkPolicy bool
|
||||||
installArch flags.Arch
|
installArch flags.Arch
|
||||||
installLogLevel = flags.LogLevel(defaults.LogLevel)
|
installLogLevel = flags.LogLevel(rootArgs.defaults.LogLevel)
|
||||||
installClusterDomain string
|
installClusterDomain string
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -72,34 +72,34 @@ func init() {
|
|||||||
"write the install manifests to stdout and exit")
|
"write the install manifests to stdout and exit")
|
||||||
installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false,
|
installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false,
|
||||||
"only print the object that would be applied")
|
"only print the object that would be applied")
|
||||||
installCmd.Flags().StringVarP(&installVersion, "version", "v", defaults.Version,
|
installCmd.Flags().StringVarP(&installVersion, "version", "v", rootArgs.defaults.Version,
|
||||||
"toolkit version")
|
"toolkit version")
|
||||||
installCmd.Flags().StringSliceVar(&installDefaultComponents, "components", defaults.Components,
|
installCmd.Flags().StringSliceVar(&installDefaultComponents, "components", rootArgs.defaults.Components,
|
||||||
"list of components, accepts comma-separated values")
|
"list of components, accepts comma-separated values")
|
||||||
installCmd.Flags().StringSliceVar(&installExtraComponents, "components-extra", nil,
|
installCmd.Flags().StringSliceVar(&installExtraComponents, "components-extra", nil,
|
||||||
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
||||||
installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory")
|
installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory")
|
||||||
installCmd.Flags().StringVar(&installRegistry, "registry", defaults.Registry,
|
installCmd.Flags().StringVar(&installRegistry, "registry", rootArgs.defaults.Registry,
|
||||||
"container registry where the toolkit images are published")
|
"container registry where the toolkit images are published")
|
||||||
installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "",
|
installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "",
|
||||||
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
||||||
installCmd.Flags().Var(&installArch, "arch", installArch.Description())
|
installCmd.Flags().Var(&installArch, "arch", installArch.Description())
|
||||||
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", defaults.WatchAllNamespaces,
|
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", rootArgs.defaults.WatchAllNamespaces,
|
||||||
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
|
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
|
||||||
installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description())
|
installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description())
|
||||||
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", defaults.NetworkPolicy,
|
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy,
|
||||||
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
||||||
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", defaults.ClusterDomain, "internal cluster domain")
|
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||||
installCmd.Flags().MarkHidden("manifests")
|
installCmd.Flags().MarkHidden("manifests")
|
||||||
installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
||||||
rootCmd.AddCommand(installCmd)
|
rootCmd.AddCommand(installCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func installCmdRun(cmd *cobra.Command, args []string) error {
|
func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", namespace)
|
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -118,16 +118,16 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
opts := install.Options{
|
opts := install.Options{
|
||||||
BaseURL: installManifestsPath,
|
BaseURL: installManifestsPath,
|
||||||
Version: installVersion,
|
Version: installVersion,
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Components: components,
|
Components: components,
|
||||||
Registry: installRegistry,
|
Registry: installRegistry,
|
||||||
ImagePullSecret: installImagePullSecret,
|
ImagePullSecret: installImagePullSecret,
|
||||||
WatchAllNamespaces: installWatchAllNamespaces,
|
WatchAllNamespaces: installWatchAllNamespaces,
|
||||||
NetworkPolicy: installNetworkPolicy,
|
NetworkPolicy: installNetworkPolicy,
|
||||||
LogLevel: installLogLevel.String(),
|
LogLevel: installLogLevel.String(),
|
||||||
NotificationController: defaults.NotificationController,
|
NotificationController: rootArgs.defaults.NotificationController,
|
||||||
ManifestFile: fmt.Sprintf("%s.yaml", namespace),
|
ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace),
|
||||||
Timeout: timeout,
|
Timeout: rootArgs.timeout,
|
||||||
ClusterDomain: installClusterDomain,
|
ClusterDomain: installClusterDomain,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("install failed: %w", err)
|
return fmt.Errorf("install failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if verbose {
|
if rootArgs.verbose {
|
||||||
fmt.Print(manifest.Content)
|
fmt.Print(manifest.Content)
|
||||||
} else if installExport {
|
} else if installExport {
|
||||||
fmt.Println("---")
|
fmt.Println("---")
|
||||||
@@ -156,9 +156,9 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Successf("manifests build completed")
|
logger.Successf("manifests build completed")
|
||||||
logger.Actionf("installing components in %s namespace", namespace)
|
logger.Actionf("installing components in %s namespace", rootArgs.namespace)
|
||||||
applyOutput := utils.ModeStderrOS
|
applyOutput := utils.ModeStderrOS
|
||||||
if verbose {
|
if rootArgs.verbose {
|
||||||
applyOutput = utils.ModeOS
|
applyOutput = utils.ModeOS
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
kubectlArgs = append(kubectlArgs, "--dry-run=client")
|
kubectlArgs = append(kubectlArgs, "--dry-run=client")
|
||||||
applyOutput = utils.ModeOS
|
applyOutput = utils.ModeOS
|
||||||
}
|
}
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||||
return fmt.Errorf("install failed")
|
return fmt.Errorf("install failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,8 +180,8 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
logger.Waitingf("verifying installation")
|
logger.Waitingf("verifying installation")
|
||||||
for _, deployment := range components {
|
for _, deployment := range components {
|
||||||
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
|
kubectlArgs = []string{"-n", rootArgs.namespace, "rollout", "status", "deployment", deployment, "--timeout", rootArgs.timeout.String()}
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||||
return fmt.Errorf("install failed")
|
return fmt.Errorf("install failed")
|
||||||
} else {
|
} else {
|
||||||
logger.Successf("%s ready", deployment)
|
logger.Successf("%s ready", deployment)
|
||||||
|
|||||||
51
cmd/flux/kustomization.go
Normal file
51
cmd/flux/kustomization.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// kustomizev1.Kustomization
|
||||||
|
|
||||||
|
var kustomizationType = apiType{
|
||||||
|
kind: kustomizev1.KustomizationKind,
|
||||||
|
humanKind: "kustomizations",
|
||||||
|
}
|
||||||
|
|
||||||
|
type kustomizationAdapter struct {
|
||||||
|
*kustomizev1.Kustomization
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a kustomizationAdapter) asClientObject() client.Object {
|
||||||
|
return a.Kustomization
|
||||||
|
}
|
||||||
|
|
||||||
|
// kustomizev1.KustomizationList
|
||||||
|
|
||||||
|
type kustomizationListAdapter struct {
|
||||||
|
*kustomizev1.KustomizationList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a kustomizationListAdapter) asClientList() client.ObjectList {
|
||||||
|
return a.KustomizationList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a kustomizationListAdapter) len() int {
|
||||||
|
return len(a.KustomizationList.Items)
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ import (
|
|||||||
"github.com/spf13/cobra/doc"
|
"github.com/spf13/cobra/doc"
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||||
|
|
||||||
fluxlog "github.com/fluxcd/flux2/pkg/log"
|
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -94,22 +93,32 @@ var rootCmd = &cobra.Command{
|
|||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var logger = stderrLogger{stderr: os.Stderr}
|
||||||
|
|
||||||
|
type rootFlags struct {
|
||||||
kubeconfig string
|
kubeconfig string
|
||||||
kubecontext string
|
kubecontext string
|
||||||
namespace string
|
namespace string
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
verbose bool
|
verbose bool
|
||||||
pollInterval = 2 * time.Second
|
pollInterval time.Duration
|
||||||
logger fluxlog.Logger = stderrLogger{stderr: os.Stderr}
|
defaults install.Options
|
||||||
defaults = install.MakeDefaultOptions()
|
}
|
||||||
)
|
|
||||||
|
var rootArgs = NewRootFlags()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", defaults.Namespace, "the namespace scope for this operation")
|
rootCmd.PersistentFlags().StringVarP(&rootArgs.namespace, "namespace", "n", rootArgs.defaults.Namespace, "the namespace scope for this operation")
|
||||||
rootCmd.PersistentFlags().DurationVar(&timeout, "timeout", 5*time.Minute, "timeout for this operation")
|
rootCmd.PersistentFlags().DurationVar(&rootArgs.timeout, "timeout", 5*time.Minute, "timeout for this operation")
|
||||||
rootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "print generated objects")
|
rootCmd.PersistentFlags().BoolVar(&rootArgs.verbose, "verbose", false, "print generated objects")
|
||||||
rootCmd.PersistentFlags().StringVarP(&kubecontext, "context", "", "", "kubernetes context to use")
|
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubecontext, "context", "", "", "kubernetes context to use")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRootFlags() rootFlags {
|
||||||
|
return rootFlags{
|
||||||
|
pollInterval: 2 * time.Second,
|
||||||
|
defaults: install.MakeDefaultOptions(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -124,22 +133,22 @@ func main() {
|
|||||||
|
|
||||||
func kubeconfigFlag() {
|
func kubeconfigFlag() {
|
||||||
if home := homeDir(); home != "" {
|
if home := homeDir(); home != "" {
|
||||||
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "", filepath.Join(home, ".kube", "config"),
|
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", filepath.Join(home, ".kube", "config"),
|
||||||
"path to the kubeconfig file")
|
"path to the kubeconfig file")
|
||||||
} else {
|
} else {
|
||||||
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "", "",
|
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "",
|
||||||
"absolute path to the kubeconfig file")
|
"absolute path to the kubeconfig file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(os.Getenv("KUBECONFIG")) > 0 {
|
if len(os.Getenv("KUBECONFIG")) > 0 {
|
||||||
kubeconfig = os.Getenv("KUBECONFIG")
|
rootArgs.kubeconfig = os.Getenv("KUBECONFIG")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateDocs() {
|
func generateDocs() {
|
||||||
args := os.Args[1:]
|
args := os.Args[1:]
|
||||||
if len(args) > 0 && args[0] == "docgen" {
|
if len(args) > 0 && args[0] == "docgen" {
|
||||||
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "", "~/.kube/config",
|
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "~/.kube/config",
|
||||||
"path to the kubeconfig file")
|
"path to the kubeconfig file")
|
||||||
rootCmd.DisableAutoGenTag = true
|
rootCmd.DisableAutoGenTag = true
|
||||||
err := doc.GenMarkdownTree(rootCmd, "./docs/cmd")
|
err := doc.GenMarkdownTree(rootCmd, "./docs/cmd")
|
||||||
|
|||||||
@@ -69,16 +69,16 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("resource is suspended")
|
return fmt.Errorf("resource is suspended")
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, namespace)
|
logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, rootArgs.namespace)
|
||||||
if err := requestReconciliation(ctx, kubeClient, namespacedName, reconcile.object); err != nil {
|
if err := requestReconciliation(ctx, kubeClient, namespacedName, reconcile.object); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
|
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
|
||||||
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
|
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
|
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,16 +51,16 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("resource is suspended")
|
return fmt.Errorf("resource is suspended")
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("annotating Alert %s in %s namespace", name, namespace)
|
logger.Actionf("annotating Alert %s in %s namespace", name, rootArgs.namespace)
|
||||||
if alert.Annotations == nil {
|
if alert.Annotations == nil {
|
||||||
alert.Annotations = map[string]string{
|
alert.Annotations = map[string]string{
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
||||||
@@ -89,7 +89,7 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Successf("Alert annotated")
|
logger.Successf("Alert annotated")
|
||||||
|
|
||||||
logger.Waitingf("waiting for reconciliation")
|
logger.Waitingf("waiting for reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,20 +51,20 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("annotating Provider %s in %s namespace", name, namespace)
|
logger.Actionf("annotating Provider %s in %s namespace", name, rootArgs.namespace)
|
||||||
var alertProvider notificationv1.Provider
|
var alertProvider notificationv1.Provider
|
||||||
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
|
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -84,7 +84,7 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Successf("Provider annotated")
|
logger.Successf("Provider annotated")
|
||||||
|
|
||||||
logger.Waitingf("waiting for reconciliation")
|
logger.Waitingf("waiting for reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isAlertProviderReady(ctx, kubeClient, namespacedName, &alertProvider)); err != nil {
|
isAlertProviderReady(ctx, kubeClient, namespacedName, &alertProvider)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,12 +51,14 @@ The reconcile kustomization command triggers a reconciliation of a HelmRelease r
|
|||||||
RunE: reconcileHrCmdRun,
|
RunE: reconcileHrCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type reconcileHelmReleaseFlags struct {
|
||||||
syncHrWithSource bool
|
syncHrWithSource bool
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var rhrArgs reconcileHelmReleaseFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
reconcileHrCmd.Flags().BoolVar(&syncHrWithSource, "with-source", false, "reconcile HelmRelease source")
|
reconcileHrCmd.Flags().BoolVar(&rhrArgs.syncHrWithSource, "with-source", false, "reconcile HelmRelease source")
|
||||||
|
|
||||||
reconcileCmd.AddCommand(reconcileHrCmd)
|
reconcileCmd.AddCommand(reconcileHrCmd)
|
||||||
}
|
}
|
||||||
@@ -67,16 +69,16 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,29 +92,43 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("resource is suspended")
|
return fmt.Errorf("resource is suspended")
|
||||||
}
|
}
|
||||||
|
|
||||||
if syncHrWithSource {
|
if rhrArgs.syncHrWithSource {
|
||||||
|
nsCopy := rootArgs.namespace
|
||||||
|
if helmRelease.Spec.Chart.Spec.SourceRef.Namespace != "" {
|
||||||
|
rootArgs.namespace = helmRelease.Spec.Chart.Spec.SourceRef.Namespace
|
||||||
|
}
|
||||||
switch helmRelease.Spec.Chart.Spec.SourceRef.Kind {
|
switch helmRelease.Spec.Chart.Spec.SourceRef.Kind {
|
||||||
case sourcev1.HelmRepositoryKind:
|
case sourcev1.HelmRepositoryKind:
|
||||||
err = reconcileSourceHelmCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
err = reconcileCommand{
|
||||||
|
apiType: helmRepositoryType,
|
||||||
|
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||||
case sourcev1.GitRepositoryKind:
|
case sourcev1.GitRepositoryKind:
|
||||||
err = reconcileSourceGitCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
err = reconcileCommand{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
|
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||||
case sourcev1.BucketKind:
|
case sourcev1.BucketKind:
|
||||||
err = reconcileSourceBucketCmdRun(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
err = reconcileCommand{
|
||||||
|
apiType: bucketType,
|
||||||
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
rootArgs.namespace = nsCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt
|
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt
|
||||||
logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace)
|
logger.Actionf("annotating HelmRelease %s in %s namespace", name, rootArgs.namespace)
|
||||||
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
|
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("HelmRelease annotated")
|
logger.Successf("HelmRelease annotated")
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmRelease reconciliation")
|
logger.Waitingf("waiting for HelmRelease reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
|
helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -50,12 +50,14 @@ The reconcile kustomization command triggers a reconciliation of a Kustomization
|
|||||||
RunE: reconcileKsCmdRun,
|
RunE: reconcileKsCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type reconcileKsFlags struct {
|
||||||
syncKsWithSource bool
|
syncKsWithSource bool
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var rksArgs reconcileKsFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
reconcileKsCmd.Flags().BoolVar(&syncKsWithSource, "with-source", false, "reconcile Kustomization source")
|
reconcileKsCmd.Flags().BoolVar(&rksArgs.syncKsWithSource, "with-source", false, "reconcile Kustomization source")
|
||||||
|
|
||||||
reconcileCmd.AddCommand(reconcileKsCmd)
|
reconcileCmd.AddCommand(reconcileKsCmd)
|
||||||
}
|
}
|
||||||
@@ -66,16 +68,16 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var kustomization kustomizev1.Kustomization
|
var kustomization kustomizev1.Kustomization
|
||||||
@@ -88,20 +90,31 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("resource is suspended")
|
return fmt.Errorf("resource is suspended")
|
||||||
}
|
}
|
||||||
|
|
||||||
if syncKsWithSource {
|
if rksArgs.syncKsWithSource {
|
||||||
|
nsCopy := rootArgs.namespace
|
||||||
|
if kustomization.Spec.SourceRef.Namespace != "" {
|
||||||
|
rootArgs.namespace = kustomization.Spec.SourceRef.Namespace
|
||||||
|
}
|
||||||
switch kustomization.Spec.SourceRef.Kind {
|
switch kustomization.Spec.SourceRef.Kind {
|
||||||
case sourcev1.GitRepositoryKind:
|
case sourcev1.GitRepositoryKind:
|
||||||
err = reconcileSourceGitCmdRun(nil, []string{kustomization.Spec.SourceRef.Name})
|
err = reconcileCommand{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
|
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
|
||||||
case sourcev1.BucketKind:
|
case sourcev1.BucketKind:
|
||||||
err = reconcileSourceBucketCmdRun(nil, []string{kustomization.Spec.SourceRef.Name})
|
err = reconcileCommand{
|
||||||
|
apiType: bucketType,
|
||||||
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
rootArgs.namespace = nsCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
|
lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
|
||||||
logger.Actionf("annotating Kustomization %s in %s namespace", name, namespace)
|
logger.Actionf("annotating Kustomization %s in %s namespace", name, rootArgs.namespace)
|
||||||
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
|
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -109,7 +122,7 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
logger.Waitingf("waiting for Kustomization reconciliation")
|
logger.Waitingf("waiting for Kustomization reconciliation")
|
||||||
if err := wait.PollImmediate(
|
if err := wait.PollImmediate(
|
||||||
pollInterval, timeout,
|
rootArgs.pollInterval, rootArgs.timeout,
|
||||||
kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
|
kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -51,16 +51,16 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("resource is suspended")
|
return fmt.Errorf("resource is suspended")
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("annotating Receiver %s in %s namespace", name, namespace)
|
logger.Actionf("annotating Receiver %s in %s namespace", name, rootArgs.namespace)
|
||||||
if receiver.Annotations == nil {
|
if receiver.Annotations == nil {
|
||||||
receiver.Annotations = map[string]string{
|
receiver.Annotations = map[string]string{
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
||||||
@@ -88,7 +88,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Successf("Receiver annotated")
|
logger.Successf("Receiver annotated")
|
||||||
|
|
||||||
logger.Waitingf("waiting for Receiver reconciliation")
|
logger.Waitingf("waiting for Receiver reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
|
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"k8s.io/client-go/util/retry"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
|
|
||||||
"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"
|
||||||
@@ -43,64 +37,16 @@ var reconcileSourceBucketCmd = &cobra.Command{
|
|||||||
Example: ` # Trigger a reconciliation for an existing source
|
Example: ` # Trigger a reconciliation for an existing source
|
||||||
flux reconcile source bucket podinfo
|
flux reconcile source bucket podinfo
|
||||||
`,
|
`,
|
||||||
RunE: reconcileSourceBucketCmdRun,
|
RunE: reconcileCommand{
|
||||||
|
apiType: bucketType,
|
||||||
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
reconcileSourceCmd.AddCommand(reconcileSourceBucketCmd)
|
reconcileSourceCmd.AddCommand(reconcileSourceBucketCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("source name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var bucket sourcev1.Bucket
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if bucket.Spec.Suspend {
|
|
||||||
return fmt.Errorf("resource is suspended")
|
|
||||||
}
|
|
||||||
|
|
||||||
lastHandledReconcileAt := bucket.Status.LastHandledReconcileAt
|
|
||||||
logger.Actionf("annotating Bucket source %s in %s namespace", name, namespace)
|
|
||||||
if err := requestBucketReconciliation(ctx, kubeClient, namespacedName, &bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Bucket source annotated")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for Bucket source reconciliation")
|
|
||||||
if err := wait.PollImmediate(
|
|
||||||
pollInterval, timeout,
|
|
||||||
bucketReconciliationHandled(ctx, kubeClient, namespacedName, &bucket, lastHandledReconcileAt),
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Bucket source reconciliation completed")
|
|
||||||
|
|
||||||
if apimeta.IsStatusConditionFalse(bucket.Status.Conditions, meta.ReadyCondition) {
|
|
||||||
return fmt.Errorf("Bucket source reconciliation failed")
|
|
||||||
}
|
|
||||||
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBucketReady(ctx context.Context, kubeClient client.Client,
|
func isBucketReady(ctx context.Context, kubeClient client.Client,
|
||||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
|
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
|
||||||
return func() (bool, error) {
|
return func() (bool, error) {
|
||||||
@@ -126,30 +72,10 @@ func isBucketReady(ctx context.Context, kubeClient client.Client,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func bucketReconciliationHandled(ctx context.Context, kubeClient client.Client,
|
func (obj bucketAdapter) lastHandledReconcileRequest() string {
|
||||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket, lastHandledReconcileAt string) wait.ConditionFunc {
|
return obj.Status.GetLastHandledReconcileRequest()
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, bucket)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return bucket.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestBucketReconciliation(ctx context.Context, kubeClient client.Client,
|
func (obj bucketAdapter) successMessage() string {
|
||||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) error {
|
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
|
||||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
|
||||||
if err := kubeClient.Get(ctx, namespacedName, bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if bucket.Annotations == nil {
|
|
||||||
bucket.Annotations = map[string]string{
|
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bucket.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
|
||||||
}
|
|
||||||
return kubeClient.Update(ctx, bucket)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,21 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
"k8s.io/client-go/util/retry"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileSourceGitCmd = &cobra.Command{
|
var reconcileSourceGitCmd = &cobra.Command{
|
||||||
@@ -41,86 +29,20 @@ var reconcileSourceGitCmd = &cobra.Command{
|
|||||||
Example: ` # Trigger a git pull for an existing source
|
Example: ` # Trigger a git pull for an existing source
|
||||||
flux reconcile source git podinfo
|
flux reconcile source git podinfo
|
||||||
`,
|
`,
|
||||||
RunE: reconcileSourceGitCmdRun,
|
RunE: reconcileCommand{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
reconcileSourceCmd.AddCommand(reconcileSourceGitCmd)
|
reconcileSourceCmd.AddCommand(reconcileSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj gitRepositoryAdapter) lastHandledReconcileRequest() string {
|
||||||
if len(args) < 1 {
|
return obj.Status.GetLastHandledReconcileRequest()
|
||||||
return fmt.Errorf("source name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.GitRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if repository.Spec.Suspend {
|
|
||||||
return fmt.Errorf("resource is suspended")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("annotating GitRepository source %s in %s namespace", name, namespace)
|
|
||||||
if err := requestGitRepositoryReconciliation(ctx, kubeClient, namespacedName, &repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("GitRepository source annotated")
|
|
||||||
|
|
||||||
lastHandledReconcileAt := repository.Status.LastHandledReconcileAt
|
|
||||||
logger.Waitingf("waiting for GitRepository source reconciliation")
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
|
||||||
gitRepositoryReconciliationHandled(ctx, kubeClient, namespacedName, &repository, lastHandledReconcileAt)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("GitRepository source reconciliation completed")
|
|
||||||
|
|
||||||
if apimeta.IsStatusConditionFalse(repository.Status.Conditions, meta.ReadyCondition) {
|
|
||||||
return fmt.Errorf("GitRepository source reconciliation failed")
|
|
||||||
}
|
|
||||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gitRepositoryReconciliationHandled(ctx context.Context, kubeClient client.Client,
|
func (obj gitRepositoryAdapter) successMessage() string {
|
||||||
namespacedName types.NamespacedName, repository *sourcev1.GitRepository, lastHandledReconcileAt string) wait.ConditionFunc {
|
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, repository)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return repository.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestGitRepositoryReconciliation(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, repository *sourcev1.GitRepository) error {
|
|
||||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
|
||||||
if err := kubeClient.Get(ctx, namespacedName, repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if repository.Annotations == nil {
|
|
||||||
repository.Annotations = map[string]string{
|
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
repository.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
|
||||||
}
|
|
||||||
return kubeClient.Update(ctx, repository)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,22 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
"k8s.io/client-go/util/retry"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileSourceHelmCmd = &cobra.Command{
|
var reconcileSourceHelmCmd = &cobra.Command{
|
||||||
@@ -42,86 +29,20 @@ var reconcileSourceHelmCmd = &cobra.Command{
|
|||||||
Example: ` # Trigger a reconciliation for an existing source
|
Example: ` # Trigger a reconciliation for an existing source
|
||||||
flux reconcile source helm podinfo
|
flux reconcile source helm podinfo
|
||||||
`,
|
`,
|
||||||
RunE: reconcileSourceHelmCmdRun,
|
RunE: reconcileCommand{
|
||||||
|
apiType: helmRepositoryType,
|
||||||
|
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
reconcileSourceCmd.AddCommand(reconcileSourceHelmCmd)
|
reconcileSourceCmd.AddCommand(reconcileSourceHelmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj helmRepositoryAdapter) lastHandledReconcileRequest() string {
|
||||||
if len(args) < 1 {
|
return obj.Status.GetLastHandledReconcileRequest()
|
||||||
return fmt.Errorf("HelmRepository source name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.HelmRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if repository.Spec.Suspend {
|
|
||||||
return fmt.Errorf("resource is suspended")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("annotating HelmRepository source %s in %s namespace", name, namespace)
|
|
||||||
if err := requestHelmRepositoryReconciliation(ctx, kubeClient, namespacedName, &repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmRepository source annotated")
|
|
||||||
|
|
||||||
lastHandledReconcileAt := repository.Status.LastHandledReconcileAt
|
|
||||||
logger.Waitingf("waiting for HelmRepository source reconciliation")
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
|
||||||
helmRepositoryReconciliationHandled(ctx, kubeClient, namespacedName, &repository, lastHandledReconcileAt)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmRepository source reconciliation completed")
|
|
||||||
|
|
||||||
if apimeta.IsStatusConditionFalse(repository.Status.Conditions, meta.ReadyCondition) {
|
|
||||||
return fmt.Errorf("HelmRepository source reconciliation failed")
|
|
||||||
}
|
|
||||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func helmRepositoryReconciliationHandled(ctx context.Context, kubeClient client.Client,
|
func (obj helmRepositoryAdapter) successMessage() string {
|
||||||
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository, lastHandledReconcileAt string) wait.ConditionFunc {
|
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, repository)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return repository.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestHelmRepositoryReconciliation(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository) error {
|
|
||||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
|
||||||
if err := kubeClient.Get(ctx, namespacedName, repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if repository.Annotations == nil {
|
|
||||||
repository.Annotations = map[string]string{
|
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
repository.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
|
||||||
}
|
|
||||||
return kubeClient.Update(ctx, repository)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,16 +55,16 @@ func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, name, namespace)
|
logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, name, rootArgs.namespace)
|
||||||
resume.object.setUnsuspended()
|
resume.object.setUnsuspended()
|
||||||
if err := kubeClient.Update(ctx, resume.object.asClientObject()); err != nil {
|
if err := kubeClient.Update(ctx, resume.object.asClientObject()); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -81,7 +81,7 @@ func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Successf("%s resumed", resume.humanKind)
|
logger.Successf("%s resumed", resume.humanKind)
|
||||||
|
|
||||||
logger.Waitingf("waiting for %s reconciliation", resume.kind)
|
logger.Waitingf("waiting for %s reconciliation", resume.kind)
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isReady(ctx, kubeClient, namespacedName, resume.object)); err != nil {
|
isReady(ctx, kubeClient, namespacedName, resume.object)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,16 +54,16 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var alert notificationv1.Alert
|
var alert notificationv1.Alert
|
||||||
@@ -72,7 +72,7 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("resuming Alert %s in %s namespace", name, namespace)
|
logger.Actionf("resuming Alert %s in %s namespace", name, rootArgs.namespace)
|
||||||
alert.Spec.Suspend = false
|
alert.Spec.Suspend = false
|
||||||
if err := kubeClient.Update(ctx, &alert); err != nil {
|
if err := kubeClient.Update(ctx, &alert); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -80,7 +80,7 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Successf("Alert resumed")
|
logger.Successf("Alert resumed")
|
||||||
|
|
||||||
logger.Waitingf("waiting for Alert reconciliation")
|
logger.Waitingf("waiting for Alert reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isAlertResumed(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
isAlertResumed(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeHrCmd = &cobra.Command{
|
var resumeHrCmd = &cobra.Command{
|
||||||
@@ -42,76 +31,24 @@ finish the apply.`,
|
|||||||
Example: ` # Resume reconciliation for an existing Helm release
|
Example: ` # Resume reconciliation for an existing Helm release
|
||||||
flux resume hr podinfo
|
flux resume hr podinfo
|
||||||
`,
|
`,
|
||||||
RunE: resumeHrCmdRun,
|
RunE: resumeCommand{
|
||||||
|
apiType: helmReleaseType,
|
||||||
|
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resumeCmd.AddCommand(resumeHrCmd)
|
resumeCmd.AddCommand(resumeHrCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj helmReleaseAdapter) getObservedGeneration() int64 {
|
||||||
if len(args) < 1 {
|
return obj.HelmRelease.Status.ObservedGeneration
|
||||||
return fmt.Errorf("HelmRelease name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var helmRelease helmv2.HelmRelease
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("resuming HelmRelease %s in %s namespace", name, namespace)
|
|
||||||
helmRelease.Spec.Suspend = false
|
|
||||||
if err := kubeClient.Update(ctx, &helmRelease); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmRelease resumed")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmRelease reconciliation")
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
|
||||||
isHelmReleaseResumed(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmRelease reconciliation completed")
|
|
||||||
|
|
||||||
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHelmReleaseResumed(ctx context.Context, kubeClient client.Client,
|
func (obj helmReleaseAdapter) setUnsuspended() {
|
||||||
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
|
obj.HelmRelease.Spec.Suspend = false
|
||||||
return func() (bool, error) {
|
}
|
||||||
err := kubeClient.Get(ctx, namespacedName, helmRelease)
|
|
||||||
if err != nil {
|
func (obj helmReleaseAdapter) successMessage() string {
|
||||||
return false, err
|
return fmt.Sprintf("applied revision %s", obj.Status.LastAppliedRevision)
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state we are observing is for the current generation
|
|
||||||
if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,10 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeKsCmd = &cobra.Command{
|
var resumeKsCmd = &cobra.Command{
|
||||||
@@ -41,76 +32,24 @@ finish the apply.`,
|
|||||||
Example: ` # Resume reconciliation for an existing Kustomization
|
Example: ` # Resume reconciliation for an existing Kustomization
|
||||||
flux resume ks podinfo
|
flux resume ks podinfo
|
||||||
`,
|
`,
|
||||||
RunE: resumeKsCmdRun,
|
RunE: resumeCommand{
|
||||||
|
apiType: kustomizationType,
|
||||||
|
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resumeCmd.AddCommand(resumeKsCmd)
|
resumeCmd.AddCommand(resumeKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj kustomizationAdapter) getObservedGeneration() int64 {
|
||||||
if len(args) < 1 {
|
return obj.Kustomization.Status.ObservedGeneration
|
||||||
return fmt.Errorf("Kustomization name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("resuming Kustomization %s in %s namespace", name, namespace)
|
|
||||||
kustomization.Spec.Suspend = false
|
|
||||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Kustomization resumed")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for Kustomization reconciliation")
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
|
||||||
isKustomizationResumed(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Kustomization reconciliation completed")
|
|
||||||
|
|
||||||
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isKustomizationResumed(ctx context.Context, kubeClient client.Client,
|
func (obj kustomizationAdapter) setUnsuspended() {
|
||||||
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
|
obj.Kustomization.Spec.Suspend = false
|
||||||
return func() (bool, error) {
|
}
|
||||||
err := kubeClient.Get(ctx, namespacedName, kustomization)
|
|
||||||
if err != nil {
|
func (obj kustomizationAdapter) successMessage() string {
|
||||||
return false, err
|
return fmt.Sprintf("applied revision %s", obj.Status.LastAppliedRevision)
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state we are observing is for the current generation
|
|
||||||
if kustomization.Generation != kustomization.Status.ObservedGeneration {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,16 +54,16 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var receiver notificationv1.Receiver
|
var receiver notificationv1.Receiver
|
||||||
@@ -72,7 +72,7 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("resuming Receiver %s in %s namespace", name, namespace)
|
logger.Actionf("resuming Receiver %s in %s namespace", name, rootArgs.namespace)
|
||||||
receiver.Spec.Suspend = false
|
receiver.Spec.Suspend = false
|
||||||
if err := kubeClient.Update(ctx, &receiver); err != nil {
|
if err := kubeClient.Update(ctx, &receiver); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -80,7 +80,7 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Successf("Receiver resumed")
|
logger.Successf("Receiver resumed")
|
||||||
|
|
||||||
logger.Waitingf("waiting for Receiver reconciliation")
|
logger.Waitingf("waiting for Receiver reconciliation")
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||||
isReceiverResumed(ctx, kubeClient, namespacedName, &receiver)); err != nil {
|
isReceiverResumed(ctx, kubeClient, namespacedName, &receiver)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeSourceBucketCmd = &cobra.Command{
|
var resumeSourceBucketCmd = &cobra.Command{
|
||||||
@@ -40,76 +28,20 @@ var resumeSourceBucketCmd = &cobra.Command{
|
|||||||
Example: ` # Resume reconciliation for an existing Bucket
|
Example: ` # Resume reconciliation for an existing Bucket
|
||||||
flux resume source bucket podinfo
|
flux resume source bucket podinfo
|
||||||
`,
|
`,
|
||||||
RunE: resumeSourceBucketCmdRun,
|
RunE: resumeCommand{
|
||||||
|
apiType: bucketType,
|
||||||
|
object: &bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resumeSourceCmd.AddCommand(resumeSourceBucketCmd)
|
resumeSourceCmd.AddCommand(resumeSourceBucketCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumeSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj bucketAdapter) getObservedGeneration() int64 {
|
||||||
if len(args) < 1 {
|
return obj.Bucket.Status.ObservedGeneration
|
||||||
return fmt.Errorf("source name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var bucket sourcev1.Bucket
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("resuming source %s in %s namespace", name, namespace)
|
|
||||||
bucket.Spec.Suspend = false
|
|
||||||
if err := kubeClient.Update(ctx, &bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source resumed")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for Bucket reconciliation")
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
|
||||||
isBucketResumed(ctx, kubeClient, namespacedName, &bucket)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("Bucket reconciliation completed")
|
|
||||||
|
|
||||||
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBucketResumed(ctx context.Context, kubeClient client.Client,
|
func (obj bucketAdapter) setUnsuspended() {
|
||||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
|
obj.Bucket.Spec.Suspend = false
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, bucket)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state we are observing is for the current generation
|
|
||||||
if bucket.Generation != bucket.Status.ObservedGeneration {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,10 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeSourceHelmChartCmd = &cobra.Command{
|
var resumeSourceHelmChartCmd = &cobra.Command{
|
||||||
@@ -40,76 +30,24 @@ var resumeSourceHelmChartCmd = &cobra.Command{
|
|||||||
Example: ` # Resume reconciliation for an existing HelmChart
|
Example: ` # Resume reconciliation for an existing HelmChart
|
||||||
flux resume source chart podinfo
|
flux resume source chart podinfo
|
||||||
`,
|
`,
|
||||||
RunE: resumeSourceHelmChartCmdRun,
|
RunE: resumeCommand{
|
||||||
|
apiType: helmChartType,
|
||||||
|
object: &helmChartAdapter{&sourcev1.HelmChart{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resumeSourceCmd.AddCommand(resumeSourceHelmChartCmd)
|
resumeSourceCmd.AddCommand(resumeSourceHelmChartCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumeSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj helmChartAdapter) getObservedGeneration() int64 {
|
||||||
if len(args) < 1 {
|
return obj.HelmChart.Status.ObservedGeneration
|
||||||
return fmt.Errorf("source name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.HelmChart
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("resuming source %s in %s namespace", name, namespace)
|
|
||||||
repository.Spec.Suspend = false
|
|
||||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source resumed")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmChart reconciliation")
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
|
||||||
isHelmChartResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmChart reconciliation completed")
|
|
||||||
|
|
||||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHelmChartResumed(ctx context.Context, kubeClient client.Client,
|
func (obj helmChartAdapter) setUnsuspended() {
|
||||||
namespacedName types.NamespacedName, chart *sourcev1.HelmChart) wait.ConditionFunc {
|
obj.HelmChart.Spec.Suspend = false
|
||||||
return func() (bool, error) {
|
}
|
||||||
err := kubeClient.Get(ctx, namespacedName, chart)
|
|
||||||
if err != nil {
|
func (obj helmChartAdapter) successMessage() string {
|
||||||
return false, err
|
return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state we are observing is for the current generation
|
|
||||||
if chart.Generation != chart.Status.ObservedGeneration {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(chart.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeSourceGitCmd = &cobra.Command{
|
var resumeSourceGitCmd = &cobra.Command{
|
||||||
@@ -40,76 +28,20 @@ var resumeSourceGitCmd = &cobra.Command{
|
|||||||
Example: ` # Resume reconciliation for an existing GitRepository
|
Example: ` # Resume reconciliation for an existing GitRepository
|
||||||
flux resume source git podinfo
|
flux resume source git podinfo
|
||||||
`,
|
`,
|
||||||
RunE: resumeSourceGitCmdRun,
|
RunE: resumeCommand{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resumeSourceCmd.AddCommand(resumeSourceGitCmd)
|
resumeSourceCmd.AddCommand(resumeSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumeSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj gitRepositoryAdapter) getObservedGeneration() int64 {
|
||||||
if len(args) < 1 {
|
return obj.GitRepository.Status.ObservedGeneration
|
||||||
return fmt.Errorf("source name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.GitRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("resuming source %s in %s namespace", name, namespace)
|
|
||||||
repository.Spec.Suspend = false
|
|
||||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source resumed")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for GitRepository reconciliation")
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
|
||||||
isGitRepositoryResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("GitRepository reconciliation completed")
|
|
||||||
|
|
||||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGitRepositoryResumed(ctx context.Context, kubeClient client.Client,
|
func (obj gitRepositoryAdapter) setUnsuspended() {
|
||||||
namespacedName types.NamespacedName, repository *sourcev1.GitRepository) wait.ConditionFunc {
|
obj.GitRepository.Spec.Suspend = false
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, repository)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state we are observing is for the current generation
|
|
||||||
if repository.Generation != repository.Status.ObservedGeneration {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeSourceHelmCmd = &cobra.Command{
|
var resumeSourceHelmCmd = &cobra.Command{
|
||||||
@@ -40,76 +28,20 @@ var resumeSourceHelmCmd = &cobra.Command{
|
|||||||
Example: ` # Resume reconciliation for an existing HelmRepository
|
Example: ` # Resume reconciliation for an existing HelmRepository
|
||||||
flux resume source helm bitnami
|
flux resume source helm bitnami
|
||||||
`,
|
`,
|
||||||
RunE: resumeSourceHelmCmdRun,
|
RunE: resumeCommand{
|
||||||
|
apiType: helmRepositoryType,
|
||||||
|
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resumeSourceCmd.AddCommand(resumeSourceHelmCmd)
|
resumeSourceCmd.AddCommand(resumeSourceHelmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumeSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj helmRepositoryAdapter) getObservedGeneration() int64 {
|
||||||
if len(args) < 1 {
|
return obj.HelmRepository.Status.ObservedGeneration
|
||||||
return fmt.Errorf("source name is required")
|
|
||||||
}
|
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.HelmRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("resuming source %s in %s namespace", name, namespace)
|
|
||||||
repository.Spec.Suspend = false
|
|
||||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source resumed")
|
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmRepository reconciliation")
|
|
||||||
if err := wait.PollImmediate(pollInterval, timeout,
|
|
||||||
isHelmRepositoryResumed(ctx, kubeClient, namespacedName, &repository)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmRepository reconciliation completed")
|
|
||||||
|
|
||||||
logger.Successf("fetched revision %s", repository.Status.Artifact.Revision)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHelmRepositoryResumed(ctx context.Context, kubeClient client.Client,
|
func (obj helmRepositoryAdapter) setUnsuspended() {
|
||||||
namespacedName types.NamespacedName, repository *sourcev1.HelmRepository) wait.ConditionFunc {
|
obj.HelmRepository.Spec.Suspend = false
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, repository)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state we are observing is for the current generation
|
|
||||||
if repository.Generation != repository.Status.ObservedGeneration {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
143
cmd/flux/source.go
Normal file
143
cmd/flux/source.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These are general-purpose adapters for attaching methods to, for
|
||||||
|
// the various commands. The *List adapters implement len(), since
|
||||||
|
// it's used in at least a couple of commands.
|
||||||
|
|
||||||
|
// sourcev1.Bucket
|
||||||
|
|
||||||
|
var bucketType = apiType{
|
||||||
|
kind: sourcev1.BucketKind,
|
||||||
|
humanKind: "source bucket",
|
||||||
|
}
|
||||||
|
|
||||||
|
type bucketAdapter struct {
|
||||||
|
*sourcev1.Bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a bucketAdapter) asClientObject() client.Object {
|
||||||
|
return a.Bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcev1.BucketList
|
||||||
|
|
||||||
|
type bucketListAdapter struct {
|
||||||
|
*sourcev1.BucketList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a bucketListAdapter) asClientList() client.ObjectList {
|
||||||
|
return a.BucketList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a bucketListAdapter) len() int {
|
||||||
|
return len(a.BucketList.Items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcev1.HelmChart
|
||||||
|
|
||||||
|
var helmChartType = apiType{
|
||||||
|
kind: sourcev1.HelmChartKind,
|
||||||
|
humanKind: "source chart",
|
||||||
|
}
|
||||||
|
|
||||||
|
type helmChartAdapter struct {
|
||||||
|
*sourcev1.HelmChart
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a helmChartAdapter) asClientObject() client.Object {
|
||||||
|
return a.HelmChart
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcev1.ImagePolicyList
|
||||||
|
|
||||||
|
type helmChartListAdapter struct {
|
||||||
|
*sourcev1.HelmChartList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a helmChartListAdapter) asClientList() client.ObjectList {
|
||||||
|
return a.HelmChartList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a helmChartListAdapter) len() int {
|
||||||
|
return len(a.HelmChartList.Items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcev1.GitRepository
|
||||||
|
|
||||||
|
var gitRepositoryType = apiType{
|
||||||
|
kind: sourcev1.GitRepositoryKind,
|
||||||
|
humanKind: "source git",
|
||||||
|
}
|
||||||
|
|
||||||
|
type gitRepositoryAdapter struct {
|
||||||
|
*sourcev1.GitRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a gitRepositoryAdapter) asClientObject() client.Object {
|
||||||
|
return a.GitRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcev1.GitRepositoryList
|
||||||
|
|
||||||
|
type gitRepositoryListAdapter struct {
|
||||||
|
*sourcev1.GitRepositoryList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a gitRepositoryListAdapter) asClientList() client.ObjectList {
|
||||||
|
return a.GitRepositoryList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a gitRepositoryListAdapter) len() int {
|
||||||
|
return len(a.GitRepositoryList.Items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcev1.HelmRepository
|
||||||
|
|
||||||
|
var helmRepositoryType = apiType{
|
||||||
|
kind: sourcev1.HelmRepositoryKind,
|
||||||
|
humanKind: "source helm",
|
||||||
|
}
|
||||||
|
|
||||||
|
type helmRepositoryAdapter struct {
|
||||||
|
*sourcev1.HelmRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a helmRepositoryAdapter) asClientObject() client.Object {
|
||||||
|
return a.HelmRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcev1.HelmRepositoryList
|
||||||
|
|
||||||
|
type helmRepositoryListAdapter struct {
|
||||||
|
*sourcev1.HelmRepositoryList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a helmRepositoryListAdapter) asClientList() client.ObjectList {
|
||||||
|
return a.HelmRepositoryList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a helmRepositoryListAdapter) len() int {
|
||||||
|
return len(a.HelmRepositoryList.Items)
|
||||||
|
}
|
||||||
@@ -53,16 +53,16 @@ func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
err = kubeClient.Get(ctx, namespacedName, suspend.object.asClientObject())
|
err = kubeClient.Get(ctx, namespacedName, suspend.object.asClientObject())
|
||||||
@@ -70,7 +70,7 @@ func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("suspending %s %s in %s namespace", suspend.humanKind, name, namespace)
|
logger.Actionf("suspending %s %s in %s namespace", suspend.humanKind, name, rootArgs.namespace)
|
||||||
suspend.object.setSuspended()
|
suspend.object.setSuspended()
|
||||||
if err := kubeClient.Update(ctx, suspend.object.asClientObject()); err != nil {
|
if err := kubeClient.Update(ctx, suspend.object.asClientObject()); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -47,16 +47,16 @@ func suspendAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var alert notificationv1.Alert
|
var alert notificationv1.Alert
|
||||||
@@ -65,7 +65,7 @@ func suspendAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("suspending Alert %s in %s namespace", name, namespace)
|
logger.Actionf("suspending Alert %s in %s namespace", name, rootArgs.namespace)
|
||||||
alert.Spec.Suspend = true
|
alert.Spec.Suspend = true
|
||||||
if err := kubeClient.Update(ctx, &alert); err != nil {
|
if err := kubeClient.Update(ctx, &alert); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,14 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var suspendHrCmd = &cobra.Command{
|
var suspendHrCmd = &cobra.Command{
|
||||||
@@ -35,43 +29,20 @@ var suspendHrCmd = &cobra.Command{
|
|||||||
Example: ` # Suspend reconciliation for an existing Helm release
|
Example: ` # Suspend reconciliation for an existing Helm release
|
||||||
flux suspend hr podinfo
|
flux suspend hr podinfo
|
||||||
`,
|
`,
|
||||||
RunE: suspendHrCmdRun,
|
RunE: suspendCommand{
|
||||||
|
apiType: helmReleaseType,
|
||||||
|
object: &helmReleaseAdapter{&helmv2.HelmRelease{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
suspendCmd.AddCommand(suspendHrCmd)
|
suspendCmd.AddCommand(suspendHrCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspendHrCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj helmReleaseAdapter) isSuspended() bool {
|
||||||
if len(args) < 1 {
|
return obj.HelmRelease.Spec.Suspend
|
||||||
return fmt.Errorf("HelmRelease name is required")
|
}
|
||||||
}
|
|
||||||
name := args[0]
|
func (obj helmReleaseAdapter) setSuspended() {
|
||||||
|
obj.HelmRelease.Spec.Suspend = true
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var helmRelease helmv2.HelmRelease
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("suspending HelmRelease %s in %s namespace", name, namespace)
|
|
||||||
helmRelease.Spec.Suspend = true
|
|
||||||
if err := kubeClient.Update(ctx, &helmRelease); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("HelmRelease suspended")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var suspendKsCmd = &cobra.Command{
|
var suspendKsCmd = &cobra.Command{
|
||||||
@@ -34,43 +29,20 @@ var suspendKsCmd = &cobra.Command{
|
|||||||
Example: ` # Suspend reconciliation for an existing Kustomization
|
Example: ` # Suspend reconciliation for an existing Kustomization
|
||||||
flux suspend ks podinfo
|
flux suspend ks podinfo
|
||||||
`,
|
`,
|
||||||
RunE: suspendKsCmdRun,
|
RunE: suspendCommand{
|
||||||
|
apiType: kustomizationType,
|
||||||
|
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
suspendCmd.AddCommand(suspendKsCmd)
|
suspendCmd.AddCommand(suspendKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspendKsCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj kustomizationAdapter) isSuspended() bool {
|
||||||
if len(args) < 1 {
|
return obj.Kustomization.Spec.Suspend
|
||||||
return fmt.Errorf("kustomization name is required")
|
}
|
||||||
}
|
|
||||||
name := args[0]
|
func (obj kustomizationAdapter) setSuspended() {
|
||||||
|
obj.Kustomization.Spec.Suspend = true
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &kustomization)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("suspending kustomization %s in %s namespace", name, namespace)
|
|
||||||
kustomization.Spec.Suspend = true
|
|
||||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("kustomization suspended")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,16 +47,16 @@ func suspendReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
var receiver notificationv1.Receiver
|
var receiver notificationv1.Receiver
|
||||||
@@ -65,7 +65,7 @@ func suspendReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("suspending Receiver %s in %s namespace", name, namespace)
|
logger.Actionf("suspending Receiver %s in %s namespace", name, rootArgs.namespace)
|
||||||
receiver.Spec.Suspend = true
|
receiver.Spec.Suspend = true
|
||||||
if err := kubeClient.Update(ctx, &receiver); err != nil {
|
if err := kubeClient.Update(ctx, &receiver); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,13 +17,8 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var suspendSourceBucketCmd = &cobra.Command{
|
var suspendSourceBucketCmd = &cobra.Command{
|
||||||
@@ -33,43 +28,20 @@ var suspendSourceBucketCmd = &cobra.Command{
|
|||||||
Example: ` # Suspend reconciliation for an existing Bucket
|
Example: ` # Suspend reconciliation for an existing Bucket
|
||||||
flux suspend source bucket podinfo
|
flux suspend source bucket podinfo
|
||||||
`,
|
`,
|
||||||
RunE: suspendSourceBucketCmdRun,
|
RunE: suspendCommand{
|
||||||
|
apiType: bucketType,
|
||||||
|
object: bucketAdapter{&sourcev1.Bucket{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
suspendSourceCmd.AddCommand(suspendSourceBucketCmd)
|
suspendSourceCmd.AddCommand(suspendSourceBucketCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspendSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj bucketAdapter) isSuspended() bool {
|
||||||
if len(args) < 1 {
|
return obj.Bucket.Spec.Suspend
|
||||||
return fmt.Errorf("source name is required")
|
}
|
||||||
}
|
|
||||||
name := args[0]
|
func (obj bucketAdapter) setSuspended() {
|
||||||
|
obj.Bucket.Spec.Suspend = true
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var bucket sourcev1.Bucket
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &bucket)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("suspending source %s in %s namespace", name, namespace)
|
|
||||||
bucket.Spec.Suspend = true
|
|
||||||
if err := kubeClient.Update(ctx, &bucket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source suspended")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var suspendSourceHelmChartCmd = &cobra.Command{
|
var suspendSourceHelmChartCmd = &cobra.Command{
|
||||||
@@ -33,43 +29,20 @@ var suspendSourceHelmChartCmd = &cobra.Command{
|
|||||||
Example: ` # Suspend reconciliation for an existing HelmChart
|
Example: ` # Suspend reconciliation for an existing HelmChart
|
||||||
flux suspend source chart podinfo
|
flux suspend source chart podinfo
|
||||||
`,
|
`,
|
||||||
RunE: suspendSourceHelmChartCmdRun,
|
RunE: suspendCommand{
|
||||||
|
apiType: helmChartType,
|
||||||
|
object: helmChartAdapter{&sourcev1.HelmChart{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
suspendSourceCmd.AddCommand(suspendSourceHelmChartCmd)
|
suspendSourceCmd.AddCommand(suspendSourceHelmChartCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspendSourceHelmChartCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj helmChartAdapter) isSuspended() bool {
|
||||||
if len(args) < 1 {
|
return obj.HelmChart.Spec.Suspend
|
||||||
return fmt.Errorf("source name is required")
|
}
|
||||||
}
|
|
||||||
name := args[0]
|
func (obj helmChartAdapter) setSuspended() {
|
||||||
|
obj.HelmChart.Spec.Suspend = true
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var chart sourcev1.HelmChart
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &chart)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("suspending source %s in %s namespace", name, namespace)
|
|
||||||
chart.Spec.Suspend = true
|
|
||||||
if err := kubeClient.Update(ctx, &chart); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source suspended")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var suspendSourceGitCmd = &cobra.Command{
|
var suspendSourceGitCmd = &cobra.Command{
|
||||||
@@ -33,43 +29,20 @@ var suspendSourceGitCmd = &cobra.Command{
|
|||||||
Example: ` # Suspend reconciliation for an existing GitRepository
|
Example: ` # Suspend reconciliation for an existing GitRepository
|
||||||
flux suspend source git podinfo
|
flux suspend source git podinfo
|
||||||
`,
|
`,
|
||||||
RunE: suspendSourceGitCmdRun,
|
RunE: suspendCommand{
|
||||||
|
apiType: gitRepositoryType,
|
||||||
|
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
suspendSourceCmd.AddCommand(suspendSourceGitCmd)
|
suspendSourceCmd.AddCommand(suspendSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspendSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj gitRepositoryAdapter) isSuspended() bool {
|
||||||
if len(args) < 1 {
|
return obj.GitRepository.Spec.Suspend
|
||||||
return fmt.Errorf("source name is required")
|
}
|
||||||
}
|
|
||||||
name := args[0]
|
func (obj gitRepositoryAdapter) setSuspended() {
|
||||||
|
obj.GitRepository.Spec.Suspend = true
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.GitRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("suspending source %s in %s namespace", name, namespace)
|
|
||||||
repository.Spec.Suspend = true
|
|
||||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source suspended")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,9 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var suspendSourceHelmCmd = &cobra.Command{
|
var suspendSourceHelmCmd = &cobra.Command{
|
||||||
@@ -33,43 +29,20 @@ var suspendSourceHelmCmd = &cobra.Command{
|
|||||||
Example: ` # Suspend reconciliation for an existing HelmRepository
|
Example: ` # Suspend reconciliation for an existing HelmRepository
|
||||||
flux suspend source helm bitnami
|
flux suspend source helm bitnami
|
||||||
`,
|
`,
|
||||||
RunE: suspendSourceHelmCmdRun,
|
RunE: suspendCommand{
|
||||||
|
apiType: helmRepositoryType,
|
||||||
|
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
|
||||||
|
}.run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
suspendSourceCmd.AddCommand(suspendSourceHelmCmd)
|
suspendSourceCmd.AddCommand(suspendSourceHelmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspendSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
func (obj helmRepositoryAdapter) isSuspended() bool {
|
||||||
if len(args) < 1 {
|
return obj.HelmRepository.Spec.Suspend
|
||||||
return fmt.Errorf("source name is required")
|
}
|
||||||
}
|
|
||||||
name := args[0]
|
func (obj helmRepositoryAdapter) setSuspended() {
|
||||||
|
obj.HelmRepository.Spec.Suspend = true
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
namespacedName := types.NamespacedName{
|
|
||||||
Namespace: namespace,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
var repository sourcev1.HelmRepository
|
|
||||||
err = kubeClient.Get(ctx, namespacedName, &repository)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("suspending source %s in %s namespace", name, namespace)
|
|
||||||
repository.Spec.Suspend = true
|
|
||||||
if err := kubeClient.Update(ctx, &repository); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Successf("source suspended")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,38 +45,40 @@ var uninstallCmd = &cobra.Command{
|
|||||||
RunE: uninstallCmdRun,
|
RunE: uninstallCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type uninstallFlags struct {
|
||||||
uninstallCRDs bool
|
crds bool
|
||||||
uninstallResources bool
|
resources bool
|
||||||
uninstallDryRun bool
|
dryRun bool
|
||||||
uninstallSilent bool
|
silent bool
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var uninstallArgs uninstallFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
uninstallCmd.Flags().BoolVar(&uninstallResources, "resources", true,
|
uninstallCmd.Flags().BoolVar(&uninstallArgs.resources, "resources", true,
|
||||||
"removes custom resources such as Kustomizations, GitRepositories and HelmRepositories")
|
"removes custom resources such as Kustomizations, GitRepositories and HelmRepositories")
|
||||||
uninstallCmd.Flags().BoolVar(&uninstallCRDs, "crds", false,
|
uninstallCmd.Flags().BoolVar(&uninstallArgs.crds, "crds", false,
|
||||||
"removes all CRDs previously installed")
|
"removes all CRDs previously installed")
|
||||||
uninstallCmd.Flags().BoolVar(&uninstallDryRun, "dry-run", false,
|
uninstallCmd.Flags().BoolVar(&uninstallArgs.dryRun, "dry-run", false,
|
||||||
"only print the object that would be deleted")
|
"only print the object that would be deleted")
|
||||||
uninstallCmd.Flags().BoolVarP(&uninstallSilent, "silent", "s", false,
|
uninstallCmd.Flags().BoolVarP(&uninstallArgs.silent, "silent", "s", false,
|
||||||
"delete components without asking for confirmation")
|
"delete components without asking for confirmation")
|
||||||
|
|
||||||
rootCmd.AddCommand(uninstallCmd)
|
rootCmd.AddCommand(uninstallCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !uninstallDryRun && !uninstallSilent {
|
if !uninstallArgs.dryRun && !uninstallArgs.silent {
|
||||||
prompt := promptui.Prompt{
|
prompt := promptui.Prompt{
|
||||||
Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", namespace),
|
Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", rootArgs.namespace),
|
||||||
IsConfirm: true,
|
IsConfirm: true,
|
||||||
}
|
}
|
||||||
if _, err := prompt.Run(); err != nil {
|
if _, err := prompt.Run(); err != nil {
|
||||||
@@ -85,7 +87,7 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dryRun := "--dry-run=server"
|
dryRun := "--dry-run=server"
|
||||||
deleteResources := uninstallResources || uninstallCRDs
|
deleteResources := uninstallArgs.resources || uninstallArgs.crds
|
||||||
|
|
||||||
// known kinds with finalizers
|
// known kinds with finalizers
|
||||||
namespacedKinds := []string{
|
namespacedKinds := []string{
|
||||||
@@ -96,8 +98,8 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// suspend bootstrap kustomization to avoid finalizers deadlock
|
// suspend bootstrap kustomization to avoid finalizers deadlock
|
||||||
kustomizationName := types.NamespacedName{
|
kustomizationName := types.NamespacedName{
|
||||||
Namespace: namespace,
|
Namespace: rootArgs.namespace,
|
||||||
Name: namespace,
|
Name: rootArgs.namespace,
|
||||||
}
|
}
|
||||||
var kustomization kustomizev1.Kustomization
|
var kustomization kustomizev1.Kustomization
|
||||||
err = kubeClient.Get(ctx, kustomizationName, &kustomization)
|
err = kubeClient.Get(ctx, kustomizationName, &kustomization)
|
||||||
@@ -113,21 +115,21 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// add HelmRelease kind to deletion list if exists
|
// add HelmRelease kind to deletion list if exists
|
||||||
var list helmv2.HelmReleaseList
|
var list helmv2.HelmReleaseList
|
||||||
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace)); err == nil {
|
if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace)); err == nil {
|
||||||
namespacedKinds = append(namespacedKinds, helmv2.HelmReleaseKind)
|
namespacedKinds = append(namespacedKinds, helmv2.HelmReleaseKind)
|
||||||
}
|
}
|
||||||
|
|
||||||
if deleteResources {
|
if deleteResources {
|
||||||
logger.Actionf("uninstalling custom resources")
|
logger.Actionf("uninstalling custom resources")
|
||||||
for _, kind := range namespacedKinds {
|
for _, kind := range namespacedKinds {
|
||||||
if err := deleteAll(ctx, kind, uninstallDryRun); err != nil {
|
if err := deleteAll(ctx, kind, uninstallArgs.dryRun); err != nil {
|
||||||
logger.Failuref("kubectl: %s", err.Error())
|
logger.Failuref("kubectl: %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var kinds []string
|
var kinds []string
|
||||||
if uninstallCRDs {
|
if uninstallArgs.crds {
|
||||||
kinds = append(kinds, "crds")
|
kinds = append(kinds, "crds")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,13 +140,13 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
for _, kind := range kinds {
|
for _, kind := range kinds {
|
||||||
kubectlArgs := []string{
|
kubectlArgs := []string{
|
||||||
"delete", kind,
|
"delete", kind,
|
||||||
"-l", fmt.Sprintf("app.kubernetes.io/instance=%s", namespace),
|
"-l", fmt.Sprintf("app.kubernetes.io/instance=%s", rootArgs.namespace),
|
||||||
"--ignore-not-found", "--timeout", timeout.String(),
|
"--ignore-not-found", "--timeout", rootArgs.timeout.String(),
|
||||||
}
|
}
|
||||||
if uninstallDryRun {
|
if uninstallArgs.dryRun {
|
||||||
kubectlArgs = append(kubectlArgs, dryRun)
|
kubectlArgs = append(kubectlArgs, dryRun)
|
||||||
}
|
}
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
||||||
return fmt.Errorf("uninstall failed: %w", err)
|
return fmt.Errorf("uninstall failed: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,13 +159,13 @@ func deleteAll(ctx context.Context, kind string, dryRun bool) error {
|
|||||||
kubectlArgs := []string{
|
kubectlArgs := []string{
|
||||||
"delete", kind, "--ignore-not-found",
|
"delete", kind, "--ignore-not-found",
|
||||||
"--all", "--all-namespaces",
|
"--all", "--all-namespaces",
|
||||||
"--timeout", timeout.String(),
|
"--timeout", rootArgs.timeout.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if dryRun {
|
if dryRun {
|
||||||
kubectlArgs = append(kubectlArgs, "--dry-run=server")
|
kubectlArgs = append(kubectlArgs, "--dry-run=server")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubeconfig, kubecontext, kubectlArgs...)
|
_, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,4 +30,5 @@ The create source sub-commands generate Kubernetes secrets specific to Flux.
|
|||||||
* [flux create](flux_create.md) - Create or update sources and resources
|
* [flux create](flux_create.md) - Create or update sources and resources
|
||||||
* [flux create secret git](flux_create_secret_git.md) - Create or update a Kubernetes secret for Git authentication
|
* [flux create secret git](flux_create_secret_git.md) - Create or update a Kubernetes secret for Git authentication
|
||||||
* [flux create secret helm](flux_create_secret_helm.md) - Create or update a Kubernetes secret for Helm repository authentication
|
* [flux create secret helm](flux_create_secret_helm.md) - Create or update a Kubernetes secret for Helm repository authentication
|
||||||
|
* [flux create secret tls](flux_create_secret_tls.md) - Create or update a Kubernetes secret with TLS certificates
|
||||||
|
|
||||||
|
|||||||
56
docs/cmd/flux_create_secret_tls.md
Normal file
56
docs/cmd/flux_create_secret_tls.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
## flux create secret tls
|
||||||
|
|
||||||
|
Create or update a Kubernetes secret with TLS certificates
|
||||||
|
|
||||||
|
### Synopsis
|
||||||
|
|
||||||
|
|
||||||
|
The create secret tls command generates a Kubernetes secret with certificates for use with TLS.
|
||||||
|
|
||||||
|
```
|
||||||
|
flux create secret tls [name] [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Create a TLS secret on disk and encrypt it with Mozilla SOPS.
|
||||||
|
# Files are expected to be PEM-encoded.
|
||||||
|
flux create secret tls certs \
|
||||||
|
--namespace=my-namespace \
|
||||||
|
--cert-file=./client.crt \
|
||||||
|
--key-file=./client.key \
|
||||||
|
--export > certs.yaml
|
||||||
|
|
||||||
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
|
--in-place certs.yaml
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
--ca-file string TLS authentication CA file path
|
||||||
|
--cert-file string TLS authentication cert file path
|
||||||
|
-h, --help help for tls
|
||||||
|
--key-file string TLS authentication key file path
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options inherited from parent commands
|
||||||
|
|
||||||
|
```
|
||||||
|
--context string kubernetes context to use
|
||||||
|
--export export in YAML format to stdout
|
||||||
|
--interval duration source sync interval (default 1m0s)
|
||||||
|
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||||
|
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
|
||||||
|
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||||
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
|
--verbose print generated objects
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [flux create secret](flux_create_secret.md) - Create or update Kubernetes secrets
|
||||||
|
|
||||||
16
docs/components/image/controller.md
Normal file
16
docs/components/image/controller.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Image reflector and automation controllers
|
||||||
|
|
||||||
|
The image-reflector-controller and image-automation-controller work together to update a Git
|
||||||
|
repository when new container images are available.
|
||||||
|
|
||||||
|
- The image-reflector-controller scans image repositories and reflects the image metadata in
|
||||||
|
Kubernetes resources.
|
||||||
|
- The image-automation-controller updates YAML files based on the latest images scanned, and commits
|
||||||
|
the changes to a given Git repository.
|
||||||
|
|
||||||
|
Links:
|
||||||
|
|
||||||
|
- Source code [fluxcd/image-reflector-controller](https://github.com/fluxcd/image-reflector-controller)
|
||||||
|
- Reflector [specification docs](https://github.com/fluxcd/image-reflector-controller/tree/main/docs/spec)
|
||||||
|
- Source code [fluxcd/image-automation-controller](https://github.com/fluxcd/image-automation-controller)
|
||||||
|
- Automation [specification docs](https://github.com/fluxcd/image-automation-controller/tree/main/docs/spec)
|
||||||
@@ -24,3 +24,7 @@ A reference for each component and API type is linked below.
|
|||||||
- [Provider CRD](notification/provider.md)
|
- [Provider CRD](notification/provider.md)
|
||||||
- [Alert CRD](notification/alert.md)
|
- [Alert CRD](notification/alert.md)
|
||||||
- [Receiver CRD](notification/receiver.md)
|
- [Receiver CRD](notification/receiver.md)
|
||||||
|
- [Image automation controllers](image/controller.md)
|
||||||
|
- [ImageRepository CRD](image/imagerepositories.md)
|
||||||
|
- [ImagePolicy CRD](image/imagepolicies.md)
|
||||||
|
- [ImageUpdateAutomation CRD](image/imageupdateautomation.md)
|
||||||
|
|||||||
@@ -1,98 +1,5 @@
|
|||||||
# Frequently asked questions
|
# Frequently asked questions
|
||||||
|
|
||||||
## General questions
|
|
||||||
|
|
||||||
### What does Flux v2 mean for Flux?
|
|
||||||
|
|
||||||
Flux v1 is a monolithic do-it-all operator; Flux v2 separates the functionalities into specialized controllers, collectively called the GitOps Toolkit.
|
|
||||||
|
|
||||||
You can install and operate Flux v2 simply using the `flux` command. You can easily pick and choose the functionality you need and extend it to serve your own purposes.
|
|
||||||
|
|
||||||
The timeline we are looking at right now is:
|
|
||||||
|
|
||||||
1. Put Flux v1 into maintenance mode (no new features being added; bugfixes and CVEs patched only).
|
|
||||||
1. Continue work on the [Flux v2 roadmap](https://toolkit.fluxcd.io/roadmap/).
|
|
||||||
1. We will provide transition guides for specific user groups, e.g. users of Flux v1 in read-only mode, or of Helm Operator v1, etc. once the functionality is integrated into Flux v2 and it's deemed "ready".
|
|
||||||
1. Once the use-cases of Flux v1 are covered, we will continue supporting Flux v1 for 6 months. This will be the transition period before it's considered unsupported.
|
|
||||||
|
|
||||||
### Why did you rewrite Flux?
|
|
||||||
|
|
||||||
Flux v2 implements its functionality in individual controllers, which allowed us to address long-standing feature requests much more easily.
|
|
||||||
|
|
||||||
By basing these controllers on modern Kubernetes tooling (`controller-runtime` libraries), they can be dynamically configured with Kubernetes custom resources either by cluster admins or by other automated tools -- and you get greatly increased observability.
|
|
||||||
|
|
||||||
This gave us the opportunity to build Flux v2 with the top Flux v1 feature requests in mind:
|
|
||||||
|
|
||||||
- Supporting multiple source Git repositories
|
|
||||||
- Operational insight through health checks, events and alerts
|
|
||||||
- Multi-tenancy capabilities, like applying each source repository with its own set of permissions
|
|
||||||
|
|
||||||
On top of that, testing the individual components and understanding the codebase becomes a lot easier.
|
|
||||||
|
|
||||||
### What are significant new differences between Flux v1 and Flux v2?
|
|
||||||
|
|
||||||
#### Reconciliation
|
|
||||||
|
|
||||||
Flux v1 | Flux v2
|
|
||||||
---------------------------------- | ----------------------------------
|
|
||||||
Limited to a single Git repository | Multiple Git repositories
|
|
||||||
Declarative config via arguments in the Flux deployment | `GitRepository` custom resource, which produces an artifact which can be reconciled by other controllers
|
|
||||||
Follow `HEAD` of Git branches | Supports Git branches, pinning on commits and tags, follow SemVer tag ranges
|
|
||||||
Suspending of reconciliation by downscaling Flux deployment | Reconciliation can be paused per resource by suspending the `GitRepository`
|
|
||||||
Credentials config via Arguments and/or Secret volume mounts in the Flux pod | Credentials config per `GitRepository` resource: SSH private key, HTTP/S username/password/token, OpenPGP public keys
|
|
||||||
|
|
||||||
#### `kustomize` support
|
|
||||||
|
|
||||||
Flux v1 | Flux v2
|
|
||||||
---------------------------------- | ----------------------------------
|
|
||||||
Declarative config through `.flux.yaml` files in the Git repository | Declarative config through a `Kustomization` custom resource, consuming the artifact from the GitRepository
|
|
||||||
Manifests are generated via shell exec and then reconciled by `fluxd` | Generation, server-side validation, and reconciliation is handled by a specialised `kustomize-controller`
|
|
||||||
Reconciliation using the service account of the Flux deployment | Support for service account impersonation
|
|
||||||
Garbage collection needs cluster role binding for Flux to query the Kubernetes discovery API | Garbage collection needs no cluster role binding or access to Kubernetes discovery API
|
|
||||||
Support for custom commands and generators executed by fluxd in a POSIX shell | No support for custom commands
|
|
||||||
|
|
||||||
#### Helm integration
|
|
||||||
|
|
||||||
Flux v1 | Flux v2
|
|
||||||
---------------------------------- | ----------------------------------
|
|
||||||
Declarative config in a single Helm custom resource | Declarative config through `HelmRepository`, `GitRepository`, `Bucket`, `HelmChart` and `HelmRelease` custom resources
|
|
||||||
Chart synchronisation embedded in the operator | Extensive release configuration options, and a reconciliation interval per source
|
|
||||||
Support for fixed SemVer versions from Helm repositories | Support for SemVer ranges for `HelmChart` resources
|
|
||||||
Git repository synchronisation on a global interval | Planned support for charts from GitRepository sources
|
|
||||||
Limited observability via the status object of the HelmRelease resource | Better observability via the HelmRelease status object, Kubernetes events, and notifications
|
|
||||||
Resource heavy, relatively slow | Better performance
|
|
||||||
Chart changes from Git sources are determined from Git metadata | Chart changes must be accompanied by a version bump in `Chart.yaml` to produce a new artifact
|
|
||||||
|
|
||||||
#### Notifications, webhooks, observability
|
|
||||||
|
|
||||||
Flux v1 | Flux v2
|
|
||||||
---------------------------------- | ----------------------------------
|
|
||||||
Emits "custom Flux events" to a webhook endpoint | Emits Kubernetes events for included custom resources
|
|
||||||
RPC endpoint can be configured to a 3rd party solution like FluxCloud to be forwarded as notifications to e.g. Slack | Flux v2 components can be configured to POST the events to a `notification-controller` endpoint. Selective forwarding of POSTed events as notifications using `Provider` and `Alert` custom resources.
|
|
||||||
Webhook receiver is a side-project | Webhook receiver, handling a wide range of platforms, is included
|
|
||||||
Unstructured logging | Structured logging for all components
|
|
||||||
Custom Prometheus metrics | Generic / common `controller-runtime` Prometheus metrics
|
|
||||||
|
|
||||||
### How can I get involved?
|
|
||||||
|
|
||||||
There are a variety of ways and we look forward to having you on board building the future of GitOps together:
|
|
||||||
|
|
||||||
- [Discuss the direction](https://github.com/fluxcd/flux2/discussions) of Flux v2 with us
|
|
||||||
- Join us in #flux-dev on the [CNCF Slack](https://slack.cncf.io)
|
|
||||||
- Check out our [contributor docs](https://toolkit.fluxcd.io/contributing/)
|
|
||||||
- Take a look at the [roadmap for Flux v2](https://toolkit.fluxcd.io/roadmap/)
|
|
||||||
|
|
||||||
### Are there any breaking changes?
|
|
||||||
|
|
||||||
- In Flux v1 Kustomize support was implemented through `.flux.yaml` files in the Git repository. As indicated in the comparison table above, while this approach worked, we found it to be error-prone and hard to debug. The new [Kustomization CR](https://github.com/fluxcd/kustomize-controller/blob/master/docs/spec/v1alpha1/kustomization.md) should make troubleshooting much easier. Unfortunately we needed to drop the support for custom commands as running arbitrary shell scripts in-cluster poses serious security concerns.
|
|
||||||
- Helm users: we redesigned the `HelmRelease` API and the automation will work quite differently, so upgrading to `HelmRelease` v2 will require a little work from you, but you will gain more flexibility, better observability and performance.
|
|
||||||
|
|
||||||
### Is the GitOps Toolkit related to the GitOps Engine?
|
|
||||||
|
|
||||||
In an announcement in August 2019, the expectation was set that the Flux project would integrate the GitOps Engine, then being factored out of ArgoCD. Since the result would be backward-incompatible, it would require a major version bump: Flux v2.
|
|
||||||
|
|
||||||
After experimentation and considerable thought, we (the maintainers) have found a path to Flux v2 that we think better serves our vision of GitOps: the GitOps Toolkit. In consequence, we do not now plan to integrate GitOps Engine into Flux.
|
|
||||||
|
|
||||||
## Kustomize questions
|
## Kustomize questions
|
||||||
|
|
||||||
### Are there two Kustomization types?
|
### Are there two Kustomization types?
|
||||||
@@ -242,3 +149,127 @@ The kustomize-controller creates `kustomization.yaml` files similar to:
|
|||||||
```sh
|
```sh
|
||||||
cd ./deploy/prod && kustomize create --autodetect --recursive
|
cd ./deploy/prod && kustomize create --autodetect --recursive
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Helm questions
|
||||||
|
|
||||||
|
### How to debug "not ready" errors?
|
||||||
|
|
||||||
|
Misconfiguring the `HelmRelease.spec.chart`, like a typo in the chart name, version or chart source URL
|
||||||
|
would result in a "HelmChart is not ready" error displayed by:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ flux get helmreleases --all-namespaces
|
||||||
|
NAMESPACE NAME READY MESSAGE
|
||||||
|
default podinfo False HelmChart 'default/default-podinfo' is not ready
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to get to the root cause, first make sure the source e.g. the `HelmRepository`
|
||||||
|
is configured properly and has access to the remote `index.yaml`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ flux get sources helm --all-namespaces
|
||||||
|
NAMESPACE NAME READY MESSAGE
|
||||||
|
default podinfo False failed to fetch https://stefanprodan.github.io/podinfo2/index.yaml : 404 Not Found
|
||||||
|
```
|
||||||
|
|
||||||
|
If the source is `Ready`, then the error must be caused by the chart,
|
||||||
|
for example due to an invalid chart name or non-existing version:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ flux get sources chart --all-namespaces
|
||||||
|
NAMESPACE NAME READY MESSAGE
|
||||||
|
default default-podinfo False no chart version found for podinfo-9.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flux v1 vs v2 questions
|
||||||
|
|
||||||
|
### What does Flux v2 mean for Flux?
|
||||||
|
|
||||||
|
Flux v1 is a monolithic do-it-all operator; Flux v2 separates the functionalities into specialized controllers, collectively called the GitOps Toolkit.
|
||||||
|
|
||||||
|
You can install and operate Flux v2 simply using the `flux` command. You can easily pick and choose the functionality you need and extend it to serve your own purposes.
|
||||||
|
|
||||||
|
The timeline we are looking at right now is:
|
||||||
|
|
||||||
|
1. Put Flux v1 into maintenance mode (no new features being added; bugfixes and CVEs patched only).
|
||||||
|
1. Continue work on the [Flux v2 roadmap](https://toolkit.fluxcd.io/roadmap/).
|
||||||
|
1. We will provide transition guides for specific user groups, e.g. users of Flux v1 in read-only mode, or of Helm Operator v1, etc. once the functionality is integrated into Flux v2 and it's deemed "ready".
|
||||||
|
1. Once the use-cases of Flux v1 are covered, we will continue supporting Flux v1 for 6 months. This will be the transition period before it's considered unsupported.
|
||||||
|
|
||||||
|
### Why did you rewrite Flux?
|
||||||
|
|
||||||
|
Flux v2 implements its functionality in individual controllers, which allowed us to address long-standing feature requests much more easily.
|
||||||
|
|
||||||
|
By basing these controllers on modern Kubernetes tooling (`controller-runtime` libraries), they can be dynamically configured with Kubernetes custom resources either by cluster admins or by other automated tools -- and you get greatly increased observability.
|
||||||
|
|
||||||
|
This gave us the opportunity to build Flux v2 with the top Flux v1 feature requests in mind:
|
||||||
|
|
||||||
|
- Supporting multiple source Git repositories
|
||||||
|
- Operational insight through health checks, events and alerts
|
||||||
|
- Multi-tenancy capabilities, like applying each source repository with its own set of permissions
|
||||||
|
|
||||||
|
On top of that, testing the individual components and understanding the codebase becomes a lot easier.
|
||||||
|
|
||||||
|
### What are significant new differences between Flux v1 and Flux v2?
|
||||||
|
|
||||||
|
#### Reconciliation
|
||||||
|
|
||||||
|
Flux v1 | Flux v2
|
||||||
|
---------------------------------- | ----------------------------------
|
||||||
|
Limited to a single Git repository | Multiple Git repositories
|
||||||
|
Declarative config via arguments in the Flux deployment | `GitRepository` custom resource, which produces an artifact which can be reconciled by other controllers
|
||||||
|
Follow `HEAD` of Git branches | Supports Git branches, pinning on commits and tags, follow SemVer tag ranges
|
||||||
|
Suspending of reconciliation by downscaling Flux deployment | Reconciliation can be paused per resource by suspending the `GitRepository`
|
||||||
|
Credentials config via Arguments and/or Secret volume mounts in the Flux pod | Credentials config per `GitRepository` resource: SSH private key, HTTP/S username/password/token, OpenPGP public keys
|
||||||
|
|
||||||
|
#### `kustomize` support
|
||||||
|
|
||||||
|
Flux v1 | Flux v2
|
||||||
|
---------------------------------- | ----------------------------------
|
||||||
|
Declarative config through `.flux.yaml` files in the Git repository | Declarative config through a `Kustomization` custom resource, consuming the artifact from the GitRepository
|
||||||
|
Manifests are generated via shell exec and then reconciled by `fluxd` | Generation, server-side validation, and reconciliation is handled by a specialised `kustomize-controller`
|
||||||
|
Reconciliation using the service account of the Flux deployment | Support for service account impersonation
|
||||||
|
Garbage collection needs cluster role binding for Flux to query the Kubernetes discovery API | Garbage collection needs no cluster role binding or access to Kubernetes discovery API
|
||||||
|
Support for custom commands and generators executed by fluxd in a POSIX shell | No support for custom commands
|
||||||
|
|
||||||
|
#### Helm integration
|
||||||
|
|
||||||
|
Flux v1 | Flux v2
|
||||||
|
---------------------------------- | ----------------------------------
|
||||||
|
Declarative config in a single Helm custom resource | Declarative config through `HelmRepository`, `GitRepository`, `Bucket`, `HelmChart` and `HelmRelease` custom resources
|
||||||
|
Chart synchronisation embedded in the operator | Extensive release configuration options, and a reconciliation interval per source
|
||||||
|
Support for fixed SemVer versions from Helm repositories | Support for SemVer ranges for `HelmChart` resources
|
||||||
|
Git repository synchronisation on a global interval | Planned support for charts from GitRepository sources
|
||||||
|
Limited observability via the status object of the HelmRelease resource | Better observability via the HelmRelease status object, Kubernetes events, and notifications
|
||||||
|
Resource heavy, relatively slow | Better performance
|
||||||
|
Chart changes from Git sources are determined from Git metadata | Chart changes must be accompanied by a version bump in `Chart.yaml` to produce a new artifact
|
||||||
|
|
||||||
|
#### Notifications, webhooks, observability
|
||||||
|
|
||||||
|
Flux v1 | Flux v2
|
||||||
|
---------------------------------- | ----------------------------------
|
||||||
|
Emits "custom Flux events" to a webhook endpoint | Emits Kubernetes events for included custom resources
|
||||||
|
RPC endpoint can be configured to a 3rd party solution like FluxCloud to be forwarded as notifications to e.g. Slack | Flux v2 components can be configured to POST the events to a `notification-controller` endpoint. Selective forwarding of POSTed events as notifications using `Provider` and `Alert` custom resources.
|
||||||
|
Webhook receiver is a side-project | Webhook receiver, handling a wide range of platforms, is included
|
||||||
|
Unstructured logging | Structured logging for all components
|
||||||
|
Custom Prometheus metrics | Generic / common `controller-runtime` Prometheus metrics
|
||||||
|
|
||||||
|
### How can I get involved?
|
||||||
|
|
||||||
|
There are a variety of ways and we look forward to having you on board building the future of GitOps together:
|
||||||
|
|
||||||
|
- [Discuss the direction](https://github.com/fluxcd/flux2/discussions) of Flux v2 with us
|
||||||
|
- Join us in #flux-dev on the [CNCF Slack](https://slack.cncf.io)
|
||||||
|
- Check out our [contributor docs](https://toolkit.fluxcd.io/contributing/)
|
||||||
|
- Take a look at the [roadmap for Flux v2](https://toolkit.fluxcd.io/roadmap/)
|
||||||
|
|
||||||
|
### Are there any breaking changes?
|
||||||
|
|
||||||
|
- In Flux v1 Kustomize support was implemented through `.flux.yaml` files in the Git repository. As indicated in the comparison table above, while this approach worked, we found it to be error-prone and hard to debug. The new [Kustomization CR](https://github.com/fluxcd/kustomize-controller/blob/master/docs/spec/v1alpha1/kustomization.md) should make troubleshooting much easier. Unfortunately we needed to drop the support for custom commands as running arbitrary shell scripts in-cluster poses serious security concerns.
|
||||||
|
- Helm users: we redesigned the `HelmRelease` API and the automation will work quite differently, so upgrading to `HelmRelease` v2 will require a little work from you, but you will gain more flexibility, better observability and performance.
|
||||||
|
|
||||||
|
### Is the GitOps Toolkit related to the GitOps Engine?
|
||||||
|
|
||||||
|
In an announcement in August 2019, the expectation was set that the Flux project would integrate the GitOps Engine, then being factored out of ArgoCD. Since the result would be backward-incompatible, it would require a major version bump: Flux v2.
|
||||||
|
|
||||||
|
After experimentation and considerable thought, we (the maintainers) have found a path to Flux v2 that we think better serves our vision of GitOps: the GitOps Toolkit. In consequence, we do not now plan to integrate GitOps Engine into Flux.
|
||||||
|
|||||||
@@ -88,8 +88,7 @@ There is a new `spec.suspend` field, that if set to `true` causes the Helm Contr
|
|||||||
|
|
||||||
We have added support for making Helm releases to other clusters. If the `spec.kubeConfig` field in the `HelmRelease` is set, Helm actions will run against the default cluster specified in that KubeConfig instead of the local cluster that is responsible for the reconciliation of the `HelmRelease`.
|
We have added support for making Helm releases to other clusters. If the `spec.kubeConfig` field in the `HelmRelease` is set, Helm actions will run against the default cluster specified in that KubeConfig instead of the local cluster that is responsible for the reconciliation of the `HelmRelease`.
|
||||||
|
|
||||||
The Helm storage is stored on the remote cluster in a namespace that equals to the namespace of the `HelmRelease`, the release itself is made in either this namespace, or the configured `spec.targetNamespace`. In any case, both are expected to exist, and can for example be created using the [Kustomize Controller](https://toolkit.fluxcd.io/components/kustomize/controller/) which has the same cross-cluster support.
|
The Helm storage is stored on the remote cluster in a namespace that equals to the namespace of the `HelmRelease`, or the configured `spec.storageNamespace`. The release itself is made in a namespace that equals to the namespace of the `HelmRelease`, or the configured `spec.targetNamespace`. The namespaces are expected to exist, and can for example be created using the [Kustomize Controller](https://toolkit.fluxcd.io/components/kustomize/controller/) which has the same cross-cluster support.
|
||||||
|
|
||||||
Other references to Kubernetes resources in the `HelmRelease`, like `ValuesReference` resources, are expected to exist on the reconciling cluster.
|
Other references to Kubernetes resources in the `HelmRelease`, like `ValuesReference` resources, are expected to exist on the reconciling cluster.
|
||||||
|
|
||||||
### Added support for notifications and webhooks
|
### Added support for notifications and webhooks
|
||||||
@@ -681,7 +680,7 @@ The recommended migration steps for a single `HelmRelease` are as follows:
|
|||||||
|
|
||||||
1. Ensure the Helm Operator is not running, as otherwise the Helm Controller and Helm Operator will fight over the release.
|
1. Ensure the Helm Operator is not running, as otherwise the Helm Controller and Helm Operator will fight over the release.
|
||||||
1. Create a [`GitRepository` or `HelmRepository` resource for the `HelmRelease`](#defining-the-helm-chart), including any `Secret` that may be required to access the source. Note that it is possible for multiple `HelmRelease` resources to share a `GitRepository` or `HelmRepository` resource.
|
1. Create a [`GitRepository` or `HelmRepository` resource for the `HelmRelease`](#defining-the-helm-chart), including any `Secret` that may be required to access the source. Note that it is possible for multiple `HelmRelease` resources to share a `GitRepository` or `HelmRepository` resource.
|
||||||
1. Create a new `HelmRelease` resource ([with the `helm.toolkit.fluxcd.io` group domain](#the-helmrelease-custom-resource-group-domain-changed)), define the `spec.releaseName` (and `spec.targetNamespace` if applicable) to match that of the existing release, and rewrite the configuration to adhere to the [API spec changes](#api-spec-changes).
|
1. Create a new `HelmRelease` resource ([with the `helm.toolkit.fluxcd.io` group domain](#the-helmrelease-custom-resource-group-domain-changed)), define the `spec.releaseName` (plus the `spec.targetNamespace` and `spec.storageNamespace` if applicable) to match that of the existing release, and rewrite the configuration to adhere to the [API spec changes](#api-spec-changes).
|
||||||
1. Confirm the Helm Controller successfully upgrades the release.
|
1. Confirm the Helm Controller successfully upgrades the release.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|||||||
@@ -5,23 +5,19 @@ This guide walks you through configuring container image scanning and deployment
|
|||||||
For a container image you can configure Flux to:
|
For a container image you can configure Flux to:
|
||||||
|
|
||||||
- scan the container registry and fetch the image tags
|
- scan the container registry and fetch the image tags
|
||||||
- select the latest tag based on a semver range
|
- select the latest tag based on the defined policy (semver, calver, regex)
|
||||||
- replace the tag in Kubernetes manifests (YAML format)
|
- replace the tag in Kubernetes manifests (YAML format)
|
||||||
- checkout a branch, commit and push the changes to the remote Git repository
|
- checkout a branch, commit and push the changes to the remote Git repository
|
||||||
- apply the changes in-cluster and rollout the container image
|
- apply the changes in-cluster and rollout the container image
|
||||||
|
|
||||||
!!! warning "Alpha version"
|
!!! warning "Alpha version"
|
||||||
Note that the image update feature is currently alpha and, it only supports **semver** filters.
|
Note that the image update feature is currently alpha,
|
||||||
In the future we plan to add support for other filtering options.
|
see the [roadmap](../roadmap/index.md) for more details.
|
||||||
Please see the [roadmap](../roadmap/index.md) for more details.
|
|
||||||
|
|
||||||
For production environments, this feature allows you to automatically deploy application patches
|
For production environments, this feature allows you to automatically deploy application patches
|
||||||
(CVEs and bug fixes), and keep a record of all deployments in Git history.
|
(CVEs and bug fixes), and keep a record of all deployments in Git history.
|
||||||
|
|
||||||
For staging environments, this features allow you to deploy the latest prerelease of an application,
|
**Production CI/CD workflow**
|
||||||
without having to manually edit its deployment manifests in Git.
|
|
||||||
|
|
||||||
Production CI/CD workflow:
|
|
||||||
|
|
||||||
* DEV: push a bug fix to the app repository
|
* DEV: push a bug fix to the app repository
|
||||||
* DEV: bump the patch version and release e.g. `v1.0.1`
|
* DEV: bump the patch version and release e.g. `v1.0.1`
|
||||||
@@ -30,6 +26,17 @@ Production CI/CD workflow:
|
|||||||
* CD: update the image tag in the app manifest to `v1.0.1` (Flux cluster to Git reconciliation)
|
* CD: update the image tag in the app manifest to `v1.0.1` (Flux cluster to Git reconciliation)
|
||||||
* CD: deploy `v1.0.1` to production clusters (Flux Git to cluster reconciliation)
|
* CD: deploy `v1.0.1` to production clusters (Flux Git to cluster reconciliation)
|
||||||
|
|
||||||
|
For staging environments, this features allow you to deploy the latest build of a branch,
|
||||||
|
without having to manually edit the app deployment manifest in Git.
|
||||||
|
|
||||||
|
**Staging CI/CD workflow**
|
||||||
|
|
||||||
|
* DEV: push code changes to the app repository `main` branch
|
||||||
|
* CI: build and push a container image tagged as `${GIT_BRANCH}-${GIT_SHA:0:7}-$(date +%s)`
|
||||||
|
* CD: pull the latest image metadata from the app registry (Flux image scanning)
|
||||||
|
* CD: update the image tag in the app manifest to `main-2d3fcbd-1611906956` (Flux cluster to Git reconciliation)
|
||||||
|
* CD: deploy `main-2d3fcbd-1611906956` to staging clusters (Flux Git to cluster reconciliation)
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
You will need a Kubernetes cluster version 1.16 or newer and kubectl version 1.18.
|
You will need a Kubernetes cluster version 1.16 or newer and kubectl version 1.18.
|
||||||
@@ -49,6 +56,11 @@ export GITHUB_USER=<your-username>
|
|||||||
|
|
||||||
## Install Flux
|
## Install Flux
|
||||||
|
|
||||||
|
!!! hint "Enable image automation components"
|
||||||
|
If you bootstrapped Flux before without the `--components-extra=` argument, you need to add
|
||||||
|
`--components-extra=image-reflector-controller,image-automation-controller` to your
|
||||||
|
bootstrapping routine as image automation components are not installed by default.
|
||||||
|
|
||||||
Install Flux with the image automation components:
|
Install Flux with the image automation components:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -185,6 +197,10 @@ spec:
|
|||||||
If you want to include pre-release e.g. `1.0.0-rc.1`,
|
If you want to include pre-release e.g. `1.0.0-rc.1`,
|
||||||
you can define a range like: `^1.x-0` or `>1.0.0-rc <2.0.0-rc`.
|
you can define a range like: `^1.x-0` or `>1.0.0-rc <2.0.0-rc`.
|
||||||
|
|
||||||
|
!!! hint "Other policy examples"
|
||||||
|
For policies that make use of CalVer, build IDs or alphabetical sorting,
|
||||||
|
have a look at [the examples](../components/image/imagepolicies.md#examples).
|
||||||
|
|
||||||
Commit and push changes to main branch:
|
Commit and push changes to main branch:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -257,7 +273,7 @@ spec:
|
|||||||
messageTemplate: '[ci skip] update image'
|
messageTemplate: '[ci skip] update image'
|
||||||
interval: 1m0s
|
interval: 1m0s
|
||||||
update:
|
update:
|
||||||
setters: {}
|
strategy: Setters
|
||||||
```
|
```
|
||||||
|
|
||||||
Commit and push changes to main branch:
|
Commit and push changes to main branch:
|
||||||
@@ -359,6 +375,60 @@ images:
|
|||||||
newTag: 5.0.0 # {"$imagepolicy": "flux-system:podinfo:tag"}
|
newTag: 5.0.0 # {"$imagepolicy": "flux-system:podinfo:tag"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Trigger image updates with webhooks
|
||||||
|
|
||||||
|
You may want to trigger a deployment
|
||||||
|
as soon as a new image tag is pushed to your container registry.
|
||||||
|
In order to notify the image-reflector-controller about new images,
|
||||||
|
you can [setup webhook receivers](webhook-receivers.md).
|
||||||
|
|
||||||
|
First generate a random string and create a secret with a `token` field:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
TOKEN=$(head -c 12 /dev/urandom | shasum | cut -d ' ' -f1)
|
||||||
|
echo $TOKEN
|
||||||
|
|
||||||
|
kubectl -n flux-system create secret generic webhook-token \
|
||||||
|
--from-literal=token=$TOKEN
|
||||||
|
```
|
||||||
|
|
||||||
|
Define a receiver for DockerHub:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: notification.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: Receiver
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
type: dockerhub
|
||||||
|
secretRef:
|
||||||
|
name: webhook-token
|
||||||
|
resources:
|
||||||
|
- kind: ImageRepository
|
||||||
|
name: podinfo
|
||||||
|
```
|
||||||
|
|
||||||
|
The notification-controller generates a unique URL using the provided token and the receiver name/namespace.
|
||||||
|
|
||||||
|
Find the URL with:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ kubectl -n flux-system get receiver/podinfo
|
||||||
|
|
||||||
|
NAME READY STATUS
|
||||||
|
podinfo True Receiver initialised with URL: /hook/bed6d00b5555b1603e1f59b94d7fdbca58089cb5663633fb83f2815dc626d92b
|
||||||
|
```
|
||||||
|
|
||||||
|
Log in to DockerHub web interface, go to your image registry Settings and select Webhooks.
|
||||||
|
Fill the form "Webhook URL" by composing the address using the receiver
|
||||||
|
LB and the generated URL `http://<LoadBalancerAddress>/<ReceiverURL>`.
|
||||||
|
|
||||||
|
!!! hint "Note"
|
||||||
|
Besides DockerHub, you can define receivers for **Harbor**, **Quay**, **Nexus**, **GCR**,
|
||||||
|
and any other system that supports webhooks e.g. GitHub Actions, Jenkins, CircleCI, etc.
|
||||||
|
See the [Receiver CRD docs](../components/notification/receiver.md) for more details.
|
||||||
|
|
||||||
## ImageRepository cloud providers authentication
|
## ImageRepository cloud providers authentication
|
||||||
|
|
||||||
If relying on a cloud provider image repository, you might need to do some extra
|
If relying on a cloud provider image repository, you might need to do some extra
|
||||||
@@ -414,6 +484,7 @@ apiVersion: v1
|
|||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
metadata:
|
metadata:
|
||||||
name: ecr-credentials-sync
|
name: ecr-credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
# Uncomment and edit if using IRSA
|
# Uncomment and edit if using IRSA
|
||||||
# annotations:
|
# annotations:
|
||||||
# eks.amazonaws.com/role-arn: <role arn>
|
# eks.amazonaws.com/role-arn: <role arn>
|
||||||
@@ -463,8 +534,8 @@ spec:
|
|||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
env:
|
env:
|
||||||
- name: SECRET_NAME
|
- name: SECRET_NAME
|
||||||
value: <secret name> # this is the generated Secret name
|
value: ecr-credentials
|
||||||
- name:
|
- name: ECR_REGISTRY
|
||||||
value: <account id>.dkr.ecr.<region>.amazonaws.com # fill in the account id and region
|
value: <account id>.dkr.ecr.<region>.amazonaws.com # fill in the account id and region
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /token
|
- mountPath: /token
|
||||||
@@ -493,15 +564,26 @@ you can manually create an init job using the following command:
|
|||||||
$ kubectl create job --from=cronjob/ecr-credentials-sync -n flux-system ecr-credentials-sync-init
|
$ kubectl create job --from=cronjob/ecr-credentials-sync -n flux-system ecr-credentials-sync-init
|
||||||
```
|
```
|
||||||
|
|
||||||
|
After the job runs, a secret named `ecr-credentials` should be created. Use this
|
||||||
|
name in your ECR ImageRepository resource manifest as the value for
|
||||||
|
`.spec.secretRef.name`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: ecr-credentials
|
||||||
|
```
|
||||||
|
|
||||||
### GCP Container Registry
|
### GCP Container Registry
|
||||||
|
|
||||||
#### Using access token [short-lived]
|
#### Using access token [short-lived]
|
||||||
|
|
||||||
!!!note "Workload Identity"
|
!!!note "Workload Identity"
|
||||||
Please ensure that you enable workload identity for your cluster, create a GCP service account that has access to the container registry and create an IAM policy binding between the GCP service account and the Kubernetes service account so that the pods created by the cronjob can access GCP APIs and get the token.
|
Please ensure that you enable workload identity for your cluster, create a GCP service account that has
|
||||||
|
access to the container registry and create an IAM policy binding between the GCP service account and
|
||||||
|
the Kubernetes service account so that the pods created by the cronjob can access GCP APIs and get the token.
|
||||||
Take a look at [this guide](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
|
Take a look at [this guide](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
|
||||||
|
|
||||||
|
|
||||||
The access token for GCR expires hourly.
|
The access token for GCR expires hourly.
|
||||||
Considering this limitation, one needs to ensure the credentials are being
|
Considering this limitation, one needs to ensure the credentials are being
|
||||||
refreshed before expiration so that the controller can rely on them for
|
refreshed before expiration so that the controller can rely on them for
|
||||||
@@ -570,7 +652,7 @@ spec:
|
|||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
env:
|
env:
|
||||||
- name: SECRET_NAME
|
- name: SECRET_NAME
|
||||||
value: <SECRET_NAME> # this is the generated Secret name
|
value: gcr-credentials
|
||||||
- name: GCR_REGISTRY
|
- name: GCR_REGISTRY
|
||||||
value: <REGISTRY_NAME> # fill in the registry name e.g gcr.io, eu.gcr.io
|
value: <REGISTRY_NAME> # fill in the registry name e.g gcr.io, eu.gcr.io
|
||||||
command:
|
command:
|
||||||
@@ -591,18 +673,37 @@ you can manually create an init job using the following command:
|
|||||||
$ kubectl create job --from=cronjob/gcr-credentials-sync -n flux-system gcr-credentials-sync-init
|
$ kubectl create job --from=cronjob/gcr-credentials-sync -n flux-system gcr-credentials-sync-init
|
||||||
```
|
```
|
||||||
|
|
||||||
|
After the job runs, a secret named `gcr-credentials` should be created. Use this
|
||||||
|
name in your GCR ImageRepository resource manifest as the value for
|
||||||
|
`.spec.secretRef.name`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: gcr-credentials
|
||||||
|
```
|
||||||
|
|
||||||
#### Using a JSON key [long-lived]
|
#### Using a JSON key [long-lived]
|
||||||
|
|
||||||
!!! warning "Less secure option"
|
!!! warning "Less secure option"
|
||||||
From [Google documentation on authenticating container registry](https://cloud.google.com/container-registry/docs/advanced-authentication#json-key)
|
From [Google documentation on authenticating container registry](https://cloud.google.com/container-registry/docs/advanced-authentication#json-key)
|
||||||
> A user-managed key-pair that you can use as a credential for a service account. Because the credential is long-lived, it is the least secure option of all the available authentication methods. When possible, use an access token or another available authentication method to reduce the risk of unauthorized access to your artifacts. If you must use a service account key, ensure that you follow best practices for managing credentials.
|
> A user-managed key-pair that you can use as a credential for a service account.
|
||||||
|
> Because the credential is long-lived, it is the least secure option of all the available authentication methods.
|
||||||
|
> When possible, use an access token or another available authentication method to reduce the risk of
|
||||||
|
> unauthorized access to your artifacts. If you must use a service account key,
|
||||||
|
> ensure that you follow best practices for managing credentials.
|
||||||
|
|
||||||
|
A Json key doesn't expire, so we don't need a cronjob,
|
||||||
|
we just need to create the secret and reference it in the ImagePolicy.
|
||||||
|
|
||||||
Json keys doesn't expire so we don't need a cronjob, we just need to create the secret and reference it in the ImagePolicy.
|
First, create a json key file by following this
|
||||||
|
[documentation](https://cloud.google.com/container-registry/docs/advanced-authentication).
|
||||||
|
Grant the service account the role of `Container Registry Service Agent`
|
||||||
|
so that it can access GCR and download the json file.
|
||||||
|
|
||||||
First, create a json key file by following this [documentation](https://cloud.google.com/container-registry/docs/advanced-authentication). Grant the service account the role of `Container Registry Service Agent` so that it can access GCR and download the json file.
|
Then create a secret, encrypt it using [Mozilla SOPS](mozilla-sops.md)
|
||||||
|
or [Sealed Secrets](sealed-secrets.md) , commit and push the encypted file to git.
|
||||||
|
|
||||||
Then create a secret, encrypt it using [Mozilla SOPS](mozilla-sops.md) or [Sealed Secrets](sealed-secrets.md) , commit and push the encypted file to git.
|
|
||||||
```
|
```
|
||||||
kubectl create secret docker-registry <secret-name> \
|
kubectl create secret docker-registry <secret-name> \
|
||||||
--docker-server=<GCR-REGISTRY> \ # e.g gcr.io
|
--docker-server=<GCR-REGISTRY> \ # e.g gcr.io
|
||||||
@@ -612,4 +713,4 @@ Then create a secret, encrypt it using [Mozilla SOPS](mozilla-sops.md) or [Seale
|
|||||||
|
|
||||||
### Azure Container Registry
|
### Azure Container Registry
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|||||||
@@ -242,9 +242,7 @@ mkdir -p ./clusters/my-cluster/flux-system
|
|||||||
Generate the Flux manifests with:
|
Generate the Flux manifests with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# on ARM64/AARCH64 clusters use --arch=arm64
|
|
||||||
flux install --version=latest \
|
flux install --version=latest \
|
||||||
--arch=amd64 \
|
|
||||||
--export > ./clusters/my-cluster/flux-system/gotk-components.yaml
|
--export > ./clusters/my-cluster/flux-system/gotk-components.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -388,7 +386,6 @@ Kubernetes manifests that can be used to install or upgrade Flux:
|
|||||||
```hcl
|
```hcl
|
||||||
data "flux_install" "main" {
|
data "flux_install" "main" {
|
||||||
target_path = "clusters/my-cluster"
|
target_path = "clusters/my-cluster"
|
||||||
arch = "amd64"
|
|
||||||
network_policy = false
|
network_policy = false
|
||||||
version = "latest"
|
version = "latest"
|
||||||
}
|
}
|
||||||
@@ -515,6 +512,10 @@ flux create helmrelease nginx \
|
|||||||
|
|
||||||
## Upgrade
|
## Upgrade
|
||||||
|
|
||||||
|
!!! note "Patch versions"
|
||||||
|
It is safe and advised to use the latest PATCH version when upgrading to a
|
||||||
|
new MINOR version.
|
||||||
|
|
||||||
Update Flux CLI to the latest release with `brew upgrade fluxcd/tap/flux` or by
|
Update Flux CLI to the latest release with `brew upgrade fluxcd/tap/flux` or by
|
||||||
downloading the binary from [GitHub](https://github.com/fluxcd/flux2/releases).
|
downloading the binary from [GitHub](https://github.com/fluxcd/flux2/releases).
|
||||||
|
|
||||||
|
|||||||
@@ -107,8 +107,9 @@ secrets by iterating over all the private keys until it finds one that works.
|
|||||||
### Using various cloud providers
|
### Using various cloud providers
|
||||||
|
|
||||||
When using AWS/GCP KMS, you don't have to include the gpg `secretRef` under
|
When using AWS/GCP KMS, you don't have to include the gpg `secretRef` under
|
||||||
`spec.provider` (you can skip the `--decryption-secret` flag when running `flux create kustomization`), instead you'll have to bind an IAM Role with access to the KMS
|
`spec.provider` (you can skip the `--decryption-secret` flag when running `flux create kustomization`),
|
||||||
keys to the `default` service account of the `flux-system` namespace for
|
instead you'll have to bind an IAM Role with access to the KMS
|
||||||
|
keys to the `kustomize-controller` service account of the `flux-system` namespace for
|
||||||
kustomize-controller to be able to fetch keys from KMS.
|
kustomize-controller to be able to fetch keys from KMS.
|
||||||
|
|
||||||
#### AWS
|
#### AWS
|
||||||
@@ -145,13 +146,12 @@ or with [add-pod-identity](https://github.com/Azure/aad-pod-identity).
|
|||||||
Please ensure that the GKE cluster has Workload Identity enabled.
|
Please ensure that the GKE cluster has Workload Identity enabled.
|
||||||
|
|
||||||
1. Create a service account with the role `Cloud KMS CryptoKey Encrypter/Decrypter`.
|
1. Create a service account with the role `Cloud KMS CryptoKey Encrypter/Decrypter`.
|
||||||
2. Create an IAM policy binding between the GCP service account to the `default` service account of the `flux-system`.
|
2. Create an IAM policy binding between the GCP service account to the `kustomize-controller` service account of the `flux-system`.
|
||||||
3. Annotate the `default` service account in the `flux-system` with the GCP service account.
|
3. Annotate the `kustomize-controller` service account in the `flux-system` with the GCP service account.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
kubectl annotate serviceaccount \
|
kubectl annotate serviceaccount kustomize-controller \
|
||||||
--namespace flux-system \
|
--namespace flux-system \
|
||||||
default \
|
|
||||||
iam.gke.io/gcp-service-account=<name-of-serviceaccount>@project-id.iam.gserviceaccount.com
|
iam.gke.io/gcp-service-account=<name-of-serviceaccount>@project-id.iam.gserviceaccount.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ the API.
|
|||||||
After installing the `flux` CLI and running a couple of very simple commands,
|
After installing the `flux` CLI and running a couple of very simple commands,
|
||||||
you will have a GitOps workflow setup which involves a staging and a production cluster.
|
you will have a GitOps workflow setup which involves a staging and a production cluster.
|
||||||
|
|
||||||
|
If you should need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
||||||
|
|
||||||
## More detail on what's in Flux
|
## More detail on what's in Flux
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|||||||
@@ -16,12 +16,20 @@ Repositories subject to semver releases:
|
|||||||
1. [fluxcd/helm-controller](https://github.com/fluxcd/helm-controller)
|
1. [fluxcd/helm-controller](https://github.com/fluxcd/helm-controller)
|
||||||
- modules: `api`
|
- modules: `api`
|
||||||
- dependencies: `github.com/fluxcd/source-controller/api`, `github.com/fluxcd/pkg/*`
|
- dependencies: `github.com/fluxcd/source-controller/api`, `github.com/fluxcd/pkg/*`
|
||||||
|
1. [fluxcd/image-reflector-controller](https://github.com/fluxcd/image-reflector-controller)
|
||||||
|
- modules: `api`
|
||||||
|
- dependencies: `github.com/fluxcd/pkg/*`
|
||||||
|
1. [fluxcd/image-automation-controller](https://github.com/fluxcd/image-automation-controller)
|
||||||
|
- modules: `api`
|
||||||
|
- dependencies: `github.com/fluxcd/source-controller/api`, `github.com/fluxcd/image-reflector-controller/api`, `github.com/fluxcd/pkg/*`
|
||||||
1. [fluxcd/notification-controller](https://github.com/fluxcd/notification-controller)
|
1. [fluxcd/notification-controller](https://github.com/fluxcd/notification-controller)
|
||||||
- modules: `api`
|
- modules: `api`
|
||||||
- dependencies: `github.com/fluxcd/source-controller/api`, `github.com/fluxcd/pkg/*`
|
- dependencies: `github.com/fluxcd/source-controller/api`, `github.com/fluxcd/pkg/*`
|
||||||
1. [fluxcd/flux2](https://github.com/fluxcd/flux2)
|
1. [fluxcd/flux2](https://github.com/fluxcd/flux2)
|
||||||
- modules: `manifestgen`
|
- modules: `manifestgen`
|
||||||
- dependencies: `github.com/fluxcd/source-controller/api`, `github.com/fluxcd/kustomize-controller/api`, `github.com/fluxcd/helm-controller/api`, `github.com/fluxcd/notification-controller/api`,`github.com/fluxcd/pkg/*`
|
- dependencies: `github.com/fluxcd/source-controller/api`, `github.com/fluxcd/kustomize-controller/api`, `github.com/fluxcd/helm-controller/api`, `github.com/fluxcd/image-reflector-controller/api`, `github.com/fluxcd/image-automation-controller/api`, `github.com/fluxcd/notification-controller/api`, `github.com/fluxcd/pkg/*`
|
||||||
|
1. [fluxcd/terraform-provider-flux](https://github.com/fluxcd/terraform-provider-flux)
|
||||||
|
- dependencies: `github.com/fluxcd/flux2/pkg/manifestgen`
|
||||||
|
|
||||||
## Release procedure
|
## Release procedure
|
||||||
|
|
||||||
@@ -77,16 +85,17 @@ Flux has the following Kubernetes dependencies:
|
|||||||
**Note** that all `k8s.io/*` packages must the have the same version in `go.mod` e.g.:
|
**Note** that all `k8s.io/*` packages must the have the same version in `go.mod` e.g.:
|
||||||
|
|
||||||
```
|
```
|
||||||
k8s.io/api v0.19.2
|
k8s.io/api v0.20.2
|
||||||
k8s.io/apiextensions-apiserver v0.19.2
|
k8s.io/apiextensions-apiserver v0.20.2
|
||||||
k8s.io/apimachinery v0.19.2
|
k8s.io/apimachinery v0.20.2
|
||||||
k8s.io/cli-runtime v0.19.2
|
k8s.io/cli-runtime v0.20.2
|
||||||
k8s.io/client-go v0.19.2
|
k8s.io/client-go v0.20.2
|
||||||
```
|
```
|
||||||
|
|
||||||
The specialised reconcilers depend on:
|
The specialised reconcilers depend on:
|
||||||
|
|
||||||
- kustomize-controller: `sigs.k8s.io/kustomize/api`
|
- kustomize-controller: `sigs.k8s.io/kustomize/api`
|
||||||
|
- image-automation-controller: `sigs.k8s.io/kustomize/kyaml`
|
||||||
- helm-controller: `helm.sh/helm/v3`
|
- helm-controller: `helm.sh/helm/v3`
|
||||||
|
|
||||||
**Note** that the `k8s.io/*` version must be compatible with both `kustomize/api` and `helm/v3`.
|
**Note** that the `k8s.io/*` version must be compatible with both `kustomize/api` and `helm/v3`.
|
||||||
@@ -111,7 +120,7 @@ Upgrade procedure:
|
|||||||
1. Update the `github.com/fluxcd/pkg/runtime` version in `source-controller/go.mod`
|
1. Update the `github.com/fluxcd/pkg/runtime` version in `source-controller/go.mod`
|
||||||
1. Release the `api` package
|
1. Release the `api` package
|
||||||
|
|
||||||
`fluxcd/<kustomize|helm|notification>-controller`:
|
`fluxcd/<kustomize|helm|notification|image-automation>-controller`:
|
||||||
|
|
||||||
1. Update the `github.com/fluxcd/source-controller/api` version in `controller/api/go.mod` and `controller/go.mod`
|
1. Update the `github.com/fluxcd/source-controller/api` version in `controller/api/go.mod` and `controller/go.mod`
|
||||||
1. Update the `github.com/fluxcd/pkg/apis/meta` version in `controller/api/go.mod` and `controller/go.mod`
|
1. Update the `github.com/fluxcd/pkg/apis/meta` version in `controller/api/go.mod` and `controller/go.mod`
|
||||||
@@ -121,6 +130,10 @@ Upgrade procedure:
|
|||||||
|
|
||||||
`fluxcd/flux2`:
|
`fluxcd/flux2`:
|
||||||
|
|
||||||
1. Update the `github.com/fluxcd/*-controller/api` version in `flux2/go.mod`
|
1. Update the `github.com/fluxcd/*-controller/api` version in `flux2/go.mod` (automated with [GitHub Actions](../../.github/workflows/update.yml))
|
||||||
1. Update the `github.com/fluxcd/pkg/*` version in `flux2/go.mod`
|
1. Update the `github.com/fluxcd/pkg/*` version in `flux2/go.mod`
|
||||||
1. Update the `k8s.io/*` and `github.com/fluxcd/pkg/runtime` version in `flux2/go.mod`
|
1. Update the `k8s.io/*` and `github.com/fluxcd/pkg/runtime` version in `flux2/go.mod`
|
||||||
|
|
||||||
|
`fluxcd/terraform-provider-flux`:
|
||||||
|
|
||||||
|
1. Update the `github.com/fluxcd/flux2` version in `terraform-provider-flux/go.mod` (automated with [GitHub Actions](https://github.com/fluxcd/terraform-provider-flux/blob/main/.github/workflows/update.yaml))
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user