1
0
mirror of synced 2026-03-02 03:26:57 +00:00

Compare commits

...

44 Commits

Author SHA1 Message Date
Stefan Prodan
503e5ec950 Merge pull request #358 from fluxcd/update-components
Update toolkit components
2020-10-19 17:56:48 +03:00
fluxcdbot
414aeb0ac3 Update toolkit components 2020-10-19 14:23:20 +00:00
Stefan Prodan
55b8544d32 Merge pull request #350 from allymparker/source-git-secret-ref
Add secret-ref flag to create source git
2020-10-19 17:21:14 +03:00
Ally Parker
2d67ea5f7f Add secret-ref flag to git source
Add secret-ref flag to Helm source

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

I estimated that having the design basics figured out is worth 30%
overall.
2020-10-15 12:12:04 +01:00
Stefan Prodan
4464252cae Merge pull request #337 from fluxcd/monitoring
Add cluster stats dashboard to monitoring stack
2020-10-14 17:12:30 +03:00
Stefan Prodan
ca8cf44200 Add cluster stats dashboard to docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-14 16:34:46 +03:00
Stefan Prodan
4565165579 Add cluster stats dashboard
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2020-10-14 16:16:56 +03:00
98 changed files with 2275 additions and 1032 deletions

View File

@@ -15,18 +15,14 @@ jobs:
- name: Copy assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SOURCE_VER: ${{ 'v0.1.1' }}
KUSTOMIZE_VER: ${{ 'v0.1.1' }}
HELM_VER: ${{ 'v0.1.2' }}
NOTIFICATION_VER: ${{ 'v0.1.1' }}
run: |
controller_version() {
sed -n "s/\(.*$1\/.*?ref=\)//p;n" "manifests/bases/$1/kustomization.yaml"
sed -n "s/.*$1\/archive\/\(.*\).zip.*/\1/p;n" manifests/bases/$1/kustomization.yaml
}
{
# source-controller CRDs
# SOURCE_VER=$(controller_version source-controller)
SOURCE_VER=$(controller_version source-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/api/source.md" > docs/components/source/api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/gitrepositories.md" > docs/components/source/gitrepositories.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/source-controller/$SOURCE_VER/docs/spec/v1beta1/helmrepositories.md" > docs/components/source/helmrepositories.md
@@ -36,21 +32,21 @@ jobs:
{
# kustomize-controller CRDs
# KUSTOMIZE_VER=$(controller_version kustomize-controller)
KUSTOMIZE_VER=$(controller_version kustomize-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/kustomize-controller/$KUSTOMIZE_VER/docs/api/kustomize.md" > docs/components/kustomize/api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/kustomize-controller/$KUSTOMIZE_VER/docs/spec/v1beta1/kustomization.md" > docs/components/kustomize/kustomization.md
}
{
# helm-controller CRDs
# HELM_VER=$(controller_version helm-controller)
HELM_VER=$(controller_version helm-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/helm-controller/$HELM_VER/docs/api/helmrelease.md" > docs/components/helm/api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/helm-controller/$HELM_VER/docs/spec/v2beta1/helmreleases.md" > docs/components/helm/helmreleases.md
}
{
# notification-controller CRDs
# NOTIFICATION_VER=$(controller_version notification-controller)
NOTIFICATION_VER=$(controller_version notification-controller)
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/api/notification.md" > docs/components/notification/api.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/event.md" > docs/components/notification/event.md
curl -# -Lf "https://raw.githubusercontent.com/fluxcd/notification-controller/$NOTIFICATION_VER/docs/spec/v1beta1/alert.md" > docs/components/notification/alert.md

View File

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

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

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

View File

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

View File

@@ -6,6 +6,7 @@ builds:
goos:
- darwin
- linux
- windows
goarch:
- amd64
- arm64

View File

@@ -33,7 +33,7 @@ curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
. <(gotk completion bash)
```
Binaries for macOS and Linux AMD64/ARM64 are available to download on the
Binaries for macOS, Windows and Linux AMD64/ARM are available to download on the
[release page](https://github.com/fluxcd/toolkit/releases).
Verify that your cluster satisfies the prerequisites with:
@@ -82,17 +82,13 @@ The GitOps Toolkit is always looking for new contributors and there are a multit
- Check out [how to contribute](CONTRIBUTING.md) to the project
## Featured Talks
- [12 Oct 2020 - Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
- [4 Sep 2020 - KubeCon/CloudNativeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
- [25 June 2020 - Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
- [7 May 2020 - GitOps Days - Community Special: GitOps Toolkit Experimentation with Stefan Prodan](https://youtu.be/WHzxunv4DKk?t=6521)
- 12 Oct 2020 - [Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
- 4 Sep 2020 - [KubeCon/CloudNativeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
- 25 June 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
- 7 May 2020 - [GitOps Days - Community Special: GitOps Toolkit Experimentation with Stefan Prodan](https://youtu.be/WHzxunv4DKk?t=6521)
### Upcoming Meetups
- [19 October 2020 - GitOps Toolkit Guide Walk-through](https://www.meetup.com/GitOps-Community/events/273640196/)
Join us 10am PT / 18:00 BST) for to this special walk-through of the GitOps Toolkit!
Through this talk you'll be able to see how the upcoming Flux v2 and GitOps Toolkit will bring
great improvements to the Flux that you love! Watch or follow along as Leigh Capili shares some
highlights and then goes through Getting Started with GitOps Toolkit.
- 2 November 2020 - GitOps Toolkit Guide Walk-through - Part 2 (TBD)
- 19 Oct 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 1](https://www.meetup.com/GitOps-Community/events/273640196/)
- 2 Nov 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 2](https://www.meetup.com/GitOps-Community/events/273934676/)
We are looking forward to seeing you with us!

View File

@@ -38,6 +38,8 @@ import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/flags"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/fluxcd/toolkit/pkg/install"
)
@@ -52,11 +54,11 @@ var (
bootstrapComponents []string
bootstrapRegistry string
bootstrapImagePullSecret string
bootstrapArch string
bootstrapArch flags.Arch = "amd64"
bootstrapBranch string
bootstrapWatchAllNamespaces bool
bootstrapNetworkPolicy bool
bootstrapLogLevel string
bootstrapLogLevel flags.LogLevel = "info"
bootstrapManifestsPath string
bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"}
)
@@ -77,8 +79,7 @@ func init() {
"container registry where the toolkit images are published")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapImagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArch, "arch", "amd64",
"arch can be amd64 or arm64")
bootstrapCmd.PersistentFlags().Var(&bootstrapArch, "arch", bootstrapArch.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapBranch, "branch", bootstrapDefaultBranch,
"default branch (for GitHub this must match the default branch setting for the organization)")
rootCmd.AddCommand(bootstrapCmd)
@@ -86,22 +87,14 @@ func init() {
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapNetworkPolicy, "network-policy", true,
"deny ingress access to the toolkit controllers from other namespaces using network policies")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapLogLevel, "log-level", "info", "set the controllers log level")
bootstrapCmd.PersistentFlags().Var(&bootstrapLogLevel, "log-level", bootstrapLogLevel.Description())
bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory")
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
}
func bootstrapValidate() error {
if !utils.containsItemString(supportedArch, bootstrapArch) {
return fmt.Errorf("arch %s is not supported, can be %v", bootstrapArch, supportedArch)
}
if !utils.containsItemString(supportedLogLevels, bootstrapLogLevel) {
return fmt.Errorf("log level %s is not supported, can be %v", bootstrapLogLevel, supportedLogLevels)
}
for _, component := range bootstrapRequiredComponents {
if !utils.containsItemString(bootstrapComponents, component) {
if !utils.ContainsItemString(bootstrapComponents, component) {
return fmt.Errorf("component %s is required", component)
}
}
@@ -124,10 +117,10 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
Components: bootstrapComponents,
Registry: bootstrapRegistry,
ImagePullSecret: bootstrapImagePullSecret,
Arch: bootstrapArch,
Arch: bootstrapArch.String(),
WatchAllNamespaces: bootstrapWatchAllNamespaces,
NetworkPolicy: bootstrapNetworkPolicy,
LogLevel: bootstrapLogLevel,
LogLevel: bootstrapLogLevel.String(),
NotificationController: defaultNotification,
ManifestsFile: fmt.Sprintf("%s.yaml", namespace),
Timeout: timeout,
@@ -150,15 +143,14 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
}
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
command := fmt.Sprintf("kubectl apply -f %s", manifestPath)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
kubectlArgs := []string{"apply", "-f", manifestPath}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
}
for _, deployment := range components {
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
namespace, deployment, timeout.String())
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
}
}
@@ -195,7 +187,7 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
return err
}
if err := utils.writeFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, bootstrapSourceManifest)); err != nil {
if err := utils.WriteFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, bootstrapSourceManifest)); err != nil {
return err
}
@@ -219,6 +211,7 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
Kind: sourcev1.GitRepositoryKind,
Name: name,
},
Validation: "client",
},
}
@@ -227,11 +220,11 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
return err
}
if err := utils.writeFile(string(ksData), filepath.Join(tmpDir, targetPath, namespace, bootstrapKustomizationManifest)); err != nil {
if err := utils.WriteFile(string(ksData), filepath.Join(tmpDir, targetPath, namespace, bootstrapKustomizationManifest)); err != nil {
return err
}
if err := utils.generateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
return err
}
@@ -239,20 +232,22 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
}
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error {
command := fmt.Sprintf("kubectl apply -k %s", filepath.Join(tmpDir, targetPath, namespace))
if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil {
kubectlArgs := []string{"apply", "-k", filepath.Join(tmpDir, targetPath, namespace)}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubectlArgs...); err != nil {
return err
}
logger.Waitingf("waiting for cluster sync")
var gitRepository sourcev1.GitRepository
if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil {
return err
}
var kustomization kustomizev1.Kustomization
if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
return err
}

View File

@@ -28,6 +28,7 @@ import (
"github.com/spf13/cobra"
"github.com/fluxcd/pkg/git"
"github.com/fluxcd/toolkit/internal/utils"
)
var bootstrapGitHubCmd = &cobra.Command{
@@ -183,7 +184,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("components are up to date")
}
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -26,8 +26,11 @@ import (
"time"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fluxcd/pkg/git"
"github.com/fluxcd/toolkit/internal/utils"
)
var bootstrapGitLabCmd = &cobra.Command{
@@ -41,14 +44,17 @@ the bootstrap command will perform an upgrade if needed.`,
Example: ` # Create a GitLab API token and export it as an env var
export GITLAB_TOKEN=<my-token>
# Run bootstrap for a private repo owned by a GitLab group
# Run bootstrap for a private repo using HTTPS token authentication
gotk bootstrap gitlab --owner=<group> --repository=<repo name>
# Run bootstrap for a private repo using SSH authentication
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --ssh-hostname=gitlab.com
# Run bootstrap for a repository path
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --path=dev-cluster
# Run bootstrap for a public repository on a personal account
gotk bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
gotk bootstrap gitlab --owner=<user> --repository=<repo name> --private=false --personal=true
# Run bootstrap for a private repo hosted on a GitLab server
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
@@ -77,7 +83,7 @@ func init() {
bootstrapGitLabCmd.Flags().BoolVar(&glPrivate, "private", true, "is private repository")
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, defaults to hostname if not specified")
bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, when specified a deploy key will be added to the repository")
bootstrapGitLabCmd.Flags().StringVar(&glPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
@@ -107,7 +113,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
IsPersonal: glPersonal,
}
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -172,34 +178,54 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("install completed")
}
// setup SSH deploy key
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
logger.Actionf("configuring deploy key")
u, err := url.Parse(repository.GetSSH())
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
repoURL := repository.GetURL()
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
}
if glSSHHostname != "" {
// setup SSH deploy key
repoURL = repository.GetSSH()
if shouldCreateDeployKey(ctx, kubeClient, namespace) {
logger.Actionf("configuring deploy key")
u, err := url.Parse(repoURL)
if err != nil {
return fmt.Errorf("git URL parse failed: %w", err)
}
keyName := "gotk"
if glPath != "" {
keyName = fmt.Sprintf("gotk-%s", glPath)
}
key, err := generateDeployKey(ctx, kubeClient, u, namespace)
if err != nil {
return fmt.Errorf("generating deploy key failed: %w", err)
}
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
keyName := "gotk"
if glPath != "" {
keyName = fmt.Sprintf("gotk-%s", glPath)
}
if changed, err := provider.AddDeployKey(ctx, repository, key, keyName); err != nil {
return err
} else if changed {
logger.Successf("deploy key configured")
}
}
} else {
// setup HTTPS token auth
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
Namespace: namespace,
},
StringData: map[string]string{
"username": "git",
"password": glToken,
},
}
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
} else if changed {
logger.Successf("deploy key configured")
}
}
// configure repo synchronization
logger.Actionf("generating sync manifests")
if err := generateSyncManifests(repository.GetSSH(), bootstrapBranch, namespace, namespace, glPath, tmpDir, glInterval); err != nil {
if err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, glPath, tmpDir, glInterval); err != nil {
return err
}

View File

@@ -18,13 +18,15 @@ package main
import (
"context"
"fmt"
"encoding/json"
"os"
"os/exec"
"strings"
"github.com/blang/semver/v4"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
@@ -48,6 +50,10 @@ var (
checkComponents []string
)
type kubectlVersion struct {
ClientVersion *apimachineryversion.Info `json:"clientVersion"`
}
func init() {
checkCmd.Flags().BoolVarP(&checkPre, "pre", "", false,
"only run pre-installation checks")
@@ -97,14 +103,20 @@ func kubectlCheck(ctx context.Context, version string) bool {
return false
}
command := "kubectl version --client --short | awk '{ print $3 }'"
output, err := utils.execCommand(ctx, ModeCapture, command)
kubectlArgs := []string{"version", "--client", "--output", "json"}
output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubectlArgs...)
if err != nil {
logger.Failuref("kubectl version can't be determined")
return false
}
v, err := semver.ParseTolerant(output)
kv := &kubectlVersion{}
if err = json.Unmarshal([]byte(output), kv); err != nil {
logger.Failuref("kubectl version output can't be unmarshaled")
return false
}
v, err := semver.ParseTolerant(kv.ClientVersion.GitVersion)
if err != nil {
logger.Failuref("kubectl version can't be parsed")
return false
@@ -161,9 +173,8 @@ func componentsCheck() bool {
ok := true
for _, deployment := range checkComponents {
command := fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
namespace, deployment, timeout.String())
if output, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
kubectlArgs := []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubectlArgs...); err != nil {
logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
ok = false
} else {

View File

@@ -19,7 +19,9 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
@@ -61,7 +63,7 @@ func init() {
func createAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("alert name is required")
return fmt.Errorf("Alert name is required")
}
name := args[0]
@@ -71,7 +73,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
eventSources := []notificationv1.CrossNamespaceObjectReference{}
for _, eventSource := range aEventSources {
kind, name := utils.parseObjectKindName(eventSource)
kind, name := utils.ParseObjectKindName(eventSource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
}
@@ -92,7 +94,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating alert")
logger.Generatef("generating Alert")
}
alert := notificationv1.Alert{
@@ -118,28 +120,28 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying alert")
if err := upsertAlert(ctx, kubeClient, alert); err != nil {
logger.Actionf("applying Alert")
namespacedName, err := upsertAlert(ctx, kubeClient, &alert)
if err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, name, namespace)); err != nil {
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("alert %s is ready", name)
logger.Successf("Alert %s is ready", name)
return nil
}
func upsertAlert(ctx context.Context, kubeClient client.Client, alert notificationv1.Alert) error {
func upsertAlert(ctx context.Context, kubeClient client.Client,
alert *notificationv1.Alert) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: alert.GetNamespace(),
Name: alert.GetName(),
@@ -149,35 +151,30 @@ func upsertAlert(ctx context.Context, kubeClient client.Client, alert notificati
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &alert); err != nil {
return err
if err := kubeClient.Create(ctx, alert); err != nil {
return namespacedName, err
} else {
logger.Successf("alert created")
return nil
logger.Successf("Alert created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = alert.Labels
existing.Spec = alert.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("alert updated")
return nil
alert = &existing
logger.Successf("Alert updated")
return namespacedName, nil
}
func isAlertReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isAlertReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
return func() (bool, error) {
var alert notificationv1.Alert
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alert)
err := kubeClient.Get(ctx, namespacedName, alert)
if err != nil {
return false, err
}

View File

@@ -19,7 +19,6 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
@@ -30,6 +29,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
)
var createAlertProviderCmd = &cobra.Command{
@@ -71,12 +72,12 @@ func init() {
func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("provider name is required")
return fmt.Errorf("Provider name is required")
}
name := args[0]
if apType == "" {
return fmt.Errorf("type is required")
return fmt.Errorf("Provider type is required")
}
sourceLabels, err := parseLabels()
@@ -85,10 +86,10 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating provider")
logger.Generatef("generating Provider")
}
alertProvider := notificationv1.Provider{
provider := notificationv1.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
@@ -99,84 +100,84 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
Channel: apChannel,
Username: apUsername,
Address: apAddress,
SecretRef: &corev1.LocalObjectReference{
Name: apSecretRef,
},
},
}
if apSecretRef != "" {
provider.Spec.SecretRef = &corev1.LocalObjectReference{
Name: apSecretRef,
}
}
if export {
return exportAlertProvider(alertProvider)
return exportAlertProvider(provider)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying provider")
if err := upsertAlertProvider(ctx, kubeClient, alertProvider); err != nil {
logger.Actionf("applying Provider")
namespacedName, err := upsertAlertProvider(ctx, kubeClient, &provider)
if err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Provider reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, name, namespace)); err != nil {
isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
return err
}
logger.Successf("provider %s is ready", name)
logger.Successf("Provider %s is ready", name)
return nil
}
func upsertAlertProvider(ctx context.Context, kubeClient client.Client, alertProvider notificationv1.Provider) error {
func upsertAlertProvider(ctx context.Context, kubeClient client.Client,
provider *notificationv1.Provider) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: alertProvider.GetNamespace(),
Name: alertProvider.GetName(),
Namespace: provider.GetNamespace(),
Name: provider.GetName(),
}
var existing notificationv1.Provider
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &alertProvider); err != nil {
return err
if err := kubeClient.Create(ctx, provider); err != nil {
return namespacedName, err
} else {
logger.Successf("provider created")
return nil
logger.Successf("Provider created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = alertProvider.Labels
existing.Spec = alertProvider.Spec
existing.Labels = provider.Labels
existing.Spec = provider.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("provider updated")
return nil
provider = &existing
logger.Successf("Provider updated")
return namespacedName, nil
}
func isAlertProviderReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isAlertProviderReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, provider *notificationv1.Provider) wait.ConditionFunc {
return func() (bool, error) {
var alertProvider notificationv1.Provider
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alertProvider)
err := kubeClient.Get(ctx, namespacedName, provider)
if err != nil {
return false, err
}
if c := meta.GetCondition(alertProvider.Status.Conditions, meta.ReadyCondition); c != nil {
if c := meta.GetCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil

View File

@@ -19,14 +19,15 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"io/ioutil"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/flags"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -34,7 +35,6 @@ import (
"sigs.k8s.io/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
var createHelmReleaseCmd = &cobra.Command{
@@ -91,7 +91,7 @@ var createHelmReleaseCmd = &cobra.Command{
var (
hrName string
hrSource string
hrSource flags.HelmChartSource
hrDependsOn []string
hrChart string
hrChartVersion string
@@ -100,8 +100,8 @@ var (
)
func init() {
createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<hr-name>'")
createHelmReleaseCmd.Flags().StringVar(&hrSource, "source", "", "source that contains the chart (<kind>/<name>)")
createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
createHelmReleaseCmd.Flags().Var(&hrSource, "source", hrSource.Description())
createHelmReleaseCmd.Flags().StringVar(&hrChart, "chart", "", "Helm chart name or path")
createHelmReleaseCmd.Flags().StringVar(&hrChartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
createHelmReleaseCmd.Flags().StringArrayVar(&hrDependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
@@ -112,21 +112,10 @@ func init() {
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("release name is required")
return fmt.Errorf("HelmRelease name is required")
}
name := args[0]
if hrSource == "" {
return fmt.Errorf("source is required")
}
sourceKind, sourceName := utils.parseObjectKindName(hrSource)
if sourceKind == "" {
return fmt.Errorf("invalid source '%s', must be in format <kind>/<name>", hrSource)
}
if !utils.containsItemString(supportedHelmChartSourceKinds, sourceKind) {
return fmt.Errorf("source kind %s is not supported, can be %v",
sourceKind, supportedHelmChartSourceKinds)
}
if hrChart == "" {
return fmt.Errorf("chart name or path is required")
}
@@ -137,7 +126,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating release")
logger.Generatef("generating HelmRelease")
}
helmRelease := helmv2.HelmRelease{
@@ -148,7 +137,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
},
Spec: helmv2.HelmReleaseSpec{
ReleaseName: hrName,
DependsOn: utils.makeDependsOn(hrDependsOn),
DependsOn: utils.MakeDependsOn(hrDependsOn),
Interval: metav1.Duration{
Duration: interval,
},
@@ -158,8 +147,8 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
Chart: hrChart,
Version: hrChartVersion,
SourceRef: helmv2.CrossNamespaceObjectReference{
Kind: sourceKind,
Name: sourceName,
Kind: hrSource.Kind,
Name: hrSource.Name,
},
},
},
@@ -188,48 +177,30 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying release")
if err := upsertHelmRelease(ctx, kubeClient, helmRelease); err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
chartName := fmt.Sprintf("%s-%s", namespace, name)
if err := wait.PollImmediate(pollInterval, timeout,
isHelmChartReady(ctx, kubeClient, chartName, namespace)); err != nil {
return err
}
if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("release %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
logger.Actionf("applying HelmRelease")
namespacedName, err := upsertHelmRelease(ctx, kubeClient, &helmRelease)
if err != nil {
return fmt.Errorf("release failed: %w", err)
return err
}
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
} else {
return fmt.Errorf("reconciliation failed")
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return err
}
logger.Successf("HelmRelease %s is ready", name)
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
return nil
}
func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmRelease helmv2.HelmRelease) error {
func upsertHelmRelease(ctx context.Context, kubeClient client.Client,
helmRelease *helmv2.HelmRelease) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: helmRelease.GetNamespace(),
Name: helmRelease.GetName(),
@@ -239,75 +210,39 @@ func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmReleas
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRelease); err != nil {
return err
if err := kubeClient.Create(ctx, helmRelease); err != nil {
return namespacedName, err
} else {
logger.Successf("release created")
return nil
logger.Successf("HelmRelease created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = helmRelease.Labels
existing.Spec = helmRelease.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("release updated")
return nil
helmRelease = &existing
logger.Successf("HelmRelease updated")
return namespacedName, nil
}
func isHelmChartReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isHelmReleaseReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
return func() (bool, error) {
var helmChart sourcev1.HelmChart
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmChart)
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}
return false, err
}
if c := meta.GetCondition(helmChart.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}
func isHelmReleaseReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
return func() (bool, error) {
var helmRelease helmv2.HelmRelease
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
err := kubeClient.Get(ctx, namespacedName, helmRelease)
if err != nil {
return false, err
}
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
// Confirm the state we are observing is for the current generation
if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
return false, nil
}
return false, nil
return meta.HasReadyCondition(helmRelease.Status.Conditions), nil
}
}

View File

@@ -33,7 +33,8 @@ import (
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/flags"
"github.com/fluxcd/toolkit/internal/utils"
)
var createKsCmd = &cobra.Command{
@@ -71,7 +72,7 @@ var createKsCmd = &cobra.Command{
}
var (
ksSource string
ksSource flags.KustomizationSource
ksPath string
ksPrune bool
ksDependsOn []string
@@ -80,13 +81,12 @@ var (
ksHealthTimeout time.Duration
ksSAName string
ksSANamespace string
ksDecryptionProvider string
ksDecryptionProvider flags.DecryptionProvider
ksDecryptionSecret string
)
func init() {
createKsCmd.Flags().StringVar(&ksSource, "source", "",
"source that contains the Kubernetes manifests in the format '[<kind>/]<name>', where kind can be GitRepository or Bucket, if kind is not specified it defaults to GitRepository")
createKsCmd.Flags().Var(&ksSource, "source", ksSource.Description())
createKsCmd.Flags().StringVar(&ksPath, "path", "./", "path to the directory containing the Kustomization file")
createKsCmd.Flags().BoolVar(&ksPrune, "prune", false, "enable garbage collection")
createKsCmd.Flags().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
@@ -95,30 +95,17 @@ func init() {
createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'")
createKsCmd.Flags().StringVar(&ksSAName, "sa-name", "", "service account name")
createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace")
createKsCmd.Flags().StringVar(&ksDecryptionProvider, "decryption-provider", "", "enables secrets decryption, provider can be 'sops'")
createKsCmd.Flags().Var(&ksDecryptionProvider, "decryption-provider", ksDecryptionProvider.Description())
createKsCmd.Flags().StringVar(&ksDecryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
createCmd.AddCommand(createKsCmd)
}
func createKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("kustomization name is required")
return fmt.Errorf("Kustomization name is required")
}
name := args[0]
if ksSource == "" {
return fmt.Errorf("source is required")
}
sourceKind, sourceName := utils.parseObjectKindName(ksSource)
if sourceKind == "" {
sourceKind = sourcev1.GitRepositoryKind
}
if !utils.containsItemString(supportedKustomizationSourceKinds, sourceKind) {
return fmt.Errorf("source kind %s is not supported, can be %v",
sourceKind, supportedKustomizationSourceKinds)
}
if ksPath == "" {
return fmt.Errorf("path is required")
}
@@ -127,7 +114,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating kustomization")
logger.Generatef("generating Kustomization")
}
ksLabels, err := parseLabels()
@@ -142,15 +129,15 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
Labels: ksLabels,
},
Spec: kustomizev1.KustomizationSpec{
DependsOn: utils.makeDependsOn(ksDependsOn),
DependsOn: utils.MakeDependsOn(ksDependsOn),
Interval: metav1.Duration{
Duration: interval,
},
Path: ksPath,
Prune: ksPrune,
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourceKind,
Name: sourceName,
Kind: ksSource.Kind,
Name: ksSource.Name,
},
Suspend: false,
Validation: ksValidation,
@@ -206,13 +193,8 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}
if ksDecryptionProvider != "" {
if !utils.containsItemString(supportedDecryptionProviders, ksDecryptionProvider) {
return fmt.Errorf("decryption provider %s is not supported, can be %v",
ksDecryptionProvider, supportedDecryptionProviders)
}
kustomization.Spec.Decryption = &kustomizev1.Decryption{
Provider: ksDecryptionProvider,
Provider: ksDecryptionProvider.String(),
}
if ksDecryptionSecret != "" {
@@ -227,43 +209,30 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying kustomization")
if err := upsertKustomization(ctx, kubeClient, kustomization); err != nil {
logger.Actionf("applying Kustomization")
namespacedName, err := upsertKustomization(ctx, kubeClient, &kustomization)
if err != nil {
return err
}
logger.Waitingf("waiting for kustomization sync")
logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil {
isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err
}
logger.Successf("Kustomization %s is ready", name)
logger.Successf("kustomization %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return fmt.Errorf("kustomization sync failed: %w", err)
}
if kustomization.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
} else {
return fmt.Errorf("kustomization sync failed")
}
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
return nil
}
func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomization kustomizev1.Kustomization) error {
func upsertKustomization(ctx context.Context, kubeClient client.Client,
kustomization *kustomizev1.Kustomization) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: kustomization.GetNamespace(),
Name: kustomization.GetName(),
@@ -273,39 +242,39 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomiz
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &kustomization); err != nil {
return err
if err := kubeClient.Create(ctx, kustomization); err != nil {
return namespacedName, err
} else {
logger.Successf("kustomization created")
return nil
logger.Successf("Kustomization created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = kustomization.Labels
existing.Spec = kustomization.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("kustomization updated")
return nil
kustomization = &existing
logger.Successf("Kustomization updated")
return namespacedName, nil
}
func isKustomizationReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isKustomizationReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
return func() (bool, error) {
var kustomization kustomizev1.Kustomization
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomization)
err := kubeClient.Get(ctx, namespacedName, kustomization)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:

View File

@@ -30,6 +30,7 @@ import (
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
)
var createReceiverCmd = &cobra.Command{
@@ -65,12 +66,12 @@ func init() {
func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("receiver name is required")
return fmt.Errorf("Receiver name is required")
}
name := args[0]
if rcvType == "" {
return fmt.Errorf("type is required")
return fmt.Errorf("Receiver type is required")
}
if rcvSecretRef == "" {
@@ -79,7 +80,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
resources := []notificationv1.CrossNamespaceObjectReference{}
for _, resource := range rcvResources {
kind, name := utils.parseObjectKindName(resource)
kind, name := utils.ParseObjectKindName(resource)
if kind == "" {
return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", resource)
}
@@ -100,7 +101,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
if !export {
logger.Generatef("generating receiver")
logger.Generatef("generating Receiver")
}
receiver := notificationv1.Receiver{
@@ -127,39 +128,30 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying receiver")
if err := upsertReceiver(ctx, kubeClient, receiver); err != nil {
logger.Actionf("applying Receiver")
namespacedName, err := upsertReceiver(ctx, kubeClient, &receiver)
if err != nil {
return err
}
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, name, namespace)); err != nil {
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("receiver %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return fmt.Errorf("receiver sync failed: %w", err)
}
logger.Successf("Receiver %s is ready", name)
logger.Successf("generated webhook URL %s", receiver.Status.URL)
return nil
}
func upsertReceiver(ctx context.Context, kubeClient client.Client, receiver notificationv1.Receiver) error {
func upsertReceiver(ctx context.Context, kubeClient client.Client,
receiver *notificationv1.Receiver) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: receiver.GetNamespace(),
Name: receiver.GetName(),
@@ -169,35 +161,30 @@ func upsertReceiver(ctx context.Context, kubeClient client.Client, receiver noti
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &receiver); err != nil {
return err
if err := kubeClient.Create(ctx, receiver); err != nil {
return namespacedName, err
} else {
logger.Successf("receiver created")
return nil
logger.Successf("Receiver created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = receiver.Labels
existing.Spec = receiver.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("receiver updated")
return nil
receiver = &existing
logger.Successf("Receiver updated")
return namespacedName, nil
}
func isReceiverReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isReceiverReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
return func() (bool, error) {
var receiver notificationv1.Receiver
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &receiver)
err := kubeClient.Get(ctx, namespacedName, receiver)
if err != nil {
return false, err
}

View File

@@ -31,6 +31,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/flags"
"github.com/fluxcd/toolkit/internal/utils"
)
var createSourceBucketCmd = &cobra.Command{
@@ -61,37 +63,33 @@ For Buckets with static authentication, the credentials are stored in a Kubernet
var (
sourceBucketName string
sourceBucketProvider string
sourceBucketProvider = flags.SourceBucketProvider(sourcev1.GenericBucketProvider)
sourceBucketEndpoint string
sourceBucketAccessKey string
sourceBucketSecretKey string
sourceBucketRegion string
sourceBucketInsecure bool
sourceBucketSecretRef string
)
func init() {
createSourceBucketCmd.Flags().StringVar(&sourceBucketProvider, "provider", sourcev1.GenericBucketProvider, "the S3 compatible storage provider name, can be 'generic' or 'aws'")
createSourceBucketCmd.Flags().Var(&sourceBucketProvider, "provider", sourceBucketProvider.Description())
createSourceBucketCmd.Flags().StringVar(&sourceBucketName, "bucket-name", "", "the bucket name")
createSourceBucketCmd.Flags().StringVar(&sourceBucketEndpoint, "endpoint", "", "the bucket endpoint address")
createSourceBucketCmd.Flags().StringVar(&sourceBucketAccessKey, "access-key", "", "the bucket access key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretKey, "secret-key", "", "the bucket secret key")
createSourceBucketCmd.Flags().StringVar(&sourceBucketRegion, "region", "", "the bucket region")
createSourceBucketCmd.Flags().BoolVar(&sourceBucketInsecure, "insecure", false, "for when connecting to a non-TLS S3 HTTP endpoint")
createSourceBucketCmd.Flags().StringVar(&sourceBucketSecretRef, "secret-ref", "", "the name of an existing secret containing credentials")
createSourceCmd.AddCommand(createSourceBucketCmd)
}
func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
return fmt.Errorf("Bucket source name is required")
}
name := args[0]
secretName := fmt.Sprintf("bucket-%s", name)
if !utils.containsItemString(supportedSourceBucketProviders, sourceBucketProvider) {
return fmt.Errorf("bucket provider %s is not supported, can be %v",
sourceBucketProvider, supportedSourceBucketProviders)
}
if sourceBucketName == "" {
return fmt.Errorf("bucket-name is required")
@@ -112,7 +110,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
}
defer os.RemoveAll(tmpDir)
bucket := sourcev1.Bucket{
bucket := &sourcev1.Bucket{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
@@ -120,7 +118,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
},
Spec: sourcev1.BucketSpec{
BucketName: sourceBucketName,
Provider: sourceBucketProvider,
Provider: sourceBucketProvider.String(),
Insecure: sourceBucketInsecure,
Endpoint: sourceBucketEndpoint,
Region: sourceBucketRegion,
@@ -129,77 +127,76 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
},
},
}
if sourceHelmSecretRef != "" {
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceBucketSecretRef,
}
}
if export {
return exportBucket(bucket)
return exportBucket(*bucket)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Generatef("generating source")
logger.Generatef("generating Bucket source")
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{},
}
if sourceBucketSecretRef == "" {
secretName := fmt.Sprintf("bucket-%s", name)
if sourceBucketAccessKey != "" && sourceBucketSecretKey != "" {
secret.StringData["accesskey"] = sourceBucketAccessKey
secret.StringData["secretkey"] = sourceBucketSecretKey
}
if len(secret.StringData) > 0 {
logger.Actionf("applying secret with the bucket credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{},
}
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
if sourceBucketAccessKey != "" && sourceBucketSecretKey != "" {
secret.StringData["accesskey"] = sourceBucketAccessKey
secret.StringData["secretkey"] = sourceBucketSecretKey
}
if len(secret.StringData) > 0 {
logger.Actionf("applying secret with the bucket credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
bucket.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
}
logger.Successf("authentication configured")
}
logger.Successf("authentication configured")
}
logger.Actionf("applying source")
if err := upsertBucket(ctx, kubeClient, bucket); err != nil {
logger.Actionf("applying Bucket source")
namespacedName, err := upsertBucket(ctx, kubeClient, bucket)
if err != nil {
return err
}
logger.Waitingf("waiting for download")
logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isBucketReady(ctx, kubeClient, name, namespace)); err != nil {
isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
return err
}
logger.Successf("Bucket source reconciliation completed")
logger.Successf("download completed")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
if bucket.Status.Artifact == nil {
return fmt.Errorf("Bucket source reconciliation but no artifact was found")
}
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return fmt.Errorf("could not retrieve bucket: %w", err)
}
if bucket.Status.Artifact != nil {
logger.Successf("fetched revision: %s", bucket.Status.Artifact.Revision)
} else {
return fmt.Errorf("download failed, artifact not found")
}
logger.Successf("fetched revision: %s", bucket.Status.Artifact.Revision)
return nil
}
func upsertBucket(ctx context.Context, kubeClient client.Client, bucket sourcev1.Bucket) error {
func upsertBucket(ctx context.Context, kubeClient client.Client,
bucket *sourcev1.Bucket) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: bucket.GetNamespace(),
Name: bucket.GetName(),
@@ -209,22 +206,22 @@ func upsertBucket(ctx context.Context, kubeClient client.Client, bucket sourcev1
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &bucket); err != nil {
return err
if err := kubeClient.Create(ctx, bucket); err != nil {
return namespacedName, err
} else {
logger.Successf("source created")
return nil
logger.Successf("Bucket source created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = bucket.Labels
existing.Spec = bucket.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("source updated")
return nil
bucket = &existing
logger.Successf("Bucket source updated")
return namespacedName, nil
}

View File

@@ -20,12 +20,15 @@ import (
"context"
"crypto/elliptic"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"io/ioutil"
"net/url"
"os"
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/flags"
"github.com/fluxcd/toolkit/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
@@ -84,15 +87,17 @@ For private Git repositories, the basic authentication credentials are stored in
}
var (
sourceGitURL string
sourceGitBranch string
sourceGitTag string
sourceGitSemver string
sourceGitUsername string
sourceGitPassword string
sourceGitKeyAlgorithm PublicKeyAlgorithm = "rsa"
sourceGitRSABits RSAKeyBits = 2048
sourceGitECDSACurve = ECDSACurve{elliptic.P384()}
sourceGitURL string
sourceGitBranch string
sourceGitTag string
sourceGitSemver string
sourceGitUsername string
sourceGitPassword string
sourceGitKeyAlgorithm flags.PublicKeyAlgorithm = "rsa"
sourceGitRSABits flags.RSAKeyBits = 2048
sourceGitECDSACurve = flags.ECDSACurve{Curve: elliptic.P384()}
sourceGitSecretRef string
)
func init() {
@@ -105,13 +110,14 @@ func init() {
createSourceGitCmd.Flags().Var(&sourceGitKeyAlgorithm, "ssh-key-algorithm", sourceGitKeyAlgorithm.Description())
createSourceGitCmd.Flags().Var(&sourceGitRSABits, "ssh-rsa-bits", sourceGitRSABits.Description())
createSourceGitCmd.Flags().Var(&sourceGitECDSACurve, "ssh-ecdsa-curve", sourceGitECDSACurve.Description())
createSourceGitCmd.Flags().StringVarP(&sourceGitSecretRef, "secret-ref", "", "", "the name of an existing secret containing SSH or basic credentials")
createSourceCmd.AddCommand(createSourceGitCmd)
}
func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
return fmt.Errorf("GitRepository source name is required")
}
name := args[0]
@@ -159,20 +165,27 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
}
if export {
if sourceGitSecretRef != "" {
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceGitSecretRef,
}
}
return exportGit(gitRepository)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
withAuth := false
// TODO(hidde): move all auth prep to separate func?
if u.Scheme == "ssh" {
if sourceGitSecretRef != "" {
withAuth = true
} else if u.Scheme == "ssh" {
logger.Actionf("generating deploy key pair")
pair, err := generateKeyPair(ctx)
if err != nil {
@@ -234,42 +247,35 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("authentication configured")
}
logger.Generatef("generating source")
logger.Generatef("generating GitRepository source")
if withAuth {
secretName := name
if sourceGitSecretRef != "" {
secretName = sourceGitSecretRef
}
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: name,
Name: secretName,
}
}
logger.Actionf("applying source")
if err := upsertGitRepository(ctx, kubeClient, gitRepository); err != nil {
return err
}
logger.Waitingf("waiting for git sync")
if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
return err
}
logger.Successf("git sync completed")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
logger.Actionf("applying GitRepository source")
namespacedName, err := upsertGitRepository(ctx, kubeClient, &gitRepository)
if err != nil {
return fmt.Errorf("git sync failed: %w", err)
return err
}
if gitRepository.Status.Artifact != nil {
logger.Successf("fetched revision: %s", gitRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("git sync failed, artifact not found")
logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
return err
}
logger.Successf("GitRepository source reconciliation completed")
if gitRepository.Status.Artifact == nil {
return fmt.Errorf("GitRepository source reconciliation completed but no artifact was found")
}
logger.Successf("fetched revision: %s", gitRepository.Status.Artifact.Revision)
return nil
}
@@ -330,7 +336,8 @@ func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.S
return nil
}
func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepository sourcev1.GitRepository) error {
func upsertGitRepository(ctx context.Context, kubeClient client.Client,
gitRepository *sourcev1.GitRepository) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: gitRepository.GetNamespace(),
Name: gitRepository.GetName(),
@@ -340,35 +347,30 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepos
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &gitRepository); err != nil {
return err
if err := kubeClient.Create(ctx, gitRepository); err != nil {
return namespacedName, err
} else {
logger.Successf("source created")
return nil
logger.Successf("GitRepository source created")
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = gitRepository.Labels
existing.Spec = gitRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
logger.Successf("source updated")
return nil
gitRepository = &existing
logger.Successf("GitRepository source updated")
return namespacedName, nil
}
func isGitRepositoryReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isGitRepositoryReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, gitRepository *sourcev1.GitRepository) wait.ConditionFunc {
return func() (bool, error) {
var gitRepository sourcev1.GitRepository
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &gitRepository)
err := kubeClient.Get(ctx, namespacedName, gitRepository)
if err != nil {
return false, err
}

View File

@@ -32,6 +32,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var createSourceHelmCmd = &cobra.Command{
@@ -62,12 +63,13 @@ For private Helm repositories, the basic authentication credentials are stored i
}
var (
sourceHelmURL string
sourceHelmUsername string
sourceHelmPassword string
sourceHelmCertFile string
sourceHelmKeyFile string
sourceHelmCAFile string
sourceHelmURL string
sourceHelmUsername string
sourceHelmPassword string
sourceHelmCertFile string
sourceHelmKeyFile string
sourceHelmCAFile string
sourceHelmSecretRef string
)
func init() {
@@ -77,16 +79,16 @@ func init() {
createSourceHelmCmd.Flags().StringVar(&sourceHelmCertFile, "cert-file", "", "TLS authentication cert file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmKeyFile, "key-file", "", "TLS authentication key file path")
createSourceHelmCmd.Flags().StringVar(&sourceHelmCAFile, "ca-file", "", "TLS authentication CA file path")
createSourceHelmCmd.Flags().StringVarP(&sourceHelmSecretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
createSourceCmd.AddCommand(createSourceHelmCmd)
}
func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
return fmt.Errorf("HelmRepository source name is required")
}
name := args[0]
secretName := fmt.Sprintf("helm-%s", name)
if sourceHelmURL == "" {
return fmt.Errorf("url is required")
@@ -107,7 +109,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("url parse failed: %w", err)
}
helmRepository := sourcev1.HelmRepository{
helmRepository := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
@@ -121,98 +123,97 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
},
}
if sourceHelmSecretRef != "" {
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: sourceHelmSecretRef,
}
}
if export {
return exportHelmRepository(helmRepository)
return exportHelmRepository(*helmRepository)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
logger.Generatef("generating source")
logger.Generatef("generating HelmRepository source")
if sourceHelmSecretRef == "" {
secretName := fmt.Sprintf("helm-%s", name)
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{},
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{},
}
if sourceHelmUsername != "" && sourceHelmPassword != "" {
secret.StringData["username"] = sourceHelmUsername
secret.StringData["password"] = sourceHelmPassword
}
if sourceHelmCertFile != "" && sourceHelmKeyFile != "" {
cert, err := ioutil.ReadFile(sourceHelmCertFile)
if err != nil {
return fmt.Errorf("failed to read repository cert file '%s': %w", sourceHelmCertFile, err)
}
secret.StringData["certFile"] = string(cert)
key, err := ioutil.ReadFile(sourceHelmKeyFile)
if err != nil {
return fmt.Errorf("failed to read repository key file '%s': %w", sourceHelmKeyFile, err)
}
secret.StringData["keyFile"] = string(key)
}
if sourceHelmCAFile != "" {
ca, err := ioutil.ReadFile(sourceHelmCAFile)
if err != nil {
return fmt.Errorf("failed to read repository CA file '%s': %w", sourceHelmCAFile, err)
}
secret.StringData["caFile"] = string(ca)
}
if len(secret.StringData) > 0 {
logger.Actionf("applying secret with repository credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
}
logger.Successf("authentication configured")
}
}
if sourceHelmUsername != "" && sourceHelmPassword != "" {
secret.StringData["username"] = sourceHelmUsername
secret.StringData["password"] = sourceHelmPassword
}
if sourceHelmCertFile != "" && sourceHelmKeyFile != "" {
cert, err := ioutil.ReadFile(sourceHelmCertFile)
if err != nil {
return fmt.Errorf("failed to read repository cert file '%s': %w", sourceHelmCertFile, err)
}
secret.StringData["certFile"] = string(cert)
key, err := ioutil.ReadFile(sourceHelmKeyFile)
if err != nil {
return fmt.Errorf("failed to read repository key file '%s': %w", sourceHelmKeyFile, err)
}
secret.StringData["keyFile"] = string(key)
}
if sourceHelmCAFile != "" {
ca, err := ioutil.ReadFile(sourceHelmCAFile)
if err != nil {
return fmt.Errorf("failed to read repository CA file '%s': %w", sourceHelmCAFile, err)
}
secret.StringData["caFile"] = string(ca)
}
if len(secret.StringData) > 0 {
logger.Actionf("applying secret with repository credentials")
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
return err
}
helmRepository.Spec.SecretRef = &corev1.LocalObjectReference{
Name: secretName,
}
logger.Successf("authentication configured")
}
logger.Actionf("applying source")
if err := upsertHelmRepository(ctx, kubeClient, helmRepository); err != nil {
logger.Actionf("applying HelmRepository source")
namespacedName, err := upsertHelmRepository(ctx, kubeClient, helmRepository)
if err != nil {
return err
}
logger.Waitingf("waiting for index download")
logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
return err
}
logger.Successf("HelmRepository source reconciliation completed")
logger.Successf("index download completed")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
if helmRepository.Status.Artifact == nil {
return fmt.Errorf("HelmRepository source reconciliation completed but no artifact was found")
}
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
return fmt.Errorf("helm index failed: %w", err)
}
if helmRepository.Status.Artifact != nil {
logger.Successf("fetched revision: %s", helmRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("index download failed, artifact not found")
}
logger.Successf("fetched revision: %s", helmRepository.Status.Artifact.Revision)
return nil
}
func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRepository sourcev1.HelmRepository) error {
func upsertHelmRepository(ctx context.Context, kubeClient client.Client,
helmRepository *sourcev1.HelmRepository) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{
Namespace: helmRepository.GetNamespace(),
Name: helmRepository.GetName(),
@@ -222,22 +223,22 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRep
err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil {
if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRepository); err != nil {
return err
if err := kubeClient.Create(ctx, helmRepository); err != nil {
return namespacedName, err
} else {
logger.Successf("source created")
return nil
return namespacedName, nil
}
}
return err
return namespacedName, err
}
existing.Labels = helmRepository.Labels
existing.Spec = helmRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil {
return err
return namespacedName, err
}
helmRepository = &existing
logger.Successf("source updated")
return nil
return namespacedName, nil
}

View File

@@ -21,6 +21,7 @@ import (
"context"
"fmt"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
@@ -144,7 +145,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/types"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var deleteAlertCmd = &cobra.Command{
@@ -50,7 +51,7 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/types"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var deleteAlertProviderCmd = &cobra.Command{
@@ -50,7 +51,7 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/types"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var deleteHelmReleaseCmd = &cobra.Command{
@@ -51,7 +52,7 @@ func deleteHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -21,6 +21,7 @@ import (
"fmt"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
@@ -50,7 +51,7 @@ func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/types"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var deleteReceiverCmd = &cobra.Command{
@@ -50,7 +51,7 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -21,6 +21,7 @@ import (
"fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
@@ -49,7 +50,7 @@ func deleteSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -21,6 +21,7 @@ import (
"fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
@@ -49,7 +50,7 @@ func deleteSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -21,6 +21,7 @@ import (
"fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
@@ -49,7 +50,7 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/yaml"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var exportAlertCmd = &cobra.Command{
@@ -54,7 +55,7 @@ func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/yaml"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var exportAlertProviderCmd = &cobra.Command{
@@ -54,7 +55,7 @@ func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var exportHelmReleaseCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/yaml"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var exportKsCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -27,6 +27,7 @@ import (
"sigs.k8s.io/yaml"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var exportReceiverCmd = &cobra.Command{
@@ -54,7 +55,7 @@ func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -28,6 +28,7 @@ import (
"sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var exportSourceBucketCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -28,6 +28,7 @@ import (
"sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var exportSourceGitCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -28,6 +28,7 @@ import (
"sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var exportSourceHelmCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -28,6 +28,7 @@ import (
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
)
var getAlertCmd = &cobra.Command{
@@ -48,7 +49,7 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -97,6 +98,6 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -26,6 +26,7 @@ import (
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
)
var getAlertProviderCmd = &cobra.Command{
@@ -46,7 +47,7 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -91,6 +92,6 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -23,6 +23,7 @@ import (
"strings"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
@@ -50,7 +51,7 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -99,6 +100,6 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -23,6 +23,7 @@ import (
"strings"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/spf13/cobra"
@@ -49,7 +50,7 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -98,6 +99,6 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -28,6 +28,7 @@ import (
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
)
var getReceiverCmd = &cobra.Command{
@@ -48,7 +49,7 @@ func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -92,6 +93,6 @@ func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -21,6 +21,7 @@ import (
"os"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
@@ -46,7 +47,7 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -97,6 +98,6 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -21,6 +21,7 @@ import (
"os"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
@@ -46,7 +47,7 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -97,6 +98,6 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -21,6 +21,7 @@ import (
"os"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
@@ -46,7 +47,7 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -97,6 +98,6 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
}
rows = append(rows, row)
}
utils.printTable(os.Stdout, header, rows)
utils.PrintTable(os.Stdout, header, rows)
return nil
}

View File

@@ -26,6 +26,8 @@ import (
"github.com/spf13/cobra"
"github.com/fluxcd/toolkit/internal/flags"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/fluxcd/toolkit/pkg/install"
)
@@ -57,10 +59,10 @@ var (
installComponents []string
installRegistry string
installImagePullSecret string
installArch string
installArch flags.Arch = "amd64"
installWatchAllNamespaces bool
installNetworkPolicy bool
installLogLevel string
installLogLevel flags.LogLevel = "info"
)
func init() {
@@ -78,25 +80,16 @@ func init() {
"container registry where the toolkit images are published")
installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "",
"Kubernetes secret name used for pulling the toolkit images from a private registry")
installCmd.Flags().StringVar(&installArch, "arch", "amd64",
"arch can be amd64 or arm64")
installCmd.Flags().Var(&installArch, "arch", installArch.Description())
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", true,
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
installCmd.Flags().StringVar(&installLogLevel, "log-level", "info", "set the controllers log level")
installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description())
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", true,
"deny ingress access to the toolkit controllers from other namespaces using network policies")
rootCmd.AddCommand(installCmd)
}
func installCmdRun(cmd *cobra.Command, args []string) error {
if !utils.containsItemString(supportedArch, installArch) {
return fmt.Errorf("arch %s is not supported, can be %v", installArch, supportedArch)
}
if !utils.containsItemString(supportedLogLevels, installLogLevel) {
return fmt.Errorf("log level %s is not supported, can be %v", bootstrapLogLevel, installLogLevel)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
@@ -117,10 +110,10 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
Components: installComponents,
Registry: installRegistry,
ImagePullSecret: installImagePullSecret,
Arch: installArch,
Arch: installArch.String(),
WatchAllNamespaces: installWatchAllNamespaces,
NetworkPolicy: installNetworkPolicy,
LogLevel: installLogLevel,
LogLevel: installLogLevel.String(),
NotificationController: defaultNotification,
ManifestsFile: fmt.Sprintf("%s.yaml", namespace),
Timeout: timeout,
@@ -154,18 +147,17 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("manifests build completed")
logger.Actionf("installing components in %s namespace", namespace)
applyOutput := ModeStderrOS
applyOutput := utils.ModeStderrOS
if verbose {
applyOutput = ModeOS
}
dryRun := ""
if installDryRun {
dryRun = "--dry-run=client"
applyOutput = ModeOS
applyOutput = utils.ModeOS
}
command := fmt.Sprintf("kubectl apply -f %s %s", manifest, dryRun)
if _, err := utils.execCommand(ctx, applyOutput, command); err != nil {
kubectlArgs := []string{"apply", "-f", manifest}
if installDryRun {
args = append(args, "--dry-run=client")
applyOutput = utils.ModeOS
}
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
}
@@ -178,9 +170,8 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("verifying installation")
for _, deployment := range installComponents {
command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
namespace, deployment, timeout.String())
if _, err := utils.execCommand(ctx, applyOutput, command); err != nil {
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil {
return fmt.Errorf("install failed")
} else {
logger.Successf("%s ready", deployment)

View File

@@ -26,8 +26,6 @@ import (
"github.com/spf13/cobra/doc"
_ "k8s.io/client-go/plugin/pkg/client/auth"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
gotklog "github.com/fluxcd/toolkit/pkg/log"
)
@@ -100,22 +98,15 @@ var (
namespace string
timeout time.Duration
verbose bool
utils Utils
pollInterval = 2 * time.Second
logger gotklog.Logger = printLogger{}
)
var (
defaultComponents = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
defaultVersion = "latest"
defaultNamespace = "gotk-system"
defaultNotification = "notification-controller"
supportedLogLevels = []string{"debug", "info", "error"}
supportedArch = []string{"amd64", "arm", "arm64"}
supportedDecryptionProviders = []string{"sops"}
supportedKustomizationSourceKinds = []string{sourcev1.GitRepositoryKind, sourcev1.BucketKind}
supportedHelmChartSourceKinds = []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind, sourcev1.BucketKind}
supportedSourceBucketProviders = []string{sourcev1.GenericBucketProvider, sourcev1.AmazonBucketProvider}
defaultComponents = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
defaultVersion = "latest"
defaultNamespace = "gotk-system"
defaultNotification = "notification-controller"
)
func init() {

View File

@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -45,14 +47,14 @@ func init() {
func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("alert name is required")
return fmt.Errorf("Alert name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -62,7 +64,7 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
Name: name,
}
logger.Actionf("annotating alert %s in %s namespace", name, namespace)
logger.Actionf("annotating Alert %s in %s namespace", name, namespace)
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
@@ -79,15 +81,13 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("alert annotated")
logger.Successf("Alert annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, name, namespace)); err != nil {
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("alert reconciliation completed")
logger.Successf("Alert reconciliation completed")
return nil
}

View File

@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -45,14 +47,14 @@ func init() {
func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("provider name is required")
return fmt.Errorf("Provider name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -62,7 +64,7 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
Name: name,
}
logger.Actionf("annotating provider %s in %s namespace", name, namespace)
logger.Actionf("annotating Provider %s in %s namespace", name, namespace)
var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil {
@@ -79,15 +81,13 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &alertProvider); err != nil {
return err
}
logger.Successf("provider annotated")
logger.Successf("Provider annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, name, namespace)); err != nil {
isAlertProviderReady(ctx, kubeClient, namespacedName, &alertProvider)); err != nil {
return err
}
logger.Successf("provider reconciliation completed")
logger.Successf("Provider reconciliation completed")
return nil
}

View File

@@ -29,6 +29,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
@@ -68,7 +69,7 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -98,19 +99,19 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
}
}
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt
logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace)
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName); err != nil {
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
return err
}
logger.Successf("HelmRelease annotated")
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
helmReleaseReconciliationHandled(ctx, kubeClient, name, namespace, helmRelease.Status.LastHandledReconcileAt),
helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
); err != nil {
return err
}
logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
@@ -120,7 +121,7 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionFalse:
return fmt.Errorf("HelmRelease reconciliation failed")
return fmt.Errorf("HelmRelease reconciliation failed: %s", c.Message)
default:
logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision)
}
@@ -129,39 +130,29 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
}
func helmReleaseReconciliationHandled(ctx context.Context, kubeClient client.Client,
name, namespace, lastHandledReconcileAt string) wait.ConditionFunc {
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) {
var helmRelease helmv2.HelmRelease
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
err := kubeClient.Get(ctx, namespacedName, helmRelease)
if err != nil {
return false, err
}
return helmRelease.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestHelmReleaseReconciliation(ctx context.Context, kubeClient client.Client, namespacedName types.NamespacedName) error {
var release helmv2.HelmRelease
func requestHelmReleaseReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, &release); err != nil {
if err := kubeClient.Get(ctx, namespacedName, helmRelease); err != nil {
return err
}
if release.Annotations == nil {
release.Annotations = map[string]string{
if helmRelease.Annotations == nil {
helmRelease.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
release.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
helmRelease.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
err = kubeClient.Update(ctx, &release)
return
return kubeClient.Update(ctx, helmRelease)
})
}

View File

@@ -26,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -54,21 +55,21 @@ var (
)
func init() {
reconcileKsCmd.Flags().BoolVar(&syncKsWithSource, "with-source", false, "reconcile kustomization source")
reconcileKsCmd.Flags().BoolVar(&syncKsWithSource, "with-source", false, "reconcile Kustomization source")
reconcileCmd.AddCommand(reconcileKsCmd)
}
func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("kustomization name is required")
return fmt.Errorf("Kustomization name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -77,7 +78,6 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
Namespace: namespace,
Name: name,
}
var kustomization kustomizev1.Kustomization
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
@@ -96,30 +96,26 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
}
}
logger.Actionf("annotating kustomization %s in %s namespace", name, namespace)
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName); err != nil {
lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
logger.Actionf("annotating Kustomization %s in %s namespace", name, namespace)
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
return err
}
logger.Successf("kustomization annotated")
logger.Successf("Kustomization annotated")
logger.Waitingf("waiting for kustomization reconciliation")
logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(
pollInterval, timeout,
kustomizeReconciliationHandled(ctx, kubeClient, name, namespace, kustomization.Status.LastHandledReconcileAt),
kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
); err != nil {
return err
}
logger.Successf("Kustomization reconciliation completed")
logger.Successf("kustomization reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionFalse:
return fmt.Errorf("kustomization reconciliation failed")
return fmt.Errorf("Kustomization reconciliation failed")
default:
logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision)
}
@@ -128,30 +124,22 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
}
func kustomizeReconciliationHandled(ctx context.Context, kubeClient client.Client,
name, namespace, lastHandledReconcileAt string) wait.ConditionFunc {
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) {
var kustomize kustomizev1.Kustomization
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomize)
err := kubeClient.Get(ctx, namespacedName, kustomization)
if err != nil {
return false, err
}
return kustomize.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
return kustomization.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Client, namespacedName types.NamespacedName) error {
var kustomization kustomizev1.Kustomization
func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, &kustomization); err != nil {
if err := kubeClient.Get(ctx, namespacedName, kustomization); err != nil {
return err
}
if kustomization.Annotations == nil {
kustomization.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
@@ -159,8 +147,6 @@ func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Clien
} else {
kustomization.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
}
err = kubeClient.Update(ctx, &kustomization)
return
return kubeClient.Update(ctx, kustomization)
})
}

View File

@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -52,7 +54,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -62,7 +64,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
Name: name,
}
logger.Actionf("annotating receiver %s in %s namespace", name, namespace)
logger.Actionf("annotating Receiver %s in %s namespace", name, namespace)
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
@@ -79,15 +81,15 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("receiver annotated")
logger.Successf("Receiver annotated")
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, name, namespace)); err != nil {
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("receiver reconciliation completed")
logger.Successf("Receiver reconciliation completed")
return nil
}

View File

@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
@@ -54,7 +56,7 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -64,7 +66,7 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
Name: name,
}
logger.Actionf("annotating source %s in %s namespace", name, namespace)
logger.Actionf("annotating Bucket source %s in %s namespace", name, namespace)
var bucket sourcev1.Bucket
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
@@ -81,42 +83,35 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &bucket); err != nil {
return err
}
logger.Successf("source annotated")
logger.Successf("Bucket source annotated")
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isBucketReady(ctx, kubeClient, name, namespace)); err != nil {
isBucketReady(ctx, kubeClient, namespacedName, &bucket)); err != nil {
return err
}
logger.Successf("Bucket source reconciliation completed")
logger.Successf("bucket reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return err
}
if bucket.Status.Artifact != nil {
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
} else {
return fmt.Errorf("bucket reconciliation failed, artifact not found")
if bucket.Status.Artifact == nil {
return fmt.Errorf("Bucket source reconciliation completed but no artifact was found")
}
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
return nil
}
func isBucketReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isBucketReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
return func() (bool, error) {
var bucket sourcev1.Bucket
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &bucket)
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 := meta.GetCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:

View File

@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
@@ -52,7 +54,7 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -62,7 +64,7 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
Name: name,
}
logger.Actionf("annotating source %s in %s namespace", name, namespace)
logger.Actionf("annotating GitRepository source %s in %s namespace", name, namespace)
var gitRepository sourcev1.GitRepository
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil {
@@ -79,25 +81,18 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &gitRepository); err != nil {
return err
}
logger.Successf("source annotated")
logger.Successf("GitRepository source annotated")
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
return err
}
logger.Successf("GitRepository source reconciliation completed")
logger.Successf("git reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil {
return err
}
if gitRepository.Status.Artifact != nil {
logger.Successf("fetched revision %s", gitRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("git reconciliation failed, artifact not found")
if gitRepository.Status.Artifact == nil {
return fmt.Errorf("GitRepository source reconciliation completed but no artifact was found")
}
logger.Successf("fetched revision %s", gitRepository.Status.Artifact.Revision)
return nil
}

View File

@@ -19,9 +19,11 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
@@ -47,14 +49,14 @@ func init() {
func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("source name is required")
return fmt.Errorf("HelmRepository source name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -64,7 +66,7 @@ func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
Name: name,
}
logger.Actionf("annotating source %s in %s namespace", name, namespace)
logger.Actionf("annotating HelmRepository source %s in %s namespace", name, namespace)
var helmRepository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
@@ -81,42 +83,35 @@ func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &helmRepository); err != nil {
return err
}
logger.Successf("source annotated")
logger.Successf("HelmRepository source annotated")
logger.Waitingf("waiting for reconciliation")
logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryReady(ctx, kubeClient, name, namespace)); err != nil {
isHelmRepositoryReady(ctx, kubeClient, namespacedName, &helmRepository)); err != nil {
return err
}
logger.Successf("HelmRepository source reconciliation completed")
logger.Successf("helm reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
return err
}
if helmRepository.Status.Artifact != nil {
logger.Successf("fetched revision %s", helmRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("helm reconciliation failed, artifact not found")
if helmRepository.Status.Artifact == nil {
return fmt.Errorf("HelmRepository source reconciliation completed but no artifact was found")
}
logger.Successf("fetched revision %s", helmRepository.Status.Artifact.Revision)
return nil
}
func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRepository *sourcev1.HelmRepository) wait.ConditionFunc {
return func() (bool, error) {
var helmRepository sourcev1.HelmRepository
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRepository)
err := kubeClient.Get(ctx, namespacedName, helmRepository)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if helmRepository.Generation != helmRepository.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(helmRepository.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:

View File

@@ -19,7 +19,9 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
@@ -54,7 +56,7 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -78,24 +80,17 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isAlertResumed(ctx, kubeClient, name, namespace)); err != nil {
isAlertResumed(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("Alert reconciliation completed")
return nil
}
func isAlertResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isAlertResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
return func() (bool, error) {
var alert notificationv1.Alert
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alert)
err := kubeClient.Get(ctx, namespacedName, alert)
if err != nil {
return false, err
}

View File

@@ -19,7 +19,9 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
@@ -55,7 +57,7 @@ func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -79,36 +81,25 @@ func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseResumed(ctx, kubeClient, name, namespace)); err != nil {
isHelmReleaseResumed(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return err
}
logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
} else {
return fmt.Errorf("HelmRelease reconciliation failed")
}
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
return nil
}
func isHelmReleaseResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isHelmReleaseResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
return func() (bool, error) {
var helmRelease helmv2.HelmRelease
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
err := kubeClient.Get(ctx, namespacedName, helmRelease)
if err != nil {
return false, err
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
// Confirm the state we are observing is for the current generation
if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
return false, err
}

View File

@@ -19,7 +19,9 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/spf13/cobra"
@@ -47,14 +49,14 @@ func init() {
func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("kustomization name is required")
return fmt.Errorf("Kustomization name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -69,48 +71,37 @@ func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
return err
}
logger.Actionf("resuming kustomization %s in %s namespace", name, namespace)
logger.Actionf("resuming Kustomization %s in %s namespace", name, namespace)
kustomization.Spec.Suspend = false
if err := kubeClient.Update(ctx, &kustomization); err != nil {
return err
}
logger.Successf("kustomization resumed")
logger.Successf("Kustomization resumed")
logger.Waitingf("waiting for kustomization sync")
logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationResumed(ctx, kubeClient, name, namespace)); err != nil {
isKustomizationResumed(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err
}
logger.Successf("Kustomization reconciliation completed")
logger.Successf("kustomization sync completed")
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if kustomization.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
} else {
return fmt.Errorf("kustomization sync failed")
}
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
return nil
}
func isKustomizationResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isKustomizationResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
return func() (bool, error) {
var kustomization kustomizev1.Kustomization
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomization)
err := kubeClient.Get(ctx, namespacedName, kustomization)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:

View File

@@ -19,7 +19,9 @@ package main
import (
"context"
"fmt"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
@@ -54,7 +56,7 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
@@ -78,24 +80,18 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout,
isReceiverResumed(ctx, kubeClient, name, namespace)); err != nil {
isReceiverResumed(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("Receiver reconciliation completed")
return nil
}
func isReceiverResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
func isReceiverResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
return func() (bool, error) {
var receiver notificationv1.Receiver
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &receiver)
err := kubeClient.Get(ctx, namespacedName, receiver)
if err != nil {
return false, err
}

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/types"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var suspendAlertCmd = &cobra.Command{
@@ -49,7 +50,7 @@ func suspendAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/types"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var suspendHrCmd = &cobra.Command{
@@ -50,7 +51,7 @@ func suspendHrCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -19,7 +19,9 @@ package main
import (
"context"
"fmt"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
)
@@ -48,7 +50,7 @@ func suspendKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/types"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var suspendReceiverCmd = &cobra.Command{
@@ -49,7 +50,7 @@ func suspendReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}

View File

@@ -27,6 +27,7 @@ import (
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var uninstallCmd = &cobra.Command{
@@ -66,15 +67,13 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
kubeClient, err := utils.KubeClient(kubeconfig)
if err != nil {
return err
}
dryRun := ""
if uninstallDryRun {
dryRun = "--dry-run=server"
} else if !uninstallSilent {
dryRun := "--dry-run=server"
if !uninstallDryRun && !uninstallSilent {
prompt := promptui.Prompt{
Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", namespace),
IsConfirm: true,
@@ -105,9 +104,15 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
sourcev1.HelmRepositoryKind,
helmv2.HelmReleaseKind,
} {
command := fmt.Sprintf("kubectl -n %s delete %s --all --ignore-not-found --timeout=%s %s",
namespace, kind, timeout.String(), dryRun)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
kubectlArgs := []string{
"-n", namespace,
"delete", kind, "--all", "--ignore-not-found",
"--timeout", timeout.String(),
}
if uninstallDryRun {
kubectlArgs = append(kubectlArgs, dryRun)
}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
return fmt.Errorf("uninstall failed: %w", err)
}
}
@@ -123,9 +128,15 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
logger.Actionf("uninstalling components")
for _, kind := range kinds {
command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --ignore-not-found --timeout=%s %s",
kind, namespace, timeout.String(), dryRun)
if _, err := utils.execCommand(ctx, ModeOS, command); err != nil {
kubectlArgs := []string{
"delete", kind,
"-l", fmt.Sprintf("app.kubernetes.io/instance=%s", namespace),
"--ignore-not-found", "--timeout", timeout.String(),
}
if uninstallDryRun {
kubectlArgs = append(kubectlArgs, dryRun)
}
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
return fmt.Errorf("uninstall failed: %w", err)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

View File

@@ -9,12 +9,12 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
### Options
```
--arch string arch can be amd64 or arm64 (default "amd64")
--arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
-h, --help help for bootstrap
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--log-level string set the controllers log level (default "info")
--log-level logLevel log level, available options are: (debug, info, error) (default info)
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
-v, --version string toolkit version (default "latest")

View File

@@ -57,12 +57,12 @@ gotk bootstrap github [flags]
### Options inherited from parent commands
```
--arch string arch can be amd64 or arm64 (default "amd64")
--arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--log-level string set the controllers log level (default "info")
--log-level logLevel log level, available options are: (debug, info, error) (default info)
-n, --namespace string the namespace scope for this operation (default "gotk-system")
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")

View File

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

View File

@@ -66,8 +66,8 @@ gotk create helmrelease [name] [flags]
--chart-version string Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)
--depends-on stringArray HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'
-h, --help help for helmrelease
--release-name string name used for the Helm release, defaults to a composition of '[<target-namespace>-]<hr-name>'
--source string source that contains the chart (<kind>/<name>)
--release-name string name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'
--source helmChartSource source that contains the chart in the format '<kind>/<name>',where kind can be one of: (HelmRepository, GitRepository, Bucket)
--target-namespace string namespace to install this release, defaults to the HelmRelease namespace
--values string local path to the values.yaml file
```

View File

@@ -44,18 +44,18 @@ gotk create kustomization [name] [flags]
### Options
```
--decryption-provider string enables secrets decryption, provider can be 'sops'
--decryption-secret string set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption
--depends-on stringArray Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'
--health-check stringArray workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'
--health-check-timeout duration timeout of health checking operations (default 2m0s)
-h, --help help for kustomization
--path string path to the directory containing the Kustomization file (default "./")
--prune enable garbage collection
--sa-name string service account name
--sa-namespace string service account namespace
--source string source that contains the Kubernetes manifests in the format '[<kind>/]<name>', where kind can be GitRepository or Bucket, if kind is not specified it defaults to GitRepository
--validation string validate the manifests before applying them on the cluster, can be 'client' or 'server'
--decryption-provider decryptionProvider decryption provider, available options are: (sops)
--decryption-secret string set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption
--depends-on stringArray Kustomization that must be ready before this Kustomization can be applied, supported formats '<name>' and '<namespace>/<name>'
--health-check stringArray workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'
--health-check-timeout duration timeout of health checking operations (default 2m0s)
-h, --help help for kustomization
--path string path to the directory containing the Kustomization file (default "./")
--prune enable garbage collection
--sa-name string service account name
--sa-namespace string service account namespace
--source kustomizationSource source that contains the Kubernetes manifests in the format '[<kind>/]<name>',where kind can be one of: (GitRepository, Bucket), if kind is not specified it defaults to GitRepository
--validation string validate the manifests before applying them on the cluster, can be 'client' or 'server'
```
### Options inherited from parent commands

View File

@@ -37,14 +37,15 @@ gotk create source bucket [name] [flags]
### Options
```
--access-key string the bucket access key
--bucket-name string the bucket name
--endpoint string the bucket endpoint address
-h, --help help for bucket
--insecure for when connecting to a non-TLS S3 HTTP endpoint
--provider string the S3 compatible storage provider name, can be 'generic' or 'aws' (default "generic")
--region string the bucket region
--secret-key string the bucket secret key
--access-key string the bucket access key
--bucket-name string the bucket name
--endpoint string the bucket endpoint address
-h, --help help for bucket
--insecure for when connecting to a non-TLS S3 HTTP endpoint
--provider sourceBucketProvider the S3 compatible storage provider name, available options are: (generic, aws) (default generic)
--region string the bucket region
--secret-key string the bucket secret key
--secret-ref string the name of an existing secret containing credentials
```
### Options inherited from parent commands

View File

@@ -58,6 +58,7 @@ gotk create source git [name] [flags]
--branch string git branch (default "master")
-h, --help help for git
-p, --password string basic authentication password
--secret-ref string the name of an existing secret containing SSH or basic credentials
--ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p256, p384, p521) (default p384)
--ssh-key-algorithm publicKeyAlgorithm SSH public key algorithm (rsa, ecdsa, ed25519) (default rsa)
--ssh-rsa-bits rsaKeyBits SSH RSA public key bit size (multiplies of 8) (default 2048)

View File

@@ -38,13 +38,14 @@ gotk create source helm [name] [flags]
### Options
```
--ca-file string TLS authentication CA file path
--cert-file string TLS authentication cert file path
-h, --help help for helm
--key-file string TLS authentication key file path
-p, --password string basic authentication password
--url string Helm repository address
-u, --username string basic authentication username
--ca-file string TLS authentication CA file path
--cert-file string TLS authentication cert file path
-h, --help help for helm
--key-file string TLS authentication key file path
-p, --password string basic authentication password
--secret-ref string the name of an existing secret containing TLS or basic auth credentials
--url string Helm repository address
-u, --username string basic authentication username
```
### Options inherited from parent commands

View File

@@ -31,13 +31,13 @@ gotk install [flags]
### Options
```
--arch string arch can be amd64 or arm64 (default "amd64")
--arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--dry-run only print the object that would be applied
--export write the install manifests to stdout and exit
-h, --help help for install
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--log-level string set the controllers log level (default "info")
--log-level logLevel log level, available options are: (debug, info, error) (default info)
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
-v, --version string toolkit version (default "latest")

View File

@@ -26,7 +26,7 @@ gotk reconcile kustomization [name] [flags]
```
-h, --help help for kustomization
--with-source reconcile kustomization source
--with-source reconcile Kustomization source
```
### Options inherited from parent commands

View File

@@ -24,4 +24,4 @@ Features:
Links:
- Source code [fluxcd/helm-controller](https://github.com/fluxcd/helm-controller)
- Specification [docs](https://github.com/fluxcd/helm-controller/tree/master/docs/spec)
- Specification [docs](https://github.com/fluxcd/helm-controller/tree/main/docs/spec)

View File

@@ -20,4 +20,4 @@ Features:
Links:
- Source code [fluxcd/kustomize-controller](https://github.com/fluxcd/kustomize-controller)
- Specification [docs](https://github.com/fluxcd/kustomize-controller/tree/master/docs/spec)
- Specification [docs](https://github.com/fluxcd/kustomize-controller/tree/main/docs/spec)

View File

@@ -14,4 +14,4 @@ based on event severity and involved objects.
Links:
- Source code [fluxcd/notification-controller](https://github.com/fluxcd/notification-controller)
- Specification [docs](https://github.com/fluxcd/notification-controller/tree/master/docs/spec)
- Specification [docs](https://github.com/fluxcd/notification-controller/tree/main/docs/spec)

View File

@@ -21,4 +21,4 @@ Features:
Links:
- Source code [fluxcd/source-controller](https://github.com/fluxcd/source-controller)
- Specification [docs](https://github.com/fluxcd/source-controller/tree/master/docs/spec)
- Specification [docs](https://github.com/fluxcd/source-controller/tree/main/docs/spec)

View File

@@ -34,7 +34,8 @@ curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
```
The install script downloads the gotk binary to `/usr/local/bin`.
Binaries for macOS and Linux AMD64/ARM are available for download on the
Binaries for **macOS**, **Windows** and **Linux** AMD64/ARM are available for download on the
[release page](https://github.com/fluxcd/toolkit/releases).
To configure your shell to load gotk completions add to your Bash profile:

View File

@@ -29,7 +29,7 @@ curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
Command-line completion for `zsh`, `fish`, and `powershell`
are also supported with their own sub-commands.
Binaries for macOS and Linux AMD64/ARM are available for download on the
Binaries for macOS, Windows and Linux AMD64/ARM are available for download on the
[release page](https://github.com/fluxcd/toolkit/releases).
Verify that your cluster satisfies the prerequisites with:
@@ -154,6 +154,22 @@ gotk bootstrap gitlab \
--personal
```
To run the bootstrap for a repository using deploy keys for authentication, you have to specify the SSH hostname:
```sh
gotk bootstrap gitlab \
--ssh-hostname=gitlab.com \
--owner=my-gitlab-username \
--repository=my-repository \
--branch=master \
--path=my-cluster
```
!!! hint "Authentication"
When providing the `--ssh-hostname`, a read-only (SSH) deploy key will be added
to your repository, otherwise your GitLab personal token will be used to
authenticate against the HTTPS endpoint instead.
Run the bootstrap for a repository owned by a GitLab group:
```sh
@@ -393,12 +409,17 @@ kubectl -n gotk-system port-forward svc/grafana 3000:3000
```
Navigate to [http://localhost:3000/d/gitops-toolkit-control-plane](http://localhost:3000/d/gitops-toolkit-control-plane/gitops-toolkit-control-plane)
for the control plane dashboards:
for the control plane dashboard:
![](../_files/cp-dashboard-p1.png)
![](../_files/cp-dashboard-p2.png)
Navigate to [http://localhost:3000/d/gitops-toolkit-cluster](http://localhost:3000/d/gitops-toolkit-cluster/gitops-toolkit-cluster-stats)
for the cluster reconciliation stats dashboard:
![](../_files/cluster-dashboard.png)
If you wish to use your own Prometheus and Grafana instances, then you can import the dashboards from
[GitHub](https://github.com/fluxcd/toolkit/tree/main/manifests/monitoring/grafana/dashboards).

View File

@@ -65,4 +65,14 @@ The GitOps Toolkit is always looking for new contributors and there are a multit
- And if you are completely new to the GitOps Toolkit, take a look at our [Get Started guide](get-started/index.md) and give us feedback
- Check out [how to contribute](contributing/index.md) to the project
## Featured Talks
- 12 Oct 2020 - [Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
- 4 Sep 2020 - [KubeCon/CloudNativeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
- 25 June 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
- 7 May 2020 - [GitOps Days - Community Special: GitOps Toolkit Experimentation with Stefan Prodan](https://youtu.be/WHzxunv4DKk?t=6521)
### Upcoming Meetups
- 19 Oct 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 1](https://www.meetup.com/GitOps-Community/events/273640196/)
- 2 Nov 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 2](https://www.meetup.com/GitOps-Community/events/273934676/)
We are looking forward to seeing you with us!

View File

@@ -43,7 +43,7 @@ Tasks
### Flux image update feature parity
[= 0% "0%"]
[= 30% "30%"]
Goals
@@ -55,9 +55,9 @@ Non-Goals
Tasks
- [ ] [Design the image scanning and automation API](https://github.com/fluxcd/toolkit/discussions/107)
- [x] <span style="color:grey">[Design the image scanning and automation API](https://github.com/fluxcd/toolkit/discussions/107)</span>
- [ ] Implement an image scanning controller
- [ ] Design the automation component
- [x] <span style="color:grey">Design the automation component</span>
- [ ] Implement the image scan/patch/push workflow
- [ ] Integrate the new components in the toolkit assembler
- [ ] Create a migration guide from Flux annotations

6
go.mod
View File

@@ -4,9 +4,9 @@ go 1.15
require (
github.com/blang/semver/v4 v4.0.0
github.com/fluxcd/helm-controller/api v0.1.2
github.com/fluxcd/kustomize-controller/api v0.1.1
github.com/fluxcd/notification-controller/api v0.1.1
github.com/fluxcd/helm-controller/api v0.1.3
github.com/fluxcd/kustomize-controller/api v0.1.2
github.com/fluxcd/notification-controller/api v0.1.2
github.com/fluxcd/pkg/apis/meta v0.0.2
github.com/fluxcd/pkg/git v0.0.7
github.com/fluxcd/pkg/runtime v0.1.0

12
go.sum
View File

@@ -111,12 +111,12 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fluxcd/helm-controller/api v0.1.2 h1:gBky+nMpDaUT8mhLSaRkHEWczOvLR/JT6L5iRhu4CIs=
github.com/fluxcd/helm-controller/api v0.1.2/go.mod h1:eMkEzQrgDnOFa/iUey4VVjdqmPJFwcWb+3SFPDX9lJ0=
github.com/fluxcd/kustomize-controller/api v0.1.1 h1:hg9koO2YD5VLetwT/Xsaq4MWJ5uXKdjKhx9xDFOJxmo=
github.com/fluxcd/kustomize-controller/api v0.1.1/go.mod h1:84YzQnJ2DShfIE842HYHqB48i0vhpZMJ9XQsdgOEkfM=
github.com/fluxcd/notification-controller/api v0.1.1 h1:tu6+bi28vfHoSp2MUD9h42SIvqY+YtEwS9toH9k7cRA=
github.com/fluxcd/notification-controller/api v0.1.1/go.mod h1:w1gILYTSqt3dFMYRmCihA/K84yDBfIkL5m5dcbaUyUY=
github.com/fluxcd/helm-controller/api v0.1.3 h1:OztoSyxj5+2P38FRc9JXqnXP+f4eNQ0j199PRIqhnR4=
github.com/fluxcd/helm-controller/api v0.1.3/go.mod h1:eMkEzQrgDnOFa/iUey4VVjdqmPJFwcWb+3SFPDX9lJ0=
github.com/fluxcd/kustomize-controller/api v0.1.2 h1:gocCnhlqTjzZy7DbTdA2QezFMdudfIXv5RRAP4D5lfc=
github.com/fluxcd/kustomize-controller/api v0.1.2/go.mod h1:MztOqUKfKn/CBzRofBMq/DAOjzQSoDTmFdIKR32BEQg=
github.com/fluxcd/notification-controller/api v0.1.2 h1:rxgjIWK19cr7cvbWKn68jPQJN+4SweiUss/IaNWA4DM=
github.com/fluxcd/notification-controller/api v0.1.2/go.mod h1:w1gILYTSqt3dFMYRmCihA/K84yDBfIkL5m5dcbaUyUY=
github.com/fluxcd/pkg/apis/meta v0.0.2 h1:kyA4Y0IzNjf1joBOnFqpWG7aNDHvtLExZcaHQM7qhRI=
github.com/fluxcd/pkg/apis/meta v0.0.2/go.mod h1:nCNps5JJOcEQr3MNDmZqI4o0chjePSUYL6Q2ktDtotU=
github.com/fluxcd/pkg/git v0.0.7 h1:tFSYPy7tcIYfOt8H5EUERXIRz7fk0id302oQZde1NtU=

54
internal/flags/arch.go Normal file
View File

@@ -0,0 +1,54 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"strings"
"github.com/fluxcd/toolkit/internal/utils"
)
var supportedArchs = []string{"amd64", "arm", "arm64"}
type Arch string
func (a *Arch) String() string {
return string(*a)
}
func (a *Arch) Set(str string) error {
if strings.TrimSpace(str) == "" {
return fmt.Errorf("no arch given, must be one of: %s",
strings.Join(supportedArchs, ", "))
}
if !utils.ContainsItemString(supportedArchs, str) {
return fmt.Errorf("unsupported arch '%s', must be one of: %s",
str, strings.Join(supportedArchs, ", "))
}
*a = Arch(str)
return nil
}
func (a *Arch) Type() string {
return "arch"
}
func (a *Arch) Description() string {
return fmt.Sprintf("cluster architecture, available options are: (%s)", strings.Join(supportedArchs, ", "))
}

View File

@@ -0,0 +1,50 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"strings"
"github.com/fluxcd/toolkit/internal/utils"
)
var supportedDecryptionProviders = []string{"sops"}
type DecryptionProvider string
func (d *DecryptionProvider) String() string {
return string(*d)
}
func (d *DecryptionProvider) Set(str string) error {
if !utils.ContainsItemString(supportedDecryptionProviders, str) {
return fmt.Errorf("unsupported decryption provider '%s', must be one of: %s",
str, strings.Join(supportedDecryptionProviders, ", "))
}
*d = DecryptionProvider(str)
return nil
}
func (d *DecryptionProvider) Type() string {
return "decryptionProvider"
}
func (d *DecryptionProvider) Description() string {
return fmt.Sprintf("decryption provider, available options are: (%s)", strings.Join(supportedDecryptionProviders, ", "))
}

View File

@@ -14,79 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package main
package flags
import (
"crypto/elliptic"
"fmt"
"sort"
"strconv"
"strings"
)
var supportedPublicKeyAlgorithms = []string{"rsa", "ecdsa", "ed25519"}
type PublicKeyAlgorithm string
func (a *PublicKeyAlgorithm) String() string {
return string(*a)
}
func (a *PublicKeyAlgorithm) Set(str string) error {
if strings.TrimSpace(str) == "" {
return fmt.Errorf("no public key algorithm given, must be one of: %s",
strings.Join(supportedPublicKeyAlgorithms, ", "))
}
for _, v := range supportedPublicKeyAlgorithms {
if str == v {
*a = PublicKeyAlgorithm(str)
return nil
}
}
return fmt.Errorf("unsupported public key algorithm '%s', must be one of: %s",
str, strings.Join(supportedPublicKeyAlgorithms, ", "))
}
func (a *PublicKeyAlgorithm) Type() string {
return "publicKeyAlgorithm"
}
func (a *PublicKeyAlgorithm) Description() string {
return fmt.Sprintf("SSH public key algorithm (%s)", strings.Join(supportedPublicKeyAlgorithms, ", "))
}
var defaultRSAKeyBits = 2048
type RSAKeyBits int
func (b *RSAKeyBits) String() string {
return strconv.Itoa(int(*b))
}
func (b *RSAKeyBits) Set(str string) error {
if strings.TrimSpace(str) == "" {
*b = RSAKeyBits(defaultRSAKeyBits)
return nil
}
bits, err := strconv.Atoi(str)
if err != nil {
return err
}
if bits%8 != 0 {
return fmt.Errorf("RSA key bit size should be a multiples of 8")
}
*b = RSAKeyBits(bits)
return nil
}
func (b *RSAKeyBits) Type() string {
return "rsaKeyBits"
}
func (b *RSAKeyBits) Description() string {
return "SSH RSA public key bit size (multiplies of 8)"
}
type ECDSACurve struct {
elliptic.Curve
}

View File

@@ -0,0 +1,72 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"strings"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var supportedHelmChartSourceKinds = []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind, sourcev1.BucketKind}
type HelmChartSource struct {
Kind string
Name string
}
func (h *HelmChartSource) String() string {
if h.Name == "" {
return ""
}
return fmt.Sprintf("%s/%s", h.Kind, h.Name)
}
func (h *HelmChartSource) Set(str string) error {
if strings.TrimSpace(str) == "" {
return fmt.Errorf("no helm chart source given, please specify %s",
h.Description())
}
sourceKind, sourceName := utils.ParseObjectKindName(str)
if sourceKind == "" {
return fmt.Errorf("invalid helm chart source '%s', must be in format <kind>/<name>", str)
}
if !utils.ContainsItemString(supportedHelmChartSourceKinds, sourceKind) {
return fmt.Errorf("source kind '%s' is not supported, can be one of: %s",
sourceKind, strings.Join(supportedHelmChartSourceKinds, ", "))
}
h.Name = sourceName
h.Kind = sourceKind
return nil
}
func (h *HelmChartSource) Type() string {
return "helmChartSource"
}
func (h *HelmChartSource) Description() string {
return fmt.Sprintf(
"source that contains the chart in the format '<kind>/<name>',"+
"where kind can be one of: (%s)",
strings.Join(supportedHelmChartSourceKinds, ", "),
)
}

View File

@@ -0,0 +1,72 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"strings"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var supportedKustomizationSourceKinds = []string{sourcev1.GitRepositoryKind, sourcev1.BucketKind}
type KustomizationSource struct {
Kind string
Name string
}
func (k *KustomizationSource) String() string {
if k.Name == "" {
return ""
}
return fmt.Sprintf("%s/%s", k.Kind, k.Name)
}
func (k *KustomizationSource) Set(str string) error {
if strings.TrimSpace(str) == "" {
return fmt.Errorf("no kustomization source given, please specify %s",
k.Description())
}
sourceKind, sourceName := utils.ParseObjectKindName(str)
if sourceKind == "" {
sourceKind = sourcev1.GitRepositoryKind
}
if !utils.ContainsItemString(supportedKustomizationSourceKinds, sourceKind) {
return fmt.Errorf("source kind '%s' is not supported, can be one of: %s",
sourceKind, strings.Join(supportedKustomizationSourceKinds, ", "))
}
k.Name = sourceName
k.Kind = sourceKind
return nil
}
func (k *KustomizationSource) Type() string {
return "kustomizationSource"
}
func (k *KustomizationSource) Description() string {
return fmt.Sprintf(
"source that contains the Kubernetes manifests in the format '[<kind>/]<name>',"+
"where kind can be one of: (%s), if kind is not specified it defaults to GitRepository",
strings.Join(supportedKustomizationSourceKinds, ", "),
)
}

View File

@@ -0,0 +1,54 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"strings"
"github.com/fluxcd/toolkit/internal/utils"
)
var supportedLogLevels = []string{"debug", "info", "error"}
type LogLevel string
func (l *LogLevel) String() string {
return string(*l)
}
func (l *LogLevel) Set(str string) error {
if strings.TrimSpace(str) == "" {
return fmt.Errorf("no log level given, must be one of: %s",
strings.Join(supportedLogLevels, ", "))
}
if !utils.ContainsItemString(supportedLogLevels, str) {
return fmt.Errorf("unsupported log level '%s', must be one of: %s",
str, strings.Join(supportedLogLevels, ", "))
}
*l = LogLevel(str)
return nil
}
func (l *LogLevel) Type() string {
return "logLevel"
}
func (l *LogLevel) Description() string {
return fmt.Sprintf("log level, available options are: (%s)", strings.Join(supportedLogLevels, ", "))
}

View File

@@ -0,0 +1,53 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"strings"
)
var supportedPublicKeyAlgorithms = []string{"rsa", "ecdsa", "ed25519"}
type PublicKeyAlgorithm string
func (a *PublicKeyAlgorithm) String() string {
return string(*a)
}
func (a *PublicKeyAlgorithm) Set(str string) error {
if strings.TrimSpace(str) == "" {
return fmt.Errorf("no public key algorithm given, must be one of: %s",
strings.Join(supportedPublicKeyAlgorithms, ", "))
}
for _, v := range supportedPublicKeyAlgorithms {
if str == v {
*a = PublicKeyAlgorithm(str)
return nil
}
}
return fmt.Errorf("unsupported public key algorithm '%s', must be one of: %s",
str, strings.Join(supportedPublicKeyAlgorithms, ", "))
}
func (a *PublicKeyAlgorithm) Type() string {
return "publicKeyAlgorithm"
}
func (a *PublicKeyAlgorithm) Description() string {
return fmt.Sprintf("SSH public key algorithm (%s)", strings.Join(supportedPublicKeyAlgorithms, ", "))
}

View File

@@ -0,0 +1,55 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"strconv"
"strings"
)
var defaultRSAKeyBits = 2048
type RSAKeyBits int
func (b *RSAKeyBits) String() string {
return strconv.Itoa(int(*b))
}
func (b *RSAKeyBits) Set(str string) error {
if strings.TrimSpace(str) == "" {
*b = RSAKeyBits(defaultRSAKeyBits)
return nil
}
bits, err := strconv.Atoi(str)
if err != nil {
return err
}
if bits%8 != 0 {
return fmt.Errorf("RSA key bit size should be a multiples of 8")
}
*b = RSAKeyBits(bits)
return nil
}
func (b *RSAKeyBits) Type() string {
return "rsaKeyBits"
}
func (b *RSAKeyBits) Description() string {
return "SSH RSA public key bit size (multiplies of 8)"
}

View File

@@ -0,0 +1,58 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"strings"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/toolkit/internal/utils"
)
var supportedSourceBucketProviders = []string{sourcev1.GenericBucketProvider, sourcev1.AmazonBucketProvider}
type SourceBucketProvider string
func (s *SourceBucketProvider) String() string {
return string(*s)
}
func (s *SourceBucketProvider) Set(str string) error {
if strings.TrimSpace(str) == "" {
return fmt.Errorf("no source bucket provider given, please specify %s",
s.Description())
}
if !utils.ContainsItemString(supportedSourceBucketProviders, str) {
return fmt.Errorf("source bucket provider '%s' is not supported, can be one of: %v",
str, strings.Join(supportedSourceBucketProviders, ", "))
}
return nil
}
func (s *SourceBucketProvider) Type() string {
return "sourceBucketProvider"
}
func (s *SourceBucketProvider) Description() string {
return fmt.Sprintf(
"the S3 compatible storage provider name, available options are: (%s)",
strings.Join(supportedSourceBucketProviders, ", "),
)
}

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package main
package utils
import (
"bufio"
@@ -60,9 +60,10 @@ const (
ModeCapture ExecMode = "capture.stderr|stdout"
)
func (*Utils) execCommand(ctx context.Context, mode ExecMode, command string) (string, error) {
func ExecKubectlCommand(ctx context.Context, mode ExecMode, args ...string) (string, error) {
var stdoutBuf, stderrBuf bytes.Buffer
c := exec.CommandContext(ctx, "/bin/sh", "-c", command)
c := exec.CommandContext(ctx, "kubectl", args...)
if mode == ModeStderrOS {
c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
@@ -93,7 +94,7 @@ func (*Utils) execCommand(ctx context.Context, mode ExecMode, command string) (s
return "", nil
}
func (*Utils) execTemplate(obj interface{}, tmpl, filename string) error {
func ExecTemplate(obj interface{}, tmpl, filename string) error {
t, err := template.New("tmpl").Parse(tmpl)
if err != nil {
return err
@@ -123,8 +124,8 @@ func (*Utils) execTemplate(obj interface{}, tmpl, filename string) error {
return file.Sync()
}
func (*Utils) kubeClient(kubeConfigPath string) (client.Client, error) {
configFiles := utils.splitKubeConfigPath(kubeConfigPath)
func KubeClient(kubeConfigPath string) (client.Client, error) {
configFiles := SplitKubeConfigPath(kubeConfigPath)
cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{Precedence: configFiles},
&clientcmd.ConfigOverrides{}).ClientConfig()
@@ -150,11 +151,11 @@ func (*Utils) kubeClient(kubeConfigPath string) (client.Client, error) {
return kubeClient, nil
}
// splitKubeConfigPath splits the given KUBECONFIG path based on the runtime OS
// SplitKubeConfigPath splits the given KUBECONFIG path based on the runtime OS
// target.
//
// Ref: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#the-kubeconfig-environment-variable
func (*Utils) splitKubeConfigPath(path string) []string {
func SplitKubeConfigPath(path string) []string {
var sep string
switch runtime.GOOS {
case "windows":
@@ -165,7 +166,7 @@ func (*Utils) splitKubeConfigPath(path string) []string {
return strings.Split(path, sep)
}
func (*Utils) writeFile(content, filename string) error {
func WriteFile(content, filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
@@ -180,7 +181,7 @@ func (*Utils) writeFile(content, filename string) error {
return file.Sync()
}
func (*Utils) copyFile(src, dst string) error {
func CopyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
@@ -200,7 +201,7 @@ func (*Utils) copyFile(src, dst string) error {
return out.Close()
}
func (*Utils) containsItemString(s []string, e string) bool {
func ContainsItemString(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
@@ -209,7 +210,7 @@ func (*Utils) containsItemString(s []string, e string) bool {
return false
}
func (*Utils) parseObjectKindName(input string) (string, string) {
func ParseObjectKindName(input string) (string, string) {
kind := ""
name := input
parts := strings.Split(input, "/")
@@ -220,7 +221,7 @@ func (*Utils) parseObjectKindName(input string) (string, string) {
return kind, name
}
func (*Utils) makeDependsOn(deps []string) []dependency.CrossNamespaceDependencyReference {
func MakeDependsOn(deps []string) []dependency.CrossNamespaceDependencyReference {
refs := []dependency.CrossNamespaceDependencyReference{}
for _, dep := range deps {
parts := strings.Split(dep, "/")
@@ -240,9 +241,9 @@ func (*Utils) makeDependsOn(deps []string) []dependency.CrossNamespaceDependency
return refs
}
// generateKustomizationYaml is the equivalent of running
// GenerateKustomizationYaml is the equivalent of running
// 'kustomize create --autodetect' in the specified dir
func (*Utils) generateKustomizationYaml(dirPath string) error {
func GenerateKustomizationYaml(dirPath string) error {
fs := filesys.MakeFsOnDisk()
kfile := filepath.Join(dirPath, "kustomization.yaml")
@@ -320,7 +321,7 @@ func (*Utils) generateKustomizationYaml(dirPath string) error {
return nil
}
func (*Utils) printTable(writer io.Writer, header []string, rows [][]string) {
func PrintTable(writer io.Writer, header []string, rows [][]string) {
table := tablewriter.NewWriter(writer)
table.SetHeader(header)
table.SetAutoWrapText(false)

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/helm-controller/archive/v0.1.2.zip//helm-controller-0.1.2/config/crd
- https://github.com/fluxcd/helm-controller/archive/v0.1.2.zip//helm-controller-0.1.2/config/manager
- https://github.com/fluxcd/helm-controller/archive/v0.1.3.zip//helm-controller-0.1.3/config/crd
- https://github.com/fluxcd/helm-controller/archive/v0.1.3.zip//helm-controller-0.1.3/config/manager
patchesJson6902:
- target:
group: apps

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/kustomize-controller/archive/v0.1.1.zip//kustomize-controller-0.1.1/config/crd
- https://github.com/fluxcd/kustomize-controller/archive/v0.1.1.zip//kustomize-controller-0.1.1/config/manager
- https://github.com/fluxcd/kustomize-controller/archive/v0.1.2.zip//kustomize-controller-0.1.2/config/crd
- https://github.com/fluxcd/kustomize-controller/archive/v0.1.2.zip//kustomize-controller-0.1.2/config/manager
patchesJson6902:
- target:
group: apps

View File

@@ -1,5 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/notification-controller/archive/v0.1.1.zip//notification-controller-0.1.1/config/crd
- https://github.com/fluxcd/notification-controller/archive/v0.1.1.zip//notification-controller-0.1.1/config/manager
- https://github.com/fluxcd/notification-controller/archive/v0.1.2.zip//notification-controller-0.1.2/config/crd
- https://github.com/fluxcd/notification-controller/archive/v0.1.2.zip//notification-controller-0.1.2/config/manager

View File

@@ -0,0 +1,905 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"iteration": 1602679512025,
"links": [],
"panels": [
{
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"decimals": 0,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "red",
"value": 100
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 0
},
"id": 24,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "7.2.1",
"targets": [
{
"expr": "count(gotk_reconcile_condition{namespace=~\"$namespace\",type=\"Ready\",status=\"True\",kind=~\"Kustomization|HelmRelease\"})\n-\nsum(gotk_reconcile_condition{namespace=~\"$namespace\",type=\"Ready\",status=\"Deleted\",kind=~\"Kustomization|HelmRelease\"})",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Cluster Reconcilers",
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"decimals": 0,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 6,
"y": 0
},
"id": 28,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "7.2.1",
"targets": [
{
"expr": "sum(gotk_reconcile_condition{namespace=~\"$namespace\",type=\"Ready\",status=\"False\",kind=~\"Kustomization|HelmRelease\"})",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Failing Reconcilers",
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"decimals": 0,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "red",
"value": 100
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 12,
"y": 0
},
"id": 29,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "7.2.1",
"targets": [
{
"expr": "count(gotk_reconcile_condition{namespace=~\"$namespace\",type=\"Ready\",status=\"True\",kind=~\"GitRepository|HelmRepository|Bucket\"})\n-\nsum(gotk_reconcile_condition{namespace=~\"$namespace\",type=\"Ready\",status=\"Deleted\",kind=~\"GitRepository|HelmRepository|Bucket\"})",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Kubernetes Manifests Sources",
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"decimals": 0,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 18,
"y": 0
},
"id": 30,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "7.2.1",
"targets": [
{
"expr": "sum(gotk_reconcile_condition{namespace=~\"$namespace\",type=\"Ready\",status=\"False\",kind=~\"GitRepository|HelmRepository|Bucket\"})",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Failing Sources",
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "#EAB839",
"value": 1
},
{
"color": "red",
"value": 61
}
]
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 12,
"x": 0,
"y": 5
},
"id": 8,
"options": {
"displayMode": "gradient",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
},
"showUnfilled": true
},
"pluginVersion": "7.2.1",
"targets": [
{
"expr": " sum(rate(gotk_reconcile_duration_sum{namespace=~\"$namespace\",kind=~\"Kustomization|HelmRelease\"}[5m])) by (kind)\n/\n sum(rate(gotk_reconcile_duration_count{namespace=~\"$namespace\",kind=~\"Kustomization|HelmRelease\"}[5m])) by (kind)",
"interval": "",
"legendFormat": "{{kind}}",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Reconciler ops avg. duration",
"type": "bargauge"
},
{
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "#EAB839",
"value": 1
},
{
"color": "red",
"value": 61
}
]
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 12,
"x": 12,
"y": 5
},
"id": 31,
"options": {
"displayMode": "gradient",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
},
"showUnfilled": true
},
"pluginVersion": "7.2.1",
"targets": [
{
"expr": " sum(rate(gotk_reconcile_duration_sum{namespace=~\"$namespace\",kind=~\"GitRepository|HelmRepository|Bucket\"}[5m])) by (kind)\n/\n sum(rate(gotk_reconcile_duration_count{namespace=~\"$namespace\",kind=~\"GitRepository|HelmRepository|Bucket\"}[5m])) by (kind)",
"interval": "",
"legendFormat": "{{kind}}",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Source ops avg. duration",
"type": "bargauge"
},
{
"collapsed": false,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 9
},
"id": 15,
"panels": [],
"title": "Status",
"type": "row"
},
{
"datasource": null,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {
"align": null,
"filterable": true
},
"mappings": [
{
"from": "",
"id": 1,
"text": "Ready",
"to": "",
"type": 1,
"value": "0"
},
{
"from": "",
"id": 2,
"text": "Not Ready",
"to": "",
"type": 1,
"value": "1"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "blue",
"value": 0
},
{
"color": "red",
"value": 1
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Status"
},
"properties": [
{
"id": "custom.displayMode",
"value": "color-background"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 10
},
"id": 33,
"options": {
"showHeader": true
},
"pluginVersion": "7.2.1",
"targets": [
{
"expr": "gotk_reconcile_condition{namespace=~\"$namespace\",type=\"Ready\",status=\"False\",kind=~\"Kustomization|HelmRelease\"}",
"format": "table",
"instant": true,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Cluster reconciliation readiness ",
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"__name__": true,
"app": true,
"instance": true,
"job": true,
"kubernetes_namespace": true,
"kubernetes_pod_name": true,
"namespace": true,
"pod_template_hash": true,
"status": true,
"type": true
},
"indexByName": {},
"renameByName": {
"Value": "Status",
"kind": "Kind",
"name": "Name"
}
}
}
],
"type": "table"
},
{
"datasource": null,
"description": "",
"fieldConfig": {
"defaults": {
"custom": {
"align": null,
"filterable": true
},
"mappings": [
{
"from": "",
"id": 1,
"text": "Ready",
"to": "",
"type": 1,
"value": "0"
},
{
"from": "",
"id": 2,
"text": "Not Ready",
"to": "",
"type": 1,
"value": "1"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "blue",
"value": 0
},
{
"color": "red",
"value": 1
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Status"
},
"properties": [
{
"id": "custom.displayMode",
"value": "color-background"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 10
},
"id": 34,
"options": {
"showHeader": true
},
"pluginVersion": "7.2.1",
"targets": [
{
"expr": "gotk_reconcile_condition{namespace=~\"$namespace\",type=\"Ready\",status=\"False\",kind=~\"GitRepository|HelmRepository|Bucket\"}",
"format": "table",
"instant": true,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Source acquisition readiness ",
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"__name__": true,
"app": true,
"instance": true,
"job": true,
"kubernetes_namespace": true,
"kubernetes_pod_name": true,
"namespace": true,
"pod_template_hash": true,
"status": true,
"type": true
},
"indexByName": {},
"renameByName": {
"Value": "Status",
"kind": "Kind",
"name": "Name"
}
}
}
],
"type": "table"
},
{
"collapsed": false,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 18
},
"id": 17,
"panels": [],
"title": "Timing",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 19
},
"hiddenSeries": false,
"id": 27,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"hideEmpty": true,
"hideZero": true,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.2.1",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": " sum(rate(gotk_reconcile_duration_sum{namespace=~\"$namespace\",kind=~\"Kustomization|HelmRelease\"}[5m])) by (kind, name)\n/\n sum(rate(gotk_reconcile_duration_count{namespace=~\"$namespace\",kind=~\"Kustomization|HelmRelease\"}[5m])) by (kind, name)",
"hide": false,
"interval": "",
"legendFormat": "{{kind}}/{{name}}",
"refId": "B"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Cluster reconciliation duration",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 27
},
"hiddenSeries": false,
"id": 35,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"hideEmpty": true,
"hideZero": true,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.2.1",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": " sum(rate(gotk_reconcile_duration_sum{namespace=~\"$namespace\",kind=~\"GitRepository|HelmRepository|Bucket\"}[5m])) by (kind, name)\n/\n sum(rate(gotk_reconcile_duration_count{namespace=~\"$namespace\",kind=~\"GitRepository|HelmRepository|Bucket\"}[5m])) by (kind, name)",
"hide": false,
"interval": "",
"legendFormat": "{{kind}}/{{name}}",
"refId": "B"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Source acquisition duration",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": "10s",
"schemaVersion": 26,
"style": "light",
"tags": [
"gitops-toolkit"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "prometheus",
"value": "prometheus"
},
"hide": 2,
"includeAll": false,
"label": null,
"multi": false,
"name": "DS_PROMETHEUS",
"options": [],
"query": "prometheus",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
},
{
"allValue": ".*",
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": "$DS_PROMETHEUS",
"definition": "gotk_reconcile_condition",
"hide": 0,
"includeAll": true,
"label": null,
"multi": false,
"name": "namespace",
"options": [],
"query": "gotk_reconcile_condition",
"refresh": 2,
"regex": "/.*namespace=\"([^\"]*).*/",
"skipUrlSync": false,
"sort": 5,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "GitOps Toolkit Cluster Stats",
"uid": "gitops-toolkit-cluster",
"version": 1
}

View File

@@ -10,4 +10,5 @@ configMapGenerator:
- name: grafana-dashboards
files:
- dashboards/control-plane.json
- dashboards/cluster.json