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

Compare commits

..

31 Commits

Author SHA1 Message Date
Stefan Prodan
288d952686 Merge pull request #231 from fluxcd/kc-v0.0.12
Update kustomize-controller to v0.0.12
2020-09-14 18:34:56 +03:00
stefanprodan
c36e02bba9 Update kustomize-controller to v0.0.12 2020-09-14 18:20:05 +03:00
Stefan Prodan
c121a4c0f5 Merge pull request #229 from fluxcd/log-level
Add log level flag
2020-09-14 12:56:06 +03:00
stefanprodan
2bac5aabee Add log level flag 2020-09-14 12:35:24 +03:00
Stefan Prodan
97ff225bc0 Merge pull request #227 from fluxcd/watch-all-namespaces
Add watch all namespaces flag
2020-09-14 12:23:14 +03:00
stefanprodan
d5e78b9f80 Add watch all namespaces flag 2020-09-13 10:38:15 +03:00
Stefan Prodan
3f98affd5a Merge pull request #223 from fluxcd/update-components
Update toolkit components
2020-09-12 12:11:32 +03:00
stefanprodan
531c2bcf00 Import fluxcd/pkg/runtime 2020-09-12 12:04:37 +03:00
fluxcdbot
dd5505918a Update toolkit components 2020-09-12 08:54:22 +00:00
Stefan Prodan
4a30a69eb4 Merge pull request #226 from fluxcd/bot-pr
Use fluxcdbot token in CI
2020-09-12 11:51:31 +03:00
stefanprodan
38b302e5a5 Use fluxcdbot token in CI 2020-09-12 11:44:41 +03:00
Hidde Beydals
ea010895a0 docs: update Helm roadmap 2020-09-11 15:31:15 +02:00
Stefan Prodan
7b88512698 Merge pull request #222 from fluxcd/rbac-patch-events
Add PATCH rule to crd-controller role for events
2020-09-11 09:21:50 +03:00
Hidde Beydals
1ff24d9285 Add PATCH rule to crd-controller role for events
During high custom resource count / low interval tests, I was greated
with a `cannot patch resource "events"` message. This happened due to
event compaction, where it will perform a patch instead of a create.
By giving the role the permission to do so this should no longer pose
a problem.
2020-09-10 20:57:59 +02:00
Stefan Prodan
ebf742d272 Merge pull request #219 from fluxcd/bootstrap-branch
Add branch flag to bootstrap cmd
2020-09-09 17:59:30 +03:00
stefanprodan
a7b1b04920 Add bootstrap reinstall e2e test 2020-09-09 17:27:56 +03:00
stefanprodan
1218d6abe8 Add branch flag to bootstrap cmd 2020-09-09 16:41:48 +03:00
Stefan Prodan
f9d546676b Merge pull request #218 from fluxcd/refac
Add GitHub bootstrap e2e test
2020-09-09 14:15:33 +03:00
stefanprodan
afef6960b9 Cleanup GitHub e2e repo 2020-09-09 14:03:26 +03:00
stefanprodan
f6626b8975 Add GitHub bootstrap e2e test 2020-09-09 08:56:55 +03:00
stefanprodan
b1e66f81ab Add label validation 2020-09-08 20:08:28 +03:00
Stefan Prodan
9cc018e618 Merge pull request #216 from fluxcd/labels
Add label arg to create commands
2020-09-08 18:14:31 +03:00
stefanprodan
797cd9bea2 Add label arg to create commands 2020-09-08 18:03:04 +03:00
Hidde Beydals
9dbfca3d7a Merge pull request #212 from fluxcd/refactor-hr-source-kind-validation
Refactor HelmRelease chart source kind validation
2020-09-07 12:02:34 +02:00
Hidde Beydals
f18d1efdcb Refactor HelmRelease chart source kind validation 2020-09-07 11:13:15 +02:00
stefanprodan
29a9b89224 Set Homebrew formula name 2020-09-07 12:04:27 +03:00
Stefan Prodan
11b5b9808b Merge pull request #211 from fluxcd/homebrew
Publish Homebrew formula
2020-09-07 11:55:10 +03:00
stefanprodan
ca1f84d22b Publish Homebrew formula 2020-09-07 11:41:06 +03:00
Stefan Prodan
d6c6c88e6e Merge pull request #209 from fluxcd/sops
Mozilla SOPS
2020-09-06 15:02:36 +03:00
stefanprodan
ee33702463 Add Mozilla SOPS guide to docs 2020-09-06 14:24:33 +03:00
stefanprodan
8b6995e9ec Add decryption flags to create kustomization cmd 2020-09-06 13:56:56 +03:00
44 changed files with 677 additions and 182 deletions

74
.github/workflows/bootstrap.yaml vendored Normal file
View File

@@ -0,0 +1,74 @@
name: bootstrap
on:
push:
branches:
- '*'
jobs:
github:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Restore Go cache
uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.14.x
- name: Setup Kubernetes
uses: engineerd/setup-kind@v0.4.0
- name: Set outputs
id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Build
run: sudo go build -o ./bin/gotk ./cmd/gotk
- name: bootstrap init
run: |
./bin/gotk bootstrap github \
--owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \
--path=test-cluster
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: bootstrap no-op
run: |
./bin/gotk bootstrap github \
--owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \
--path=test-cluster
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: uninstall
run: |
./bin/gotk suspend kustomization gitops-system
./bin/gotk uninstall --resources --crds -s
- name: bootstrap reinstall
run: |
./bin/gotk bootstrap github \
--owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \
--path=test-cluster
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: delete repository
run: |
./bin/gotk bootstrap github \
--owner=fluxcd-testing \
--repository=gotk-test-${{ steps.vars.outputs.sha_short }} \
--path=test-cluster \
--delete
env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
- name: Debug failure
if: failure()
run: |
kubectl -n gitops-system get all
kubectl -n gitops-system logs deploy/source-controller
kubectl -n gitops-system logs deploy/kustomize-controller

View File

@@ -82,3 +82,4 @@ jobs:
args: release --release-notes=/tmp/release.txt --skip-validate args: release --release-notes=/tmp/release.txt --skip-validate
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}

View File

@@ -54,10 +54,10 @@ jobs:
id: cpr id: cpr
uses: peter-evans/create-pull-request@v3 uses: peter-evans/create-pull-request@v3
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.BOT_GITHUB_TOKEN }}
commit-message: Update toolkit components commit-message: Update toolkit components
committer: GitHub <noreply@github.com> committer: GitHub <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> author: fluxcdbot <fluxcdbot@users.noreply.github.com>
title: Update toolkit components title: Update toolkit components
body: | body: |
${{ steps.update.outputs.pr_body }} ${{ steps.update.outputs.pr_body }}

View File

@@ -15,3 +15,17 @@ archives:
- name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" - name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
files: files:
- none* - none*
brews:
- name: gotk
tap:
owner: fluxcd
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
folder: Formula
homepage: "https://toolkit.fluxcd.io/"
description: "GitOps Toolkit CLI"
dependencies:
- name: kubectl
type: optional
test: |
system "#{bin}/gotk --version"

View File

@@ -45,15 +45,18 @@ var bootstrapCmd = &cobra.Command{
} }
var ( var (
bootstrapVersion string bootstrapVersion string
bootstrapComponents []string bootstrapComponents []string
bootstrapRegistry string bootstrapRegistry string
bootstrapImagePullSecret string bootstrapImagePullSecret string
bootstrapArch string bootstrapArch string
bootstrapBranch string
bootstrapWatchAllNamespaces bool
bootstrapLogLevel string
) )
const ( const (
bootstrapBranch = "master" bootstrapDefaultBranch = "master"
bootstrapInstallManifest = "toolkit-components.yaml" bootstrapInstallManifest = "toolkit-components.yaml"
bootstrapSourceManifest = "toolkit-source.yaml" bootstrapSourceManifest = "toolkit-source.yaml"
bootstrapKustomizationManifest = "toolkit-kustomization.yaml" bootstrapKustomizationManifest = "toolkit-kustomization.yaml"
@@ -70,7 +73,24 @@ func init() {
"Kubernetes secret name used for pulling the toolkit images from a private registry") "Kubernetes secret name used for pulling the toolkit images from a private registry")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArch, "arch", "amd64", bootstrapCmd.PersistentFlags().StringVar(&bootstrapArch, "arch", "amd64",
"arch can be amd64 or arm64") "arch can be amd64 or arm64")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapBranch, "branch", bootstrapDefaultBranch,
"default branch (for GitHub this must match the default branch setting for the organization)")
rootCmd.AddCommand(bootstrapCmd) rootCmd.AddCommand(bootstrapCmd)
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapWatchAllNamespaces, "watch-all-namespaces", true,
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapLogLevel, "log-level", "info", "set the controllers log level")
}
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)
}
return nil
} }
func generateInstallManifests(targetPath, namespace, tmpDir string) (string, error) { func generateInstallManifests(targetPath, namespace, tmpDir string) (string, error) {
@@ -81,7 +101,9 @@ func generateInstallManifests(targetPath, namespace, tmpDir string) (string, err
return "", fmt.Errorf("generating manifests failed: %w", err) return "", fmt.Errorf("generating manifests failed: %w", err)
} }
if err := genInstallManifests(bootstrapVersion, namespace, bootstrapComponents, bootstrapRegistry, bootstrapImagePullSecret, bootstrapArch, gotkDir); err != nil { if err := genInstallManifests(bootstrapVersion, namespace, bootstrapComponents,
bootstrapWatchAllNamespaces, bootstrapRegistry, bootstrapImagePullSecret,
bootstrapArch, bootstrapLogLevel, gotkDir); err != nil {
return "", fmt.Errorf("generating manifests failed: %w", err) return "", fmt.Errorf("generating manifests failed: %w", err)
} }
@@ -114,8 +136,8 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
return nil return nil
} }
func generateSyncManifests(url, name, namespace, targetPath, tmpDir string, interval time.Duration) error { func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) error {
gvk := sourcev1.GroupVersion.WithKind("GitRepository") gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)
gitRepository := sourcev1.GitRepository{ gitRepository := sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind, Kind: gvk.Kind,
@@ -131,7 +153,7 @@ func generateSyncManifests(url, name, namespace, targetPath, tmpDir string, inte
Duration: interval, Duration: interval,
}, },
Reference: &sourcev1.GitRepositoryRef{ Reference: &sourcev1.GitRepositoryRef{
Branch: "master", Branch: branch,
}, },
SecretRef: &corev1.LocalObjectReference{ SecretRef: &corev1.LocalObjectReference{
Name: name, Name: name,
@@ -148,7 +170,7 @@ func generateSyncManifests(url, name, namespace, targetPath, tmpDir string, inte
return err return err
} }
gvk = kustomizev1.GroupVersion.WithKind("Kustomization") gvk = kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)
kustomization := kustomizev1.Kustomization{ kustomization := kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind, Kind: gvk.Kind,
@@ -164,7 +186,7 @@ func generateSyncManifests(url, name, namespace, targetPath, tmpDir string, inte
}, },
Path: fmt.Sprintf("./%s", strings.TrimPrefix(targetPath, "./")), Path: fmt.Sprintf("./%s", strings.TrimPrefix(targetPath, "./")),
Prune: true, Prune: true,
SourceRef: kustomizev1.CrossNamespaceObjectReference{ SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourcev1.GitRepositoryKind, Kind: sourcev1.GitRepositoryKind,
Name: name, Name: name,
}, },

View File

@@ -55,6 +55,9 @@ the bootstrap command will perform an upgrade if needed.`,
# Run bootstrap for a private repo hosted on GitHub Enterprise # Run bootstrap for a private repo hosted on GitHub Enterprise
gotk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> gotk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a an existing repository with a branch named main
gotk bootstrap github --owner=<organization> --repository=<repo name> --branch=main
`, `,
RunE: bootstrapGitHubCmdRun, RunE: bootstrapGitHubCmdRun,
} }
@@ -68,6 +71,7 @@ var (
ghHostname string ghHostname string
ghPath string ghPath string
ghTeams []string ghTeams []string
ghDelete bool
) )
const ( const (
@@ -84,6 +88,9 @@ func init() {
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname") bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
bootstrapGitHubCmd.Flags().StringVar(&ghPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path") bootstrapGitHubCmd.Flags().StringVar(&ghPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
bootstrapGitHubCmd.Flags().BoolVar(&ghDelete, "delete", false, "delete repository (used for testing only)")
bootstrapGitHubCmd.Flags().MarkHidden("delete")
bootstrapCmd.AddCommand(bootstrapGitHubCmd) bootstrapCmd.AddCommand(bootstrapGitHubCmd)
} }
@@ -93,8 +100,8 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("%s environment variable not found", git.GitHubTokenName) return fmt.Errorf("%s environment variable not found", git.GitHubTokenName)
} }
if !utils.containsItemString(supportedArch, bootstrapArch) { if err := bootstrapValidate(); err != nil {
return fmt.Errorf("arch %s is not supported, can be %v", bootstrapArch, supportedArch) return err
} }
repository, err := git.NewRepository(ghRepository, ghOwner, ghHostname, ghToken, "gotk", ghOwner+"@users.noreply.github.com") repository, err := git.NewRepository(ghRepository, ghOwner, ghHostname, ghToken, "gotk", ghOwner+"@users.noreply.github.com")
@@ -107,11 +114,6 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
IsPersonal: ghPersonal, IsPersonal: ghPersonal,
} }
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
tmpDir, err := ioutil.TempDir("", namespace) tmpDir, err := ioutil.TempDir("", namespace)
if err != nil { if err != nil {
return err return err
@@ -121,6 +123,14 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
if ghDelete {
if err := provider.DeleteRepository(ctx, repository); err != nil {
return err
}
logger.Successf("repository deleted")
return nil
}
// create GitHub repository if doesn't exists // create GitHub repository if doesn't exists
logger.Actionf("connecting to %s", ghHostname) logger.Actionf("connecting to %s", ghHostname)
changed, err := provider.CreateRepository(ctx, repository) changed, err := provider.CreateRepository(ctx, repository)
@@ -173,6 +183,11 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("components are up to date") logger.Successf("components are up to date")
} }
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
// determine if repo synchronization is working // determine if repo synchronization is working
isInstall := shouldInstallManifests(ctx, kubeClient, namespace) isInstall := shouldInstallManifests(ctx, kubeClient, namespace)
@@ -211,22 +226,19 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
} }
// configure repo synchronization // configure repo synchronization
if isInstall { logger.Actionf("generating sync manifests")
// generate source and kustomization manifests if err := generateSyncManifests(repository.GetSSH(), bootstrapBranch, namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
logger.Actionf("generating sync manifests") return err
if err := generateSyncManifests(repository.GetSSH(), namespace, namespace, ghPath, tmpDir, ghInterval); err != nil { }
return err
}
// commit and push manifests // commit and push manifests
if changed, err = repository.Commit(ctx, path.Join(ghPath, namespace), "Add manifests"); err != nil { if changed, err = repository.Commit(ctx, path.Join(ghPath, namespace), "Add manifests"); err != nil {
return err
} else if changed {
if err := repository.Push(ctx); err != nil {
return err return err
} else if changed {
if err := repository.Push(ctx); err != nil {
return err
}
logger.Successf("sync manifests pushed")
} }
logger.Successf("sync manifests pushed")
// apply manifests and waiting for sync // apply manifests and waiting for sync
logger.Actionf("applying sync manifests") logger.Actionf("applying sync manifests")

View File

@@ -52,6 +52,9 @@ the bootstrap command will perform an upgrade if needed.`,
# Run bootstrap for a private repo hosted on a GitLab server # Run bootstrap for a private repo hosted on a GitLab server
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain> gotk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a an existing repository with a branch named main
gotk bootstrap gitlab --owner=<organization> --repository=<repo name> --branch=main
`, `,
RunE: bootstrapGitLabCmdRun, RunE: bootstrapGitLabCmdRun,
} }
@@ -86,8 +89,8 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("%s environment variable not found", git.GitLabTokenName) return fmt.Errorf("%s environment variable not found", git.GitLabTokenName)
} }
if !utils.containsItemString(supportedArch, bootstrapArch) { if err := bootstrapValidate(); err != nil {
return fmt.Errorf("arch %s is not supported, can be %v", bootstrapArch, supportedArch) return err
} }
repository, err := git.NewRepository(glRepository, glOwner, glHostname, glToken, "gotk", glOwner+"@users.noreply.gitlab.com") repository, err := git.NewRepository(glRepository, glOwner, glHostname, glToken, "gotk", glOwner+"@users.noreply.gitlab.com")
@@ -195,22 +198,19 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
} }
// configure repo synchronization // configure repo synchronization
if isInstall { logger.Actionf("generating sync manifests")
// generate source and kustomization manifests if err := generateSyncManifests(repository.GetSSH(), bootstrapBranch, namespace, namespace, glPath, tmpDir, glInterval); err != nil {
logger.Actionf("generating sync manifests") return err
if err := generateSyncManifests(repository.GetSSH(), namespace, namespace, glPath, tmpDir, glInterval); err != nil { }
return err
}
// commit and push manifests // commit and push manifests
if changed, err = repository.Commit(ctx, path.Join(glPath, namespace), "Add manifests"); err != nil { if changed, err = repository.Commit(ctx, path.Join(glPath, namespace), "Add manifests"); err != nil {
return err
} else if changed {
if err := repository.Push(ctx); err != nil {
return err return err
} else if changed {
if err := repository.Push(ctx); err != nil {
return err
}
logger.Successf("sync manifests pushed")
} }
logger.Successf("sync manifests pushed")
// apply manifests and waiting for sync // apply manifests and waiting for sync
logger.Actionf("applying sync manifests") logger.Actionf("applying sync manifests")

View File

@@ -17,8 +17,12 @@ limitations under the License.
package main package main
import ( import (
"fmt"
"strings"
"time" "time"
"k8s.io/apimachinery/pkg/util/validation"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -31,10 +35,38 @@ var createCmd = &cobra.Command{
var ( var (
interval time.Duration interval time.Duration
export bool export bool
labels []string
) )
func init() { func init() {
createCmd.PersistentFlags().DurationVarP(&interval, "interval", "", time.Minute, "source sync interval") createCmd.PersistentFlags().DurationVarP(&interval, "interval", "", time.Minute, "source sync interval")
createCmd.PersistentFlags().BoolVar(&export, "export", false, "export in YAML format to stdout") createCmd.PersistentFlags().BoolVar(&export, "export", false, "export in YAML format to stdout")
createCmd.PersistentFlags().StringSliceVar(&labels, "label", nil,
"set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)")
rootCmd.AddCommand(createCmd) rootCmd.AddCommand(createCmd)
} }
func parseLabels() (map[string]string, error) {
result := make(map[string]string)
for _, label := range labels {
// validate key value pair
parts := strings.Split(label, "=")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid label format '%s', must be key=value", label)
}
// validate label name
if errors := validation.IsQualifiedName(parts[0]); len(errors) > 0 {
return nil, fmt.Errorf("invalid label '%s': %v", parts[0], errors)
}
// validate label value
if errors := validation.IsValidLabelValue(parts[1]); len(errors) > 0 {
return nil, fmt.Errorf("invalid label value '%s': %v", parts[1], errors)
}
result[parts[0]] = parts[1]
}
return result, nil
}

View File

@@ -115,20 +115,18 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
} }
hrSourceElements := strings.Split(hrSource, "/") hrSourceElements := strings.Split(hrSource, "/")
if len(hrSourceElements) != 2 { if len(hrSourceElements) != 2 {
return fmt.Errorf("source must be in format <kind>/<name>") return fmt.Errorf("invalid source '%s', must be in format <kind>/<name>", hrSource)
} }
hrSourceKind, hrSourceName := hrSourceElements[0], hrSourceElements[1] hrSourceKind, hrSourceName := hrSourceElements[0], hrSourceElements[1]
if hrSourceKind != sourcev1.HelmRepositoryKind && hrSourceKind != sourcev1.GitRepositoryKind { if !utils.containsItemString(supportedHelmChartSourceKinds, hrSourceKind) {
return fmt.Errorf("source kind must be one of: %s", []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind}) return fmt.Errorf("source kind %s is not supported, can be %v",
hrSourceKind, supportedHelmChartSourceKinds)
} }
if hrChart == "" { if hrChart == "" {
return fmt.Errorf("chart name or path is required") return fmt.Errorf("chart name or path is required")
} }
ctx, cancel := context.WithTimeout(context.Background(), timeout) sourceLabels, err := parseLabels()
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil { if err != nil {
return err return err
} }
@@ -141,6 +139,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
Labels: sourceLabels,
}, },
Spec: helmv2.HelmReleaseSpec{ Spec: helmv2.HelmReleaseSpec{
ReleaseName: hrName, ReleaseName: hrName,
@@ -181,6 +180,14 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
return exportHelmRelease(helmRelease) return exportHelmRelease(helmRelease)
} }
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
kubeClient, err := utils.kubeClient(kubeconfig)
if err != nil {
return err
}
logger.Actionf("applying release") logger.Actionf("applying release")
if err := upsertHelmRelease(ctx, kubeClient, helmRelease); err != nil { if err := upsertHelmRelease(ctx, kubeClient, helmRelease); err != nil {
return err return err
@@ -237,6 +244,7 @@ func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmReleas
return err return err
} }
existing.Labels = helmRelease.Labels
existing.Spec = helmRelease.Spec existing.Spec = helmRelease.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return err

View File

@@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1" sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
) )
@@ -73,15 +74,17 @@ var createKsCmd = &cobra.Command{
} }
var ( var (
ksSource string ksSource string
ksPath string ksPath string
ksPrune bool ksPrune bool
ksDependsOn []string ksDependsOn []string
ksValidation string ksValidation string
ksHealthCheck []string ksHealthCheck []string
ksHealthTimeout time.Duration ksHealthTimeout time.Duration
ksSAName string ksSAName string
ksSANamespace string ksSANamespace string
ksDecryptionProvider string
ksDecryptionSecret string
) )
func init() { func init() {
@@ -94,6 +97,8 @@ func init() {
createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied") createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied")
createKsCmd.Flags().StringVar(&ksSAName, "sa-name", "", "service account name") createKsCmd.Flags().StringVar(&ksSAName, "sa-name", "", "service account name")
createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace") createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace")
createKsCmd.Flags().StringVar(&ksDecryptionProvider, "decryption-provider", "", "enables secrets decryption, provider can be 'sops'")
createKsCmd.Flags().StringVar(&ksDecryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
createCmd.AddCommand(createKsCmd) createCmd.AddCommand(createKsCmd)
} }
@@ -117,10 +122,16 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
logger.Generatef("generating kustomization") logger.Generatef("generating kustomization")
} }
ksLabels, err := parseLabels()
if err != nil {
return err
}
kustomization := kustomizev1.Kustomization{ kustomization := kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
Labels: ksLabels,
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
DependsOn: ksDependsOn, DependsOn: ksDependsOn,
@@ -129,7 +140,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}, },
Path: ksPath, Path: ksPath,
Prune: ksPrune, Prune: ksPrune,
SourceRef: kustomizev1.CrossNamespaceObjectReference{ SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourcev1.GitRepositoryKind, Kind: sourcev1.GitRepositoryKind,
Name: ksSource, Name: ksSource,
}, },
@@ -139,31 +150,40 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
} }
if len(ksHealthCheck) > 0 { if len(ksHealthCheck) > 0 {
healthChecks := make([]kustomizev1.WorkloadReference, 0) healthChecks := make([]kustomizev1.CrossNamespaceObjectReference, 0)
for _, w := range ksHealthCheck { for _, w := range ksHealthCheck {
kindObj := strings.Split(w, "/") kindObj := strings.Split(w, "/")
if len(kindObj) != 2 { if len(kindObj) != 2 {
return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace' %v", w, kindObj) return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace' %v", w, kindObj)
} }
kind := kindObj[0] kind := kindObj[0]
//TODO: (stefan) extend this list with all the kstatus builtin kinds
kinds := map[string]bool{ kinds := map[string]bool{
"Deployment": true, "Deployment": true,
"DaemonSet": true, "DaemonSet": true,
"StatefulSet": true, "StatefulSet": true,
helmv2.HelmReleaseKind: true,
} }
if !kinds[kind] { if !kinds[kind] {
return fmt.Errorf("invalid health check kind '%s' can be Deployment, DaemonSet or StatefulSet", kind) return fmt.Errorf("invalid health check kind '%s' can be HelmRelease, Deployment, DaemonSet or StatefulSet", kind)
} }
nameNs := strings.Split(kindObj[1], ".") nameNs := strings.Split(kindObj[1], ".")
if len(nameNs) != 2 { if len(nameNs) != 2 {
return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace'", w) return fmt.Errorf("invalid health check '%s' must be in the format 'kind/name.namespace'", w)
} }
healthChecks = append(healthChecks, kustomizev1.WorkloadReference{ check := kustomizev1.CrossNamespaceObjectReference{
Kind: kind, Kind: kind,
Name: nameNs[0], Name: nameNs[0],
Namespace: nameNs[1], Namespace: nameNs[1],
}) }
//TODO: (stefan) define the API version as a constant in the API package
if kind == helmv2.HelmReleaseKind {
check.APIVersion = "helm.toolkit.fluxcd.io/v2alpha1"
}
healthChecks = append(healthChecks, check)
} }
kustomization.Spec.HealthChecks = healthChecks kustomization.Spec.HealthChecks = healthChecks
kustomization.Spec.Timeout = &metav1.Duration{ kustomization.Spec.Timeout = &metav1.Duration{
@@ -178,6 +198,21 @@ 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,
}
if ksDecryptionSecret != "" {
kustomization.Spec.Decryption.SecretRef = &corev1.LocalObjectReference{Name: ksDecryptionSecret}
}
}
if export { if export {
return exportKs(kustomization) return exportKs(kustomization)
} }
@@ -241,6 +276,7 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomiz
return err return err
} }
existing.Labels = kustomization.Labels
existing.Spec = kustomization.Spec existing.Spec = kustomization.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return err

View File

@@ -129,10 +129,16 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("git URL parse failed: %w", err) return fmt.Errorf("git URL parse failed: %w", err)
} }
sourceLabels, err := parseLabels()
if err != nil {
return err
}
gitRepository := sourcev1.GitRepository{ gitRepository := sourcev1.GitRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
Labels: sourceLabels,
}, },
Spec: sourcev1.GitRepositorySpec{ Spec: sourcev1.GitRepositorySpec{
URL: sourceGitURL, URL: sourceGitURL,
@@ -343,6 +349,7 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepos
return err return err
} }
existing.Labels = gitRepository.Labels
existing.Spec = gitRepository.Spec existing.Spec = gitRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return err

View File

@@ -19,18 +19,19 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"github.com/spf13/cobra"
"io/ioutil" "io/ioutil"
"net/url"
"os"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"net/url"
"os"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
) )
var createSourceHelmCmd = &cobra.Command{ var createSourceHelmCmd = &cobra.Command{
@@ -91,6 +92,11 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("url is required") return fmt.Errorf("url is required")
} }
sourceLabels, err := parseLabels()
if err != nil {
return err
}
tmpDir, err := ioutil.TempDir("", name) tmpDir, err := ioutil.TempDir("", name)
if err != nil { if err != nil {
return err return err
@@ -105,6 +111,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
Labels: sourceLabels,
}, },
Spec: sourcev1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
URL: sourceHelmURL, URL: sourceHelmURL,
@@ -225,6 +232,7 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRep
return err return err
} }
existing.Labels = helmRepository.Labels
existing.Spec = helmRepository.Spec existing.Spec = helmRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return err
@@ -233,27 +241,3 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRep
logger.Successf("source updated") logger.Successf("source updated")
return nil return nil
} }
func exportHelmRepository(source sourcev1.HelmRepository) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)
export := sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: source.Name,
Namespace: source.Namespace,
},
Spec: source.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(string(data))
return nil
}

View File

@@ -68,7 +68,7 @@ func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
} }
if len(list.Items) == 0 { if len(list.Items) == 0 {
logger.Failuref("no kustomizations found in %s namespace", namespace) logger.Failuref("no helmrelease found in %s namespace", namespace)
return nil return nil
} }
@@ -101,8 +101,10 @@ func exportHelmRelease(helmRelease helmv2.HelmRelease) error {
APIVersion: gvk.GroupVersion().String(), APIVersion: gvk.GroupVersion().String(),
}, },
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: helmRelease.Name, Name: helmRelease.Name,
Namespace: helmRelease.Namespace, Namespace: helmRelease.Namespace,
Labels: helmRelease.Labels,
Annotations: helmRelease.Annotations,
}, },
Spec: helmRelease.Spec, Spec: helmRelease.Spec,
} }

View File

@@ -20,12 +20,13 @@ import (
"context" "context"
"fmt" "fmt"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
) )
var exportKsCmd = &cobra.Command{ var exportKsCmd = &cobra.Command{
@@ -100,8 +101,10 @@ func exportKs(kustomization kustomizev1.Kustomization) error {
APIVersion: gvk.GroupVersion().String(), APIVersion: gvk.GroupVersion().String(),
}, },
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: kustomization.Name, Name: kustomization.Name,
Namespace: kustomization.Namespace, Namespace: kustomization.Namespace,
Labels: kustomization.Labels,
Annotations: kustomization.Annotations,
}, },
Spec: kustomization.Spec, Spec: kustomization.Spec,
} }

View File

@@ -20,13 +20,14 @@ import (
"context" "context"
"fmt" "fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
) )
var exportSourceGitCmd = &cobra.Command{ var exportSourceGitCmd = &cobra.Command{
@@ -110,8 +111,10 @@ func exportGit(source sourcev1.GitRepository) error {
APIVersion: gvk.GroupVersion().String(), APIVersion: gvk.GroupVersion().String(),
}, },
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: source.Name, Name: source.Name,
Namespace: source.Namespace, Namespace: source.Namespace,
Labels: source.Labels,
Annotations: source.Annotations,
}, },
Spec: source.Spec, Spec: source.Spec,
} }

View File

@@ -20,13 +20,14 @@ import (
"context" "context"
"fmt" "fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
) )
var exportSourceHelmCmd = &cobra.Command{ var exportSourceHelmCmd = &cobra.Command{
@@ -102,6 +103,32 @@ func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return nil return nil
} }
func exportHelmRepository(source sourcev1.HelmRepository) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)
export := sourcev1.HelmRepository{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: source.Name,
Namespace: source.Namespace,
Labels: source.Labels,
Annotations: source.Annotations,
},
Spec: source.Spec,
}
data, err := yaml.Marshal(export)
if err != nil {
return err
}
fmt.Println("---")
fmt.Println(string(data))
return nil
}
func exportHelmCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.HelmRepository) error { func exportHelmCredentials(ctx context.Context, kubeClient client.Client, source sourcev1.HelmRepository) error {
if source.Spec.SecretRef != nil { if source.Spec.SecretRef != nil {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{

View File

@@ -55,14 +55,16 @@ If a previous version is installed, then an in-place upgrade will be performed.`
} }
var ( var (
installExport bool installExport bool
installDryRun bool installDryRun bool
installManifestsPath string installManifestsPath string
installVersion string installVersion string
installComponents []string installComponents []string
installRegistry string installRegistry string
installImagePullSecret string installImagePullSecret string
installArch string installArch string
installWatchAllNamespaces bool
installLogLevel string
) )
func init() { func init() {
@@ -82,6 +84,9 @@ func init() {
"Kubernetes secret name used for pulling the toolkit images from a private registry") "Kubernetes secret name used for pulling the toolkit images from a private registry")
installCmd.Flags().StringVar(&installArch, "arch", "amd64", installCmd.Flags().StringVar(&installArch, "arch", "amd64",
"arch can be amd64 or arm64") "arch can be amd64 or arm64")
installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", true,
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
installCmd.Flags().StringVar(&installLogLevel, "log-level", "info", "set the controllers log level")
rootCmd.AddCommand(installCmd) rootCmd.AddCommand(installCmd)
} }
@@ -90,6 +95,10 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("arch %s is not supported, can be %v", installArch, supportedArch) 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) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
@@ -111,7 +120,9 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
logger.Generatef("generating manifests") logger.Generatef("generating manifests")
} }
if kustomizePath == "" { if kustomizePath == "" {
err = genInstallManifests(installVersion, namespace, installComponents, installRegistry, installImagePullSecret, installArch, tmpDir) err = genInstallManifests(installVersion, namespace, installComponents,
installWatchAllNamespaces, installRegistry, installImagePullSecret,
installArch, installLogLevel, tmpDir)
if err != nil { if err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }
@@ -199,8 +210,10 @@ fieldSpecs:
var kustomizationTmpl = `--- var kustomizationTmpl = `---
{{- $eventsAddr := .EventsAddr }} {{- $eventsAddr := .EventsAddr }}
{{- $watchAllNamespaces := .WatchAllNamespaces }}
{{- $registry := .Registry }} {{- $registry := .Registry }}
{{- $arch := .Arch }} {{- $arch := .Arch }}
{{- $logLevel := .LogLevel }}
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
namespace: {{.Namespace}} namespace: {{.Namespace}}
@@ -223,7 +236,20 @@ patches:
patchesJson6902: patchesJson6902:
{{- range $i, $component := .Components }} {{- range $i, $component := .Components }}
{{- if ne $component "notification-controller" }} {{- if eq $component "notification-controller" }}
- target:
group: apps
version: v1
kind: Deployment
name: {{$component}}
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args/0
value: --watch-all-namespaces={{$watchAllNamespaces}}
- op: replace
path: /spec/template/spec/containers/0/args/1
value: --log-level={{$logLevel}}
{{- else }}
- target: - target:
group: apps group: apps
version: v1 version: v1
@@ -233,6 +259,12 @@ patchesJson6902:
- op: replace - op: replace
path: /spec/template/spec/containers/0/args/0 path: /spec/template/spec/containers/0/args/0
value: --events-addr={{$eventsAddr}} value: --events-addr={{$eventsAddr}}
- op: replace
path: /spec/template/spec/containers/0/args/1
value: --watch-all-namespaces={{$watchAllNamespaces}}
- op: replace
path: /spec/template/spec/containers/0/args/2
value: --log-level={{$logLevel}}
{{- end }} {{- end }}
{{- end }} {{- end }}
@@ -308,28 +340,33 @@ func downloadManifests(version string, tmpDir string) error {
return nil return nil
} }
func genInstallManifests(version string, namespace string, components []string, registry, imagePullSecret, arch, tmpDir string) error { func genInstallManifests(version string, namespace string, components []string,
watchAllNamespaces bool, registry, imagePullSecret, arch, logLevel, tmpDir string) error {
eventsAddr := "" eventsAddr := ""
if utils.containsItemString(components, defaultNotification) { if utils.containsItemString(components, defaultNotification) {
eventsAddr = fmt.Sprintf("http://%s/", defaultNotification) eventsAddr = fmt.Sprintf("http://%s/", defaultNotification)
} }
model := struct { model := struct {
Version string Version string
Namespace string Namespace string
Components []string Components []string
EventsAddr string EventsAddr string
Registry string Registry string
ImagePullSecret string ImagePullSecret string
Arch string Arch string
WatchAllNamespaces bool
LogLevel string
}{ }{
Version: version, Version: version,
Namespace: namespace, Namespace: namespace,
Components: components, Components: components,
EventsAddr: eventsAddr, EventsAddr: eventsAddr,
Registry: registry, Registry: registry,
ImagePullSecret: imagePullSecret, ImagePullSecret: imagePullSecret,
Arch: arch, Arch: arch,
WatchAllNamespaces: watchAllNamespaces,
LogLevel: logLevel,
} }
if err := downloadManifests(version, tmpDir); err != nil { if err := downloadManifests(version, tmpDir); err != nil {

View File

@@ -26,6 +26,8 @@ import (
"github.com/spf13/cobra/doc" "github.com/spf13/cobra/doc"
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
gotklog "github.com/fluxcd/toolkit/pkg/log" gotklog "github.com/fluxcd/toolkit/pkg/log"
) )
@@ -104,11 +106,14 @@ var (
) )
var ( var (
defaultComponents = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"} defaultComponents = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
defaultVersion = "latest" defaultVersion = "latest"
defaultNamespace = "gitops-system" defaultNamespace = "gitops-system"
defaultNotification = "notification-controller" defaultNotification = "notification-controller"
supportedArch = []string{"arm64", "amd64"} supportedArch = []string{"arm64", "amd64"}
supportedDecryptionProviders = []string{"sops"}
supportedHelmChartSourceKinds = []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind}
supportedLogLevels = []string{"debug", "info", "error"}
) )
func init() { func init() {

View File

@@ -21,7 +21,6 @@ import (
"fmt" "fmt"
"time" "time"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@@ -29,6 +28,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1" helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1"
consts "github.com/fluxcd/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
) )
var reconcileHrCmd = &cobra.Command{ var reconcileHrCmd = &cobra.Command{
@@ -95,10 +96,10 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace) logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace)
if helmRelease.Annotations == nil { if helmRelease.Annotations == nil {
helmRelease.Annotations = map[string]string{ helmRelease.Annotations = map[string]string{
helmv2.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), consts.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
} }
} else { } else {
helmRelease.Annotations[helmv2.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) helmRelease.Annotations[consts.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
} }
if err := kubeClient.Update(ctx, &helmRelease); err != nil { if err := kubeClient.Update(ctx, &helmRelease); err != nil {
return err return err

View File

@@ -21,10 +21,12 @@ import (
"fmt" "fmt"
"time" "time"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
consts "github.com/fluxcd/pkg/runtime"
) )
var reconcileKsCmd = &cobra.Command{ var reconcileKsCmd = &cobra.Command{
@@ -86,10 +88,10 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
logger.Actionf("annotating kustomization %s in %s namespace", name, namespace) logger.Actionf("annotating kustomization %s in %s namespace", name, namespace)
if kustomization.Annotations == nil { if kustomization.Annotations == nil {
kustomization.Annotations = map[string]string{ kustomization.Annotations = map[string]string{
kustomizev1.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), consts.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
} }
} else { } else {
kustomization.Annotations[kustomizev1.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) kustomization.Annotations[consts.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
} }
if err := kubeClient.Update(ctx, &kustomization); err != nil { if err := kubeClient.Update(ctx, &kustomization); err != nil {
return err return err

View File

@@ -19,11 +19,15 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"time"
consts "github.com/fluxcd/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
) )
var reconcileSourceGitCmd = &cobra.Command{ var reconcileSourceGitCmd = &cobra.Command{
@@ -68,10 +72,10 @@ func syncSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if gitRepository.Annotations == nil { if gitRepository.Annotations == nil {
gitRepository.Annotations = map[string]string{ gitRepository.Annotations = map[string]string{
sourcev1.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), consts.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
} }
} else { } else {
gitRepository.Annotations[sourcev1.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) gitRepository.Annotations[consts.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
} }
if err := kubeClient.Update(ctx, &gitRepository); err != nil { if err := kubeClient.Update(ctx, &gitRepository); err != nil {
return err return err

View File

@@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
consts "github.com/fluxcd/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1" sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
) )
@@ -72,10 +73,10 @@ func syncSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if helmRepository.Annotations == nil { if helmRepository.Annotations == nil {
helmRepository.Annotations = map[string]string{ helmRepository.Annotations = map[string]string{
sourcev1.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), consts.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
} }
} else { } else {
helmRepository.Annotations[sourcev1.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) helmRepository.Annotations[consts.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
} }
if err := kubeClient.Update(ctx, &helmRepository); err != nil { if err := kubeClient.Update(ctx, &helmRepository); err != nil {
return err return err

View File

@@ -10,11 +10,14 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
``` ```
--arch string arch can be amd64 or arm64 (default "amd64") --arch string arch can be amd64 or arm64 (default "amd64")
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "master")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
-h, --help help for bootstrap -h, --help help for bootstrap
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--log-level string set the controllers log level (default "info")
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
-v, --version string toolkit version (default "latest") -v, --version string toolkit version (default "latest")
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

View File

@@ -35,6 +35,9 @@ gotk bootstrap github [flags]
# Run bootstrap for a private repo hosted on GitHub Enterprise # Run bootstrap for a private repo hosted on GitHub Enterprise
gotk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain> gotk bootstrap github --owner=<organization> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a an existing repository with a branch named main
gotk bootstrap github --owner=<organization> --repository=<repo name> --branch=main
``` ```
### Options ### Options
@@ -55,14 +58,17 @@ gotk bootstrap github [flags]
``` ```
--arch string arch can be amd64 or arm64 (default "amd64") --arch string arch can be amd64 or arm64 (default "amd64")
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "master")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--log-level string set the controllers log level (default "info")
--namespace string the namespace scope for this operation (default "gitops-system") --namespace string the namespace scope for this operation (default "gitops-system")
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects
-v, --version string toolkit version (default "latest") -v, --version string toolkit version (default "latest")
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
``` ```
### SEE ALSO ### SEE ALSO

View File

@@ -32,6 +32,9 @@ gotk bootstrap gitlab [flags]
# Run bootstrap for a private repo hosted on a GitLab server # Run bootstrap for a private repo hosted on a GitLab server
gotk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain> gotk bootstrap gitlab --owner=<group> --repository=<repo name> --hostname=<domain>
# Run bootstrap for a an existing repository with a branch named main
gotk bootstrap gitlab --owner=<organization> --repository=<repo name> --branch=main
``` ```
### Options ### Options
@@ -52,14 +55,17 @@ gotk bootstrap gitlab [flags]
``` ```
--arch string arch can be amd64 or arm64 (default "amd64") --arch string arch can be amd64 or arm64 (default "amd64")
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "master")
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller]) --components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--log-level string set the controllers log level (default "info")
--namespace string the namespace scope for this operation (default "gitops-system") --namespace string the namespace scope for this operation (default "gitops-system")
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects
-v, --version string toolkit version (default "latest") -v, --version string toolkit version (default "latest")
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
``` ```
### SEE ALSO ### SEE ALSO

View File

@@ -12,6 +12,7 @@ The create sub-commands generate sources and resources.
--export export in YAML format to stdout --export export in YAML format to stdout
-h, --help help for create -h, --help help for create
--interval duration source sync interval (default 1m0s) --interval duration source sync interval (default 1m0s)
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

View File

@@ -72,6 +72,7 @@ gotk create helmrelease [name] [flags]
--export export in YAML format to stdout --export export in YAML format to stdout
--interval duration source sync interval (default 1m0s) --interval duration source sync interval (default 1m0s)
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
--namespace string the namespace scope for this operation (default "gitops-system") --namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects

View File

@@ -48,6 +48,8 @@ gotk create kustomization [name] [flags]
### Options ### 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 --depends-on stringArray Kustomization that must be ready before this Kustomization can be applied
--health-check stringArray workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>' --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) --health-check-timeout duration timeout of health checking operations (default 2m0s)
@@ -66,6 +68,7 @@ gotk create kustomization [name] [flags]
--export export in YAML format to stdout --export export in YAML format to stdout
--interval duration source sync interval (default 1m0s) --interval duration source sync interval (default 1m0s)
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
--namespace string the namespace scope for this operation (default "gitops-system") --namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects

View File

@@ -18,6 +18,7 @@ The create source sub-commands generate sources.
--export export in YAML format to stdout --export export in YAML format to stdout
--interval duration source sync interval (default 1m0s) --interval duration source sync interval (default 1m0s)
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
--namespace string the namespace scope for this operation (default "gitops-system") --namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects

View File

@@ -73,6 +73,7 @@ gotk create source git [name] [flags]
--export export in YAML format to stdout --export export in YAML format to stdout
--interval duration source sync interval (default 1m0s) --interval duration source sync interval (default 1m0s)
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
--namespace string the namespace scope for this operation (default "gitops-system") --namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects

View File

@@ -53,6 +53,7 @@ gotk create source helm [name] [flags]
--export export in YAML format to stdout --export export in YAML format to stdout
--interval duration source sync interval (default 1m0s) --interval duration source sync interval (default 1m0s)
--kubeconfig string path to the kubeconfig file (default "~/.kube/config") --kubeconfig string path to the kubeconfig file (default "~/.kube/config")
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
--namespace string the namespace scope for this operation (default "gitops-system") --namespace string the namespace scope for this operation (default "gitops-system")
--timeout duration timeout for this operation (default 5m0s) --timeout duration timeout for this operation (default 5m0s)
--verbose print generated objects --verbose print generated objects

View File

@@ -37,9 +37,11 @@ gotk install [flags]
--export write the install manifests to stdout and exit --export write the install manifests to stdout and exit
-h, --help help for install -h, --help help for install
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry --image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
--log-level string set the controllers log level (default "info")
--manifests string path to the manifest directory, dev only --manifests string path to the manifest directory, dev only
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd") --registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
-v, --version string toolkit version (default "latest") -v, --version string toolkit version (default "latest")
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

View File

@@ -19,7 +19,15 @@ export GITHUB_USER=<your-username>
## Install the toolkit CLI ## Install the toolkit CLI
To install the latest `gotk` release run: To install the latest `gotk` release on MacOS and Linux using
[Homebrew](https://brew.sh/) run:
```sh
brew tap fluxcd/tap
brew install gotk
```
Or install `gotk` by downloading precompiled binaries using a Bash script:
```sh ```sh
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
@@ -29,7 +37,7 @@ The install script downloads the gotk binary to `/usr/local/bin`.
Binaries for macOS and Linux AMD64/ARM64 are available for download on the Binaries for macOS and Linux AMD64/ARM64 are available for download on the
[release page](https://github.com/fluxcd/toolkit/releases). [release page](https://github.com/fluxcd/toolkit/releases).
To configure your shell to load gotk completions add to your bash profile: To configure your shell to load gotk completions add to your Bash profile:
```sh ```sh
# ~/.bashrc or ~/.bash_profile # ~/.bashrc or ~/.bash_profile

View File

@@ -8,13 +8,24 @@ to manage one or more Kubernetes clusters.
You will need a Kubernetes cluster version **1.16** or newer You will need a Kubernetes cluster version **1.16** or newer
and kubectl version **1.18** or newer. and kubectl version **1.18** or newer.
Install the toolkit CLI with: ## Install the toolkit CLI
With Homebrew:
```sh
brew tap fluxcd/tap
brew install gotk
```
With Bash:
```sh ```sh
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
# enable completions in ~/.bash_profile
. <(gotk completion)
``` ```
The install script downloads the gotk binary to `/usr/local/bin`.
Binaries for macOS and Linux AMD64/ARM64 are available for download on the Binaries for macOS and Linux AMD64/ARM64 are available for download on the
[release page](https://github.com/fluxcd/toolkit/releases). [release page](https://github.com/fluxcd/toolkit/releases).
@@ -68,6 +79,10 @@ cluster e.g. `staging-cluster` and `production-cluster`:
└── gitops-system └── gitops-system
``` ```
!!! hint "Change the default branch"
If you wish to change the branch to something else than master, create the repository manually,
push a branch to origin and then use `gotk bootstrap <GIT-PROVIDER> --branch=your-branch`.
### GitHub and GitHub Enterprise ### GitHub and GitHub Enterprise
Generate a [personal access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) Generate a [personal access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)

166
docs/guides/mozilla-sops.md Normal file
View File

@@ -0,0 +1,166 @@
# Manage Kubernetes secrets with Mozilla SOPS
In order to store secrets safely in a public or private Git repository, you can use
Mozilla's [SOPS](https://github.com/mozilla/sops) CLI to encrypt
Kubernetes secrets with OpenPGP, AWS KMS, GCP KMS and Azure Key Vault.
## Prerequisites
To follow this guide you'll need a Kubernetes cluster with the GitOps
toolkit controllers installed on it.
Please see the [get started guide](../get-started/index.md)
or the [installation guide](installation.md).
Install [gnupg](https://www.gnupg.org/) and [sops](https://github.com/mozilla/sops):
```sh
brew install gnupg sops
```
## Generate a GPG key
Generate a GPG key with OpenPGP without specifying a passphrase:
```console
$ gpg --full-generate-key
Real name: stefanprodan
Email address: stefanprodan@users.noreply.github.com
Comment:
You selected this USER-ID:
"stefanprodan <stefanprodan@users.noreply.github.com>"
```
Retrieve the GPG key ID (second row of the sec column):
```console
$ gpg --list-secret-keys stefanprodan@users.noreply.github.com
sec rsa3072 2020-09-06 [SC]
1F3D1CED2F865F5E59CA564553241F147E7C5FA4
```
Export the public and private keypair from your local GPG keyring and
create a Kubernetes secret named `sops-gpg` in the `gitops-system` namespace:
```sh
gpg --export-secret-keys \
--armor 1F3D1CED2F865F5E59CA564553241F147E7C5FA4 |
kubectl create secret generic sops-gpg \
--namespace=gitops-system \
--from-file=sops.asc=/dev/stdin
```
## Encrypt secrets
Generate a Kubernetes secret manifest with kubectl:
```sh
kubectl -n default create secret generic basic-auth \
--from-literal=user=admin \
--from-literal=password=change-me \
--dry-run=client \
-o yaml > basic-auth.yaml
```
Encrypt the secret with sops using your GPG key:
```sh
sops --encrypt \
--pgp=1F3D1CED2F865F5E59CA564553241F147E7C5FA4 \
--encrypted-regex '^(data|stringData)$' \
--in-place basic-auth.yaml
```
!!! hint
Note that you should encrypt only the `data` section. Encrypting the Kubernetes
secret metadata, kind or apiVersion is not supported by kustomize-controller.
You can now commit the encrypted secret to your Git repository.
## Configure secrets decryption
Registry the Git repository on your cluster:
```sh
gotk create source git my-secrets \
--url=https://github.com/my-org/my-secrets
```
Create a kustomization for reconciling the secrets on the cluster:
```sh
gotk create kustomization my-secrets \
--source=my-secrets \
--prune=true \
--interval=10m \
--decryption-provider=sops \
--decryption-secret=sops-gpg
```
Note that the `sops-gpg` can contain more than one key, sops will try to decrypt the
secrets by iterating over all the private keys until it finds one that works.
!!! hint KMS
When using AWS/GCP KMS or Azure Key Vault, you'll have to bind an IAM Role
with read access to the KMS keys to the `default` service account of the
`gitops-system` namespace for kustomize-controller to be able to fetch
keys from KMS.
## GitOps workflow
A cluster admin should create the Kubernetes secret with the PGP keys on each cluster and
add the GitRepository/Kustomization manifests to the fleet repository.
Git repository manifest:
```yaml
apiVersion: source.toolkit.fluxcd.io/v1alpha1
kind: GitRepository
metadata:
name: my-secrets
namespace: gitops-system
spec:
interval: 1m
url: https://github.com/my-org/my-secrets
```
Kustomization manifest:
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: my-secrets
namespace: gitops-system
spec:
interval: 10m0s
sourceRef:
kind: GitRepository
name: my-secrets
path: ./
prune: true
decryption:
provider: sops
secretRef:
name: sops-gpg
```
!!! hint
You can generate the above manifests using `gotk create <kind> --export > manifest.yaml`.
Assuming a team member wants to deploy an application that needs to connect
to a database using a username and password, they'll be doing the following:
* create a Kubernetes Secret manifest locally with the db credentials e.g. `db-auth.yaml`
* encrypt the secret `data` field with sops
* create a Kubernetes Deployment manifest for the app e.g. `app-deployment.yaml`
* add the Secret to the Deployment manifest as a [volume mount or env var](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets)
* commit the manifests `db-auth.yaml` and `app-deployment.yaml` to a Git repository that's being synced by the GitOps toolkit controllers
Once the manifests have been pushed to the Git repository, the following happens:
* source-controller pulls the changes from Git
* kustomize-controller loads the GPG keys from the `sops-pgp` secret
* kustomize-controller decrypts the Kubernetes secrets with sops and applies them on the cluster
* kubelet creates the pods and mounts the secret as a volume or env variable inside the app container

View File

@@ -67,7 +67,7 @@ Tasks
### Helm v3 feature parity ### Helm v3 feature parity
[= 90% "90%"] [= 100% "100%"]
Goals Goals
@@ -91,5 +91,6 @@ Tasks
- [x] <span style="color:grey">Implement support for values from `Secret` and `ConfigMap` resources</span> - [x] <span style="color:grey">Implement support for values from `Secret` and `ConfigMap` resources</span>
- [x] <span style="color:grey">Implement conditional remediation on (failed) Helm actions</span> - [x] <span style="color:grey">Implement conditional remediation on (failed) Helm actions</span>
- [x] <span style="color:grey">Implement support for Helm charts from Git</span> - [x] <span style="color:grey">Implement support for Helm charts from Git</span>
- [ ] [Implement support for referring to an alternative chart values file](https://github.com/fluxcd/helm-controller/issues/4) - [x] <span style="color:grey">Implement support for referring to an alternative chart values file</span>\
- [ ] Stabilize API
- [ ] Create a migration guide for Helm Operator users - [ ] Create a migration guide for Helm Operator users

9
go.mod
View File

@@ -4,12 +4,13 @@ go 1.14
require ( require (
github.com/blang/semver v3.5.1+incompatible github.com/blang/semver v3.5.1+incompatible
github.com/fluxcd/helm-controller/api v0.0.7 github.com/fluxcd/helm-controller/api v0.0.8
github.com/fluxcd/kustomize-controller/api v0.0.10 github.com/fluxcd/kustomize-controller/api v0.0.12
github.com/fluxcd/pkg/git v0.0.6 github.com/fluxcd/pkg/git v0.0.7
github.com/fluxcd/pkg/runtime v0.0.1
github.com/fluxcd/pkg/ssh v0.0.5 github.com/fluxcd/pkg/ssh v0.0.5
github.com/fluxcd/pkg/untar v0.0.5 github.com/fluxcd/pkg/untar v0.0.5
github.com/fluxcd/source-controller/api v0.0.14 github.com/fluxcd/source-controller/api v0.0.16
github.com/manifoldco/promptui v0.7.0 github.com/manifoldco/promptui v0.7.0
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect

22
go.sum
View File

@@ -111,18 +111,20 @@ github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fluxcd/helm-controller/api v0.0.7 h1:aidjXvcklClH8omhYqiKswZ+MS6t8knOpUacsuESue8= github.com/fluxcd/helm-controller/api v0.0.8 h1:Pf+hZjsUpRmoQJeCe178bGWOOm2/Bvg8/s0aafRa1wQ=
github.com/fluxcd/helm-controller/api v0.0.7/go.mod h1:KlzwTkpphQxulgWBwCl/uxfBU0QxK/X+w4YcJqGy/1c= github.com/fluxcd/helm-controller/api v0.0.8/go.mod h1:KlzwTkpphQxulgWBwCl/uxfBU0QxK/X+w4YcJqGy/1c=
github.com/fluxcd/kustomize-controller/api v0.0.10 h1:dhkTOg3LzNQwRL+lO0YlzOP7AhdpZdghUQNXYhvfiYU= github.com/fluxcd/kustomize-controller/api v0.0.12 h1:4wTGH+Mf0jmvVMmUg39LHbQto6pT3aescyPr2xT/5os=
github.com/fluxcd/kustomize-controller/api v0.0.10/go.mod h1:88m3p6xY3J2pjh5OsL3ANy7PkyA93KiqAJE58JMQyoc= github.com/fluxcd/kustomize-controller/api v0.0.12/go.mod h1:88m3p6xY3J2pjh5OsL3ANy7PkyA93KiqAJE58JMQyoc=
github.com/fluxcd/pkg/git v0.0.6 h1:4qktw8M3zj98MAs4ny6qSi36sYvTiI1czif5FqlQl4o= github.com/fluxcd/pkg/git v0.0.7 h1:tFSYPy7tcIYfOt8H5EUERXIRz7fk0id302oQZde1NtU=
github.com/fluxcd/pkg/git v0.0.6/go.mod h1:9AI9yPkb2ruIcE70moVG3WhunA2/RAMJPc3rtoH8QFE= github.com/fluxcd/pkg/git v0.0.7/go.mod h1:5Vu92x6Q3CpxDUllmB69kAkVY5jAtPpXcY2TSZ/oCJI=
github.com/fluxcd/pkg/runtime v0.0.1 h1:h8jztHVF9UMGD7XBQSfXDdw80bpT6BOkd0xe4kknPL0=
github.com/fluxcd/pkg/runtime v0.0.1/go.mod h1:cU1t0+Ld39pZjMyrrHukw1E++OZFNHxG2qAExfDWQ34=
github.com/fluxcd/pkg/ssh v0.0.5 h1:rnbFZ7voy2JBlUfMbfyqArX2FYaLNpDhccGFC3qW83A= github.com/fluxcd/pkg/ssh v0.0.5 h1:rnbFZ7voy2JBlUfMbfyqArX2FYaLNpDhccGFC3qW83A=
github.com/fluxcd/pkg/ssh v0.0.5/go.mod h1:7jXPdXZpc0ttMNz2kD9QuMi3RNn/e0DOFbj0Tij/+Hs= github.com/fluxcd/pkg/ssh v0.0.5/go.mod h1:7jXPdXZpc0ttMNz2kD9QuMi3RNn/e0DOFbj0Tij/+Hs=
github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk= github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw= github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw=
github.com/fluxcd/source-controller/api v0.0.14 h1:iNG6AGnr44z4T6F0JC2M82ekyxzJ29c3m+DVC7FwSHQ= github.com/fluxcd/source-controller/api v0.0.16 h1:Mk+X2H+5CX7vfmrVhGT/TR8EnZ8UmZ20TpPyP3e8ZBs=
github.com/fluxcd/source-controller/api v0.0.14/go.mod h1:PUe+EYQ/s+KPnz2iOCgdf+L6clM0SWkyvdXIpbfpkQE= github.com/fluxcd/source-controller/api v0.0.16/go.mod h1:PUe+EYQ/s+KPnz2iOCgdf+L6clM0SWkyvdXIpbfpkQE=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -265,8 +267,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v32 v32.0.0 h1:q74KVb22spUq0U5HqZ9VCYqQz8YRuOtL/39ZnfwO+NM= github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
github.com/google/go-github/v32 v32.0.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
resources: resources:
- github.com/fluxcd/helm-controller/config//crd?ref=v0.0.7 - github.com/fluxcd/helm-controller/config//crd?ref=v0.0.8
- github.com/fluxcd/helm-controller/config//manager?ref=v0.0.7 - github.com/fluxcd/helm-controller/config//manager?ref=v0.0.8
patchesJson6902: patchesJson6902:
- target: - target:
group: apps group: apps

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
resources: resources:
- github.com/fluxcd/kustomize-controller/config//crd?ref=v0.0.10 - github.com/fluxcd/kustomize-controller/config//crd?ref=v0.0.12
- github.com/fluxcd/kustomize-controller/config//manager?ref=v0.0.10 - github.com/fluxcd/kustomize-controller/config//manager?ref=v0.0.12
patchesJson6902: patchesJson6902:
- target: - target:
group: apps group: apps

View File

@@ -1,5 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
resources: resources:
- github.com/fluxcd/notification-controller/config//crd?ref=v0.0.8 - github.com/fluxcd/notification-controller/config//crd?ref=v0.0.10
- github.com/fluxcd/notification-controller/config//manager?ref=v0.0.8 - github.com/fluxcd/notification-controller/config//manager?ref=v0.0.10

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
resources: resources:
- github.com/fluxcd/source-controller/config//crd?ref=v0.0.14 - github.com/fluxcd/source-controller/config//crd?ref=v0.0.16
- github.com/fluxcd/source-controller/config//manager?ref=v0.0.14 - github.com/fluxcd/source-controller/config//manager?ref=v0.0.16
patchesJson6902: patchesJson6902:
- target: - target:
group: apps group: apps

View File

@@ -27,6 +27,7 @@ rules:
- events - events
verbs: verbs:
- create - create
- patch
--- ---
apiVersion: rbac.authorization.k8s.io/v1 apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding kind: RoleBinding

View File

@@ -50,6 +50,7 @@ nav:
- Setup Notifications: guides/notifications.md - Setup Notifications: guides/notifications.md
- Setup Webhook Receivers: guides/webhook-receivers.md - Setup Webhook Receivers: guides/webhook-receivers.md
- Sealed Secrets: guides/sealed-secrets.md - Sealed Secrets: guides/sealed-secrets.md
- Mozilla SOPS: guides/mozilla-sops.md
- Toolkit Components: - Toolkit Components:
- Source Controller: - Source Controller:
- Overview: components/source/controller.md - Overview: components/source/controller.md