Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
352b864636 | ||
|
|
c034befbb5 | ||
|
|
572cdf40fc | ||
|
|
0c0d353e9c | ||
|
|
bcc90afba2 | ||
|
|
a919703011 | ||
|
|
3300a45c39 | ||
|
|
f1cfae8f26 | ||
|
|
62763961be | ||
|
|
f1dab2279d | ||
|
|
ea337cf839 | ||
|
|
27277136f8 | ||
|
|
dd0b807fe4 | ||
|
|
ed09dd57b6 | ||
|
|
58b4c980c1 | ||
|
|
dd5165dcbf | ||
|
|
6da22613fe | ||
|
|
d0926776a5 | ||
|
|
14dc39e8d2 | ||
|
|
f0f2a79384 | ||
|
|
7b6f875920 | ||
|
|
52cec044b8 | ||
|
|
07dd59892f | ||
|
|
ffeaa683c5 | ||
|
|
1301bf7c15 | ||
|
|
69387fd2a4 | ||
|
|
12a0ebe3ba | ||
|
|
3de81827eb | ||
|
|
a7362b60e7 | ||
|
|
5d4bb3a43f | ||
|
|
d02d507812 | ||
|
|
554de7ba6f | ||
|
|
5d9ccc973d | ||
|
|
53ffb8aa00 | ||
|
|
c4da4a81aa | ||
|
|
b824ea8858 | ||
|
|
22e26efec1 | ||
|
|
679490e8f4 | ||
|
|
15f17ed36d | ||
|
|
c8265fb80c | ||
|
|
3883e92631 | ||
|
|
aa9bc4ce8b | ||
|
|
37c14e8088 | ||
|
|
439fbafc01 | ||
|
|
1b8e980519 | ||
|
|
567acb6291 | ||
|
|
996bfe87ff | ||
|
|
3c1793b6c5 | ||
|
|
1a7f253767 | ||
|
|
f188e59b21 | ||
|
|
5ea4e814f5 | ||
|
|
008b3b8408 | ||
|
|
7ae3dee900 | ||
|
|
2395ab6e14 | ||
|
|
8efe053ffa | ||
|
|
612600b88c | ||
|
|
4d7df52dbe |
3
.github/workflows/bootstrap.yaml
vendored
3
.github/workflows/bootstrap.yaml
vendored
@@ -49,8 +49,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||
- name: uninstall
|
||||
run: |
|
||||
./bin/flux suspend kustomization flux-system
|
||||
./bin/flux uninstall --resources --crds -s
|
||||
./bin/flux uninstall --resources --crds -s --timeout=10m
|
||||
- name: bootstrap reinstall
|
||||
run: |
|
||||
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
||||
|
||||
11
.github/workflows/e2e.yaml
vendored
11
.github/workflows/e2e.yaml
vendored
@@ -146,12 +146,21 @@ jobs:
|
||||
--chart=podinfo \
|
||||
--chart-version="5.0.x" \
|
||||
--service-account=dev-team
|
||||
- name: flux2-kustomize-helm-example
|
||||
run: |
|
||||
./bin/flux create source git flux-system \
|
||||
--url=https://github.com/fluxcd/flux2-kustomize-helm-example \
|
||||
--branch=main
|
||||
./bin/flux create kustomization flux-system \
|
||||
--source=flux-system \
|
||||
--path=./clusters/staging
|
||||
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=2m
|
||||
- name: flux check
|
||||
run: |
|
||||
./bin/flux check
|
||||
- name: flux uninstall
|
||||
run: |
|
||||
./bin/flux uninstall --crds --silent
|
||||
./bin/flux uninstall --crds --silent --timeout=10m
|
||||
- name: Debug failure
|
||||
if: failure()
|
||||
run: |
|
||||
|
||||
14
README.md
14
README.md
@@ -108,17 +108,17 @@ Depending on what you want to do, some of the following bits might be your first
|
||||
- Check out [how to contribute](CONTRIBUTING.md) to the project
|
||||
|
||||
### Upcoming Events
|
||||
|
||||
- 14 Dec 2020 - [The Power of GitOps with Flux and Flagger with Leigh Capili](https://www.meetup.com/GitOps-Community/events/274924513/)
|
||||
- 11 Jan 2021 - [Helm + GitOps = ⚡️⚡️⚡️ with Scott Rigby](https://www.meetup.com/GitOps-Community/events/275348736/)
|
||||
|
||||
### Featured Talks
|
||||
|
||||
- 14 Dec 2020 - [The Power of GitOps with Flux and Flagger (GitOps Hands-On) with Leigh Capili](https://youtu.be/cB7iXeNLteE)
|
||||
- 30 Nov 2020 - [The Power of GitOps with Flux 2 - Part 3 with Leigh Capili](https://youtu.be/N_K5g7o9JKg)
|
||||
- 24 Nov 2020 - [Flux CD v2 with GitOps Toolkit - Kubernetes Deployment and Sync Mechanism](https://youtu.be/R6OeIgb7lUI)
|
||||
- 02 Nov 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 2 with Leigh Capili](https://youtu.be/fC2YCxQRUwU)
|
||||
- 28 Oct 2020 - [The Kubelist Podcast: Flux with Michael Bridgen](https://www.heavybit.com/library/podcasts/the-kubelist-podcast/ep-5-flux-with-michael-bridgen-of-weaveworks/)
|
||||
- 19 Oct 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 1 with Leigh Capili](https://youtu.be/0v5bjysXTL8)
|
||||
- 30 Nov 2020 - [The Power of GitOps with Flux 2 - Part 3 with Leigh Capili](https://youtu.be/N_K5g7o9JKg)
|
||||
- 12 Oct 2020 - [Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
|
||||
- 4 Sep 2020 - [KubeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
|
||||
- 25 June 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
|
||||
- 04 Sep 2020 - [KubeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
|
||||
- 25 Jun 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
|
||||
|
||||
We are looking forward to seeing you with us!
|
||||
We look forward to seeing you with us!
|
||||
|
||||
@@ -10,6 +10,16 @@ Usage:
|
||||
run: flux -v
|
||||
```
|
||||
|
||||
This action places the `flux` binary inside your repository root under `bin/flux`.
|
||||
You should add `bin/flux` to your `.gitignore` file, as in the following example:
|
||||
|
||||
```gitignore
|
||||
# ignore flux binary
|
||||
bin/flux
|
||||
```
|
||||
|
||||
Note that this action can only be used on GitHub **Linux AMD64** runners.
|
||||
|
||||
### Automate Flux updates
|
||||
|
||||
Example workflow for updating Flux's components generated with `flux bootstrap --arch=amd64 --path=clusters/production`:
|
||||
|
||||
@@ -29,7 +29,7 @@ curl -sL $BIN_URL | tar xz
|
||||
|
||||
# Copy binary to GitHub runner
|
||||
mkdir -p $GITHUB_WORKSPACE/bin
|
||||
cp ./flux $GITHUB_WORKSPACE/bin
|
||||
mv ./flux $GITHUB_WORKSPACE/bin
|
||||
chmod +x $GITHUB_WORKSPACE/bin/flux
|
||||
|
||||
# Print version
|
||||
|
||||
@@ -59,6 +59,7 @@ var (
|
||||
bootstrapLogLevel = flags.LogLevel(defaults.LogLevel)
|
||||
bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"}
|
||||
bootstrapTokenAuth bool
|
||||
bootstrapClusterDomain string
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -87,6 +88,7 @@ func init() {
|
||||
"when enabled, the personal access token will be used instead of SSH deploy key")
|
||||
bootstrapCmd.PersistentFlags().Var(&bootstrapLogLevel, "log-level", bootstrapLogLevel.Description())
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory")
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapClusterDomain, "cluster-domain", defaults.ClusterDomain, "internal cluster domain")
|
||||
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
||||
rootCmd.AddCommand(bootstrapCmd)
|
||||
}
|
||||
@@ -102,6 +104,11 @@ func bootstrapValidate() error {
|
||||
return fmt.Errorf("component %s is required", component)
|
||||
}
|
||||
}
|
||||
|
||||
if err := utils.ValidateComponents(components); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -121,6 +128,7 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
||||
ManifestFile: defaults.ManifestFile,
|
||||
Timeout: timeout,
|
||||
TargetPath: targetPath,
|
||||
ClusterDomain: bootstrapClusterDomain,
|
||||
}
|
||||
|
||||
if localManifests == "" {
|
||||
@@ -132,12 +140,11 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
||||
}
|
||||
|
||||
if filePath, err := output.WriteFile(tmpDir); err != nil {
|
||||
filePath, err := output.WriteFile(tmpDir)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
||||
} else {
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
|
||||
@@ -155,7 +162,7 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) error {
|
||||
func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) (string, error) {
|
||||
opts := sync.Options{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
@@ -168,22 +175,22 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
|
||||
|
||||
manifest, err := sync.Generate(opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating install manifests failed: %w", err)
|
||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
||||
}
|
||||
|
||||
if _, err := manifest.WriteFile(tmpDir); err != nil {
|
||||
return err
|
||||
output, err := manifest.WriteFile(tmpDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
|
||||
return err
|
||||
outputDir := filepath.Dir(output)
|
||||
if err := utils.GenerateKustomizationYaml(outputDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nil
|
||||
return outputDir, nil
|
||||
}
|
||||
|
||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error {
|
||||
kubectlArgs := []string{"apply", "-k", filepath.Join(tmpDir, targetPath, namespace)}
|
||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error {
|
||||
kubectlArgs := []string{"apply", "-k", manifestsPath}
|
||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -29,8 +29,10 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/git"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var bootstrapGitHubCmd = &cobra.Command{
|
||||
@@ -75,7 +77,7 @@ var (
|
||||
ghPersonal bool
|
||||
ghPrivate bool
|
||||
ghHostname string
|
||||
ghPath string
|
||||
ghPath flags.SafeRelativePath
|
||||
ghTeams []string
|
||||
ghDelete bool
|
||||
ghSSHHostname string
|
||||
@@ -94,7 +96,7 @@ func init() {
|
||||
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghSSHHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
|
||||
bootstrapGitHubCmd.Flags().Var(&ghPath, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||
|
||||
bootstrapGitHubCmd.Flags().BoolVar(&ghDelete, "delete", false, "delete repository (used for testing only)")
|
||||
bootstrapGitHubCmd.Flags().MarkHidden("delete")
|
||||
@@ -174,13 +176,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// generate install manifests
|
||||
logger.Generatef("generating manifests")
|
||||
manifest, err := generateInstallManifests(ghPath, namespace, tmpDir, bootstrapManifestsPath)
|
||||
installManifest, err := generateInstallManifests(ghPath.String(), namespace, tmpDir, bootstrapManifestsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage install manifests
|
||||
changed, err = repository.Commit(ctx, path.Join(ghPath, namespace), "Add manifests")
|
||||
changed, err = repository.Commit(ctx, path.Join(ghPath.String(), namespace), "Add manifests")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -206,7 +208,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if isInstall {
|
||||
// apply install manifests
|
||||
logger.Actionf("installing components in %s namespace", namespace)
|
||||
if err := applyInstallManifests(ctx, manifest, bootstrapComponents()); err != nil {
|
||||
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("install completed")
|
||||
@@ -259,12 +261,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// configure repo synchronization
|
||||
logger.Actionf("generating sync manifests")
|
||||
if err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
|
||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, ghPath.String(), tmpDir, ghInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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.String(), namespace), "Add manifests"); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
@@ -275,7 +278,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// apply manifests and waiting for sync
|
||||
logger.Actionf("applying sync manifests")
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, ghPath, tmpDir); err != nil {
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,17 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/git"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var bootstrapGitLabCmd = &cobra.Command{
|
||||
@@ -65,6 +68,10 @@ the bootstrap command will perform an upgrade if needed.`,
|
||||
RunE: bootstrapGitLabCmdRun,
|
||||
}
|
||||
|
||||
const (
|
||||
gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z`
|
||||
)
|
||||
|
||||
var (
|
||||
glOwner string
|
||||
glRepository string
|
||||
@@ -73,7 +80,7 @@ var (
|
||||
glPrivate bool
|
||||
glHostname string
|
||||
glSSHHostname string
|
||||
glPath string
|
||||
glPath flags.SafeRelativePath
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -84,7 +91,7 @@ func init() {
|
||||
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, to be used when the SSH host differs from the HTTPS one")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&glPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
|
||||
bootstrapGitLabCmd.Flags().Var(&glPath, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||
|
||||
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
||||
}
|
||||
@@ -95,6 +102,14 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("%s environment variable not found", git.GitLabTokenName)
|
||||
}
|
||||
|
||||
projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, glRepository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !projectNameIsValid {
|
||||
return fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", glRepository)
|
||||
}
|
||||
|
||||
if err := bootstrapValidate(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -145,13 +160,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// generate install manifests
|
||||
logger.Generatef("generating manifests")
|
||||
manifest, err := generateInstallManifests(glPath, namespace, tmpDir, bootstrapManifestsPath)
|
||||
installManifest, err := generateInstallManifests(glPath.String(), namespace, tmpDir, bootstrapManifestsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage install manifests
|
||||
changed, err = repository.Commit(ctx, path.Join(glPath, namespace), "Add manifests")
|
||||
changed, err = repository.Commit(ctx, path.Join(glPath.String(), namespace), "Add manifests")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -172,7 +187,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if isInstall {
|
||||
// apply install manifests
|
||||
logger.Actionf("installing components in %s namespace", namespace)
|
||||
if err := applyInstallManifests(ctx, manifest, bootstrapComponents()); err != nil {
|
||||
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("install completed")
|
||||
@@ -225,12 +240,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// configure repo synchronization
|
||||
logger.Actionf("generating sync manifests")
|
||||
if err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, glPath, tmpDir, glInterval); err != nil {
|
||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, glPath.String(), tmpDir, glInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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.String(), namespace), "Add manifests"); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
@@ -241,7 +257,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// apply manifests and waiting for sync
|
||||
logger.Actionf("applying sync manifests")
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, glPath, tmpDir); err != nil {
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
@@ -133,7 +132,7 @@ func kubectlCheck(ctx context.Context, version string) bool {
|
||||
}
|
||||
|
||||
func kubernetesCheck(version string) bool {
|
||||
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||
cfg, err := utils.KubeConfig(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||
return false
|
||||
|
||||
@@ -37,7 +37,7 @@ command -v flux >/dev/null && . <(flux completion zsh) && compdef _flux flux
|
||||
or write a cached file in one of the completion directories in your ${fpath}:
|
||||
|
||||
echo "${fpath// /\n}" | grep -i completion
|
||||
flux completions zsh > _flux
|
||||
flux completion zsh > _flux
|
||||
|
||||
mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
|
||||
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
||||
|
||||
@@ -74,7 +74,7 @@ var createKsCmd = &cobra.Command{
|
||||
|
||||
var (
|
||||
ksSource flags.KustomizationSource
|
||||
ksPath string
|
||||
ksPath flags.SafeRelativePath = "./"
|
||||
ksPrune bool
|
||||
ksDependsOn []string
|
||||
ksValidation string
|
||||
@@ -88,7 +88,7 @@ var (
|
||||
|
||||
func init() {
|
||||
createKsCmd.Flags().Var(&ksSource, "source", ksSource.Description())
|
||||
createKsCmd.Flags().StringVar(&ksPath, "path", "./", "path to the directory containing a kustomization.yaml file")
|
||||
createKsCmd.Flags().Var(&ksPath, "path", "path to the directory containing a kustomization.yaml file")
|
||||
createKsCmd.Flags().BoolVar(&ksPrune, "prune", false, "enable garbage collection")
|
||||
createKsCmd.Flags().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
||||
createKsCmd.Flags().DurationVar(&ksHealthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
||||
@@ -110,7 +110,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if ksPath == "" {
|
||||
return fmt.Errorf("path is required")
|
||||
}
|
||||
if !strings.HasPrefix(ksPath, "./") {
|
||||
if !strings.HasPrefix(ksPath.String(), "./") {
|
||||
return fmt.Errorf("path must begin with ./")
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
Interval: metav1.Duration{
|
||||
Duration: interval,
|
||||
},
|
||||
Path: ksPath,
|
||||
Path: ksPath.String(),
|
||||
Prune: ksPrune,
|
||||
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
||||
Kind: ksSource.Kind,
|
||||
|
||||
@@ -64,6 +64,7 @@ var (
|
||||
installNetworkPolicy bool
|
||||
installArch = flags.Arch(defaults.Arch)
|
||||
installLogLevel = flags.LogLevel(defaults.LogLevel)
|
||||
installClusterDomain string
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -89,6 +90,7 @@ func init() {
|
||||
installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description())
|
||||
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", defaults.NetworkPolicy,
|
||||
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
||||
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", defaults.ClusterDomain, "internal cluster domain")
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
@@ -108,6 +110,10 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
components := append(installDefaultComponents, installExtraComponents...)
|
||||
|
||||
if err := utils.ValidateComponents(components); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := install.Options{
|
||||
BaseURL: installManifestsPath,
|
||||
Version: installVersion,
|
||||
@@ -122,6 +128,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
NotificationController: defaults.NotificationController,
|
||||
ManifestFile: fmt.Sprintf("%s.yaml", namespace),
|
||||
Timeout: timeout,
|
||||
ClusterDomain: installClusterDomain,
|
||||
}
|
||||
|
||||
if installManifestsPath == "" {
|
||||
|
||||
@@ -11,6 +11,7 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
|
||||
```
|
||||
--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")
|
||||
--cluster-domain string internal cluster domain (default "cluster.local")
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
-h, --help help for bootstrap
|
||||
|
||||
@@ -46,16 +46,16 @@ flux bootstrap github [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for github
|
||||
--hostname string GitHub hostname (default "github.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitHub user or organization name
|
||||
--path string repository path, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitHub repository name
|
||||
--ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
--team stringArray GitHub team to be given maintainer access
|
||||
-h, --help help for github
|
||||
--hostname string GitHub hostname (default "github.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitHub user or organization name
|
||||
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitHub repository name
|
||||
--ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
--team stringArray GitHub team to be given maintainer access
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -63,6 +63,7 @@ flux bootstrap github [flags]
|
||||
```
|
||||
--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")
|
||||
--cluster-domain string internal cluster domain (default "cluster.local")
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
--context string kubernetes context to use
|
||||
|
||||
@@ -43,15 +43,15 @@ flux bootstrap gitlab [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for gitlab
|
||||
--hostname string GitLab hostname (default "gitlab.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitLab user or group name
|
||||
--path string repository path, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitLab repository name
|
||||
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
-h, --help help for gitlab
|
||||
--hostname string GitLab hostname (default "gitlab.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitLab user or group name
|
||||
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitLab repository name
|
||||
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -59,6 +59,7 @@ flux bootstrap gitlab [flags]
|
||||
```
|
||||
--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")
|
||||
--cluster-domain string internal cluster domain (default "cluster.local")
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
--context string kubernetes context to use
|
||||
|
||||
@@ -25,7 +25,7 @@ command -v flux >/dev/null && . <(flux completion zsh) && compdef _flux flux
|
||||
or write a cached file in one of the completion directories in your ${fpath}:
|
||||
|
||||
echo "${fpath// /\n}" | grep -i completion
|
||||
flux completions zsh > _flux
|
||||
flux completion zsh > _flux
|
||||
|
||||
mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
|
||||
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
||||
|
||||
@@ -76,10 +76,10 @@ flux create helmrelease [name] [flags]
|
||||
-h, --help help for helmrelease
|
||||
--release-name string name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'
|
||||
--service-account string the name of the service account to impersonate when reconciling this HelmRelease
|
||||
--source helmChartSource source that contains the chart in the format '<kind>/<name>',where kind can be one of: (HelmRepository, GitRepository, Bucket)
|
||||
--source helmChartSource source that contains the chart in the format '<kind>/<name>', where kind must 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
|
||||
--values-from helmReleaseValuesFrom Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>',where kind can be one of: (Secret, ConfigMap)
|
||||
--values-from helmReleaseValuesFrom Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', where kind must be one of: (Secret, ConfigMap)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -50,10 +50,10 @@ flux create kustomization [name] [flags]
|
||||
--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 a kustomization.yaml file (default "./")
|
||||
--path safeRelativePath path to the directory containing a kustomization.yaml file (default ./)
|
||||
--prune enable garbage collection
|
||||
--service-account string the name of the service account to impersonate when reconciling this Kustomization
|
||||
--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
|
||||
--source kustomizationSource source that contains the Kubernetes manifests in the format '[<kind>/]<name>', where kind must be one of: (GitRepository, Bucket), if kind is not specified it defaults to GitRepository
|
||||
--target-namespace string overrides the namespace of all Kustomization objects reconciled by this Kustomization
|
||||
--validation string validate the manifests before applying them on the cluster, can be 'client' or 'server'
|
||||
```
|
||||
|
||||
@@ -32,6 +32,7 @@ flux install [flags]
|
||||
|
||||
```
|
||||
--arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
|
||||
--cluster-domain string internal cluster domain (default "cluster.local")
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
--dry-run only print the object that would be applied
|
||||
|
||||
52
docs/core-concepts/index.md
Normal file
52
docs/core-concepts/index.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Core Concepts
|
||||
|
||||
!!! note "Work in progress"
|
||||
This document is a work in progress.
|
||||
|
||||
These are some core concepts in Flux.
|
||||
|
||||
## GitOps
|
||||
|
||||
GitOps is a way of managing your infrastructure and applications so that whole system is described declaratively and version controlled (most likely in a Git repository), and having an automated process that ensures that the deployed environment matches the state specified in a repository.
|
||||
|
||||
For more information, take a look at ["What is GitOps?"](https://www.gitops.tech/#what-is-gitops).
|
||||
|
||||
## Sources
|
||||
|
||||
A *Source* defines the origin of a source and the requirements to obtain
|
||||
it (e.g. credentials, version selectors). For example, the latest `1.x` tag
|
||||
available from a Git repository over SSH.
|
||||
|
||||
Sources produce an artifact that is consumed by other Flux elements to perform
|
||||
actions, like applying the contents of the artifact on the cluster. A source
|
||||
may be shared by multiple consumers to deduplicate configuration and/or storage.
|
||||
|
||||
The origin of the source is checked for changes on a defined interval, if
|
||||
there is a newer version available that matches the criteria, a new artifact
|
||||
is produced.
|
||||
|
||||
All sources are specified as Custom Resources in a Kubernetes cluster, examples
|
||||
of sources are `GitRepository`, `HelmRepository` and `Bucket` resources.
|
||||
|
||||
For more information, take a look at [the source controller documentation](../components/source/source.md).
|
||||
|
||||
## Reconciliation
|
||||
|
||||
Reconciliation refers to ensuring that a given state(e.g application running in the cluster, infrasatructure) matches a desired state declaratively defined somewhere(e.g a git repository). There are various examples of these in flux e.g:
|
||||
|
||||
- HelmRelease reconciliation: ensures the state of the Helm release matches what is defined in the resource, performs a release if this is not the case (including revision changes of a HelmChart resource).
|
||||
- Bucket reconciliation: downloads and archives the contents of the declared bucket on a given interval and stores this as an artifact, records the observed revision of the artifact and the artifact itself in the status of resource.
|
||||
- [Kustomization](#kustomization) reconciliation: ensures the state of the application deployed on a cluster matches resources contained in a git repository.
|
||||
|
||||
## Kustomization
|
||||
|
||||
The kustomization represents an local set of Kubernetes resources that Flux is supposed to reconcile in the cluster. The reconciliation every one minute by default but this can be specified in the kustomization. If you make any changes to the cluster using `kubectl edit` or `kubectl patch`, it will be promptly reverted. You either suspend the reconciliation or push your changes to a Git repository.
|
||||
|
||||
For more information, take a look at [this documentation](../components/kustomize/kustomization.md).
|
||||
|
||||
## Bootstrap
|
||||
|
||||
The process of installing the Flux components in a complete GitOps way is called a bootstrap. The manifests are applied to the cluster, a `GitRepository` and `Kustomization` are created for the Flux components, and the manifests are pushed to an existing Git repository(or a new one is created). Flux can manage itself just as it manages other resources.
|
||||
The bootstrap is done using the `flux` CLI `flux bootstrap`.
|
||||
|
||||
For more information, take a look at [the documentation for the bootstrap command](../cmd/flux_bootstrap.md).
|
||||
@@ -257,6 +257,11 @@ The definition of the listed keys is as follows:
|
||||
You can read more about the available formats and limitations in
|
||||
the [Helm documentation](https://helm.sh/docs/intro/using_helm/#the-format-and-limitations-of---set).
|
||||
|
||||
!!! warning "`TargetPath` and JSON values"
|
||||
When using `TargetPath` in combination with a JSON string, the
|
||||
[limitations are the same as while using `helm`](https://github.com/helm/helm/issues/5618),
|
||||
and require you to escape the full JSON string (including `=`, `[`, `,`, `.`).
|
||||
|
||||
## Refer to values in `ConfigMaps` generated with Kustomize
|
||||
|
||||
It is possible to use Kustomize [ConfigMap generator](https://kubectl.docs.kubernetes.io/references/kustomize/configmapgenerator/)
|
||||
|
||||
360
docs/guides/image-update.md
Normal file
360
docs/guides/image-update.md
Normal file
@@ -0,0 +1,360 @@
|
||||
# Automate image updates to Git
|
||||
|
||||
This guide walks you through configuring container image scanning and deployment rollouts with Flux.
|
||||
|
||||
For a container image you can configure Flux to:
|
||||
|
||||
- scan the container registry and fetch the image tags
|
||||
- select the latest tag based on a semver range
|
||||
- replace the tag in Kubernetes manifests (YAML format)
|
||||
- checkout a branch, commit and push the changes to the remote Git repository
|
||||
- apply the changes in-cluster and rollout the container image
|
||||
|
||||
!!! warning "Alpha version"
|
||||
Note that the image update feature is currently alpha and, it only supports **semver** filters.
|
||||
In the future we plan to add support for other filtering options.
|
||||
Please see the [roadmap](../roadmap/index.md) for more details.
|
||||
|
||||
For production environments, this feature allows you to automatically deploy application patches
|
||||
(CVEs and bug fixes), and keep a record of all deployments in Git history.
|
||||
|
||||
For staging environments, this features allow you to deploy the latest prerelease of an application,
|
||||
without having to manually edit its deployment manifests in Git.
|
||||
|
||||
Production CI/CD workflow:
|
||||
|
||||
* DEV: push a bug fix to the app repository
|
||||
* DEV: bump the patch version and release e.g. `v1.0.1`
|
||||
* CI: build and push a container image tagged as `registry.domain/org/app:v1.0.1`
|
||||
* CD: pull the latest image metadata from the app registry (Flux image scanning)
|
||||
* CD: update the image tag in the app manifest to `v1.0.1` (Flux cluster to Git reconciliation)
|
||||
* CD: deploy `v1.0.1` to production clusters (Flux Git to cluster reconciliation)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need a Kubernetes cluster version 1.16 or newer and kubectl version 1.18.
|
||||
For a quick local test, you can use [Kubernetes kind](https://kind.sigs.k8s.io/docs/user/quick-start/).
|
||||
Any other Kubernetes setup will work as well.
|
||||
|
||||
In order to follow the guide you'll need a GitHub account and a
|
||||
[personal access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)
|
||||
that can create repositories (check all permissions under `repo`).
|
||||
|
||||
Export your GitHub personal access token and username:
|
||||
|
||||
```sh
|
||||
export GITHUB_TOKEN=<your-token>
|
||||
export GITHUB_USER=<your-username>
|
||||
```
|
||||
|
||||
## Install Flux
|
||||
|
||||
Install Flux with the image automation components:
|
||||
|
||||
```sh
|
||||
flux bootstrap github \
|
||||
--components-extra=image-reflector-controller,image-automation-controller \
|
||||
--owner=$GITHUB_USER \
|
||||
--repository=flux-image-updates \
|
||||
--branch=main \
|
||||
--path=clusters/my-cluster \
|
||||
--token-auth \
|
||||
--personal
|
||||
```
|
||||
|
||||
The bootstrap command creates a repository if one doesn't exist, and commits the manifests for the
|
||||
Flux components to the default branch at the specified path. It then configures the target cluster to
|
||||
synchronize with the specified path inside the repository.
|
||||
|
||||
!!! hint "GitLab and other Git platforms"
|
||||
You can install Flux and bootstrap repositories hosted on GitLab, BitBucket, Azure DevOps and
|
||||
any other Git provider that support SSH or token-based authentication.
|
||||
When using SSH, make sure the deploy key is configured with write access.
|
||||
Please see the [installation guide](installation.md) for more details.
|
||||
|
||||
## Deploy a demo app
|
||||
|
||||
We'll be using a tiny webapp called [podinfo](https://github.com/stefanprodan/podinfo) to
|
||||
showcase the image update feature.
|
||||
|
||||
Clone your repository with:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/$GITHUB_USER/flux-image-updates
|
||||
cd flux-image-updates
|
||||
```
|
||||
|
||||
Add the podinfo Kubernetes deployment file inside `cluster/my-cluster`:
|
||||
|
||||
```sh
|
||||
curl -sL https://raw.githubusercontent.com/stefanprodan/podinfo/5.0.0/kustomize/deployment.yaml \
|
||||
> ./clusters/my-cluster/podinfo-deployment.yaml
|
||||
```
|
||||
|
||||
Commit and push changes to main branch:
|
||||
|
||||
```sh
|
||||
git add -A && \
|
||||
git commit -m "add podinfo deployment" && \
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Tell Flux to pull and apply the changes or wait one minute for Flux to detect the changes on its own:
|
||||
|
||||
```sh
|
||||
flux reconcile kustomization flux-system --with-source
|
||||
```
|
||||
|
||||
Print the podinfo image deployed on your cluster:
|
||||
|
||||
```console
|
||||
$ kubectl get deployment/podinfo -oyaml | grep 'image:'
|
||||
image: ghcr.io/stefanprodan/podinfo:5.0.0
|
||||
```
|
||||
|
||||
## Configure image scanning
|
||||
|
||||
Create an `ImageRepository` to tell Flux which container registry to scan for new tags:
|
||||
|
||||
```sh
|
||||
flux create image repository podinfo \
|
||||
--image=ghcr.io/stefanprodan/podinfo \
|
||||
--interval=1m \
|
||||
--export > ./clusters/my-cluster/podinfo-registry.yaml
|
||||
```
|
||||
|
||||
The above command generates the following manifest:
|
||||
|
||||
```yaml
|
||||
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||
kind: ImageRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: flux-system
|
||||
spec:
|
||||
image: ghcr.io/stefanprodan/podinfo
|
||||
interval: 1m0s
|
||||
```
|
||||
|
||||
For private images, you can create a Kubernetes secret
|
||||
in the same namespace as the `ImageRepository` with
|
||||
`kubectl create secret docker-registry`. Then you can configure
|
||||
Flux to use the credentials by referencing the Kubernetes secret
|
||||
in the `ImageRepository`:
|
||||
|
||||
```yaml
|
||||
kind: ImageRepository
|
||||
spec:
|
||||
secretRef:
|
||||
name: regcred
|
||||
```
|
||||
|
||||
!!! hint "Storing secrets in Git"
|
||||
Note that if you want to store the image pull secret in Git, you can encrypt
|
||||
the manifest with [Mozilla SOPS](mozilla-sops.md) or [Sealed Secrets](sealed-secrets.md).
|
||||
|
||||
Create an `ImagePolicy` to tell Flux which semver range to use when filtering tags:
|
||||
|
||||
```sh
|
||||
flux create image policy podinfo \
|
||||
--image-ref=podinfo \
|
||||
--interval=1m \
|
||||
--semver=5.0.x \
|
||||
--export > ./clusters/my-cluster/podinfo-policy.yaml
|
||||
```
|
||||
|
||||
The above command generates the following manifest:
|
||||
|
||||
```yaml
|
||||
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||
kind: ImagePolicy
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: flux-system
|
||||
spec:
|
||||
imageRepositoryRef:
|
||||
name: podinfo
|
||||
policy:
|
||||
semver:
|
||||
range: 5.0.x
|
||||
```
|
||||
|
||||
!!! hint "semver ranges"
|
||||
A semver range that includes stable releases can be defined with
|
||||
`1.0.x` (patch versions only) or `>=1.0.0 <2.0.0` (minor and patch versions).
|
||||
If you want to include pre-release e.g. `1.0.0-rc.1`,
|
||||
you can define a range like: `>1.0.0-rc <2.0.0`.
|
||||
|
||||
Commit and push changes to main branch:
|
||||
|
||||
```sh
|
||||
git add -A && \
|
||||
git commit -m "add podinfo image scan" && \
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Tell Flux to pull and apply changes:
|
||||
|
||||
```sh
|
||||
flux reconcile kustomization flux-system --with-source
|
||||
```
|
||||
|
||||
Wait for Flux to fetch the image tag list from GitHub container registry:
|
||||
|
||||
```console
|
||||
$ flux get image repository podinfo
|
||||
NAME READY MESSAGE LAST SCAN
|
||||
podinfo True successful scan, found 13 tags 2020-12-13T17:51:48+02:00
|
||||
```
|
||||
|
||||
Find which image tag matches the policy semver range with:
|
||||
|
||||
```console
|
||||
$ flux get image policy podinfo
|
||||
NAME READY MESSAGE
|
||||
podinfo True Latest image tag for 'ghcr.io/stefanprodan/podinfo' resolved to: 5.0.3
|
||||
```
|
||||
|
||||
## Configure image updates
|
||||
|
||||
Edit the `podinfo-deploy.yaml` and add a maker to tell Flux which policy to use when updating the container image:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
containers:
|
||||
- name: podinfod
|
||||
image: ghcr.io/stefanprodan/podinfo:5.0.0 # {"$imagepolicy": "flux-system:podinfo"}
|
||||
```
|
||||
|
||||
Create an `ImageUpdateAutomation` to tell Flux which Git repository to write image updates to:
|
||||
|
||||
```sh
|
||||
flux create image update flux-system \
|
||||
--git-repo-ref=flux-system \
|
||||
--branch=main \
|
||||
--author-name=fluxcdbot \
|
||||
--author-email=fluxcdbot@users.noreply.github.com \
|
||||
--commit-template="[ci skip] update image" \
|
||||
--export > ./clusters/my-cluster/flux-system-automation.yaml
|
||||
```
|
||||
|
||||
The above command generates the following manifest:
|
||||
|
||||
```yaml
|
||||
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||
kind: ImageUpdateAutomation
|
||||
metadata:
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
spec:
|
||||
checkout:
|
||||
branch: main
|
||||
gitRepositoryRef:
|
||||
name: flux-system
|
||||
commit:
|
||||
authorEmail: fluxcdbot@users.noreply.github.com
|
||||
authorName: fluxcdbot
|
||||
messageTemplate: '[ci skip] update image'
|
||||
interval: 1m0s
|
||||
update:
|
||||
setters: {}
|
||||
```
|
||||
|
||||
Commit and push changes to main branch:
|
||||
|
||||
```sh
|
||||
git add -A && \
|
||||
git commit -m "add image updates automation" && \
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Note that the `ImageUpdateAutomation` runs all the policies found in its namespace at the specified interval.
|
||||
|
||||
Tell Flux to pull and apply changes:
|
||||
|
||||
```sh
|
||||
flux reconcile kustomization flux-system --with-source
|
||||
```
|
||||
|
||||
In a couple of seconds Flux will push a commit to your repository with
|
||||
the latest image tag that matches the podinfo policy:
|
||||
|
||||
```console
|
||||
$ git pull && cat clusters/my-cluster/podinfo-deployment.yaml | grep "image:"
|
||||
image: ghcr.io/stefanprodan/podinfo:5.0.3 # {"$imagepolicy": "flux-system:podinfo"}
|
||||
```
|
||||
|
||||
Wait for Flux to apply the latest commit on the cluster and verify that podinfo was updated to `5.0.3`:
|
||||
|
||||
```console
|
||||
$ watch "kubectl get deployment/podinfo -oyaml | grep 'image:'"
|
||||
image: ghcr.io/stefanprodan/podinfo:5.0.3
|
||||
```
|
||||
|
||||
## Configure image update for custom resources
|
||||
|
||||
Besides Kubernetes native kinds (Deployment, StatefulSet, DaemonSet, CronJob),
|
||||
Flux can be used to patch image tags in any Kubernetes custom resource stored in Git.
|
||||
|
||||
The image policy marker format is:
|
||||
|
||||
* `{"$imagepolicy": "<policy-namespace>:<policy-name>"}`
|
||||
* `{"$imagepolicy": "<policy-namespace>:<policy-name>:tag"}`
|
||||
|
||||
|
||||
`HelmRelease` example:
|
||||
|
||||
```yaml
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: default
|
||||
spec:
|
||||
values:
|
||||
image:
|
||||
repository: ghcr.io/stefanprodan/podinfo
|
||||
tag: 5.0.0 # {"$imagepolicy": "flux-system:podinfo:tag"}
|
||||
```
|
||||
|
||||
Tekton `Task` example:
|
||||
|
||||
```yaml
|
||||
apiVersion: tekton.dev/v1beta1
|
||||
kind: Task
|
||||
metadata:
|
||||
name: golang
|
||||
namespace: default
|
||||
spec:
|
||||
steps:
|
||||
- name: golang
|
||||
image: docker.io/golang:1.15.6 # {"$imagepolicy": "flux-system:golang"}
|
||||
```
|
||||
|
||||
Flux `Kustomization` example:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: default
|
||||
spec:
|
||||
images:
|
||||
- name: ghcr.io/stefanprodan/podinfo
|
||||
newName: ghcr.io/stefanprodan/podinfo
|
||||
newTag: 5.0.0 # {"$imagepolicy": "flux-system:podinfo:tag"}
|
||||
```
|
||||
|
||||
Kustomize config (`kustomization.yaml`) example:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- deployment.yaml
|
||||
images:
|
||||
- name: ghcr.io/stefanprodan/podinfo
|
||||
newName: ghcr.io/stefanprodan/podinfo
|
||||
newTag: 5.0.0 # {"$imagepolicy": "flux-system:podinfo:tag"}
|
||||
```
|
||||
@@ -57,6 +57,7 @@ You can choose what components to install and for which cluster with:
|
||||
```sh
|
||||
flux bootstrap <GIT-PROVIDER> \
|
||||
--components=source-controller,kustomize-controller,helm-controller,notification-controller \
|
||||
--components-extra=image-reflector-controller,image-automation-controller \
|
||||
--path=clusters/my-cluster \
|
||||
--version=latest
|
||||
```
|
||||
@@ -117,6 +118,12 @@ flux bootstrap github \
|
||||
--personal
|
||||
```
|
||||
|
||||
!!! hint "Deploy Key"
|
||||
The bootstrap command creates a ssh key which it stores as a secret in the
|
||||
Kubernetes cluster. The key is also used to create a deploy key in the GitHub
|
||||
repository. The new deploy key will be linked to the personal access token used
|
||||
to authenticate. Removing the personal access token will remove the deploy key.
|
||||
|
||||
Run the bootstrap for a repository owned by a GitHub organization:
|
||||
|
||||
```sh
|
||||
@@ -291,13 +298,16 @@ Create a `GitRepository` object on your cluster by specifying the SSH address of
|
||||
|
||||
```sh
|
||||
flux create source git flux-system \
|
||||
--url= ssh://<host>/<org>/my-repository \
|
||||
--url=ssh://git@<host>/<org>/<repository> \
|
||||
--ssh-key-algorithm=ecdsa \
|
||||
--ssh-ecdsa-curve=p521 \
|
||||
--branch=master \
|
||||
--interval=1m
|
||||
```
|
||||
|
||||
You will be prompted to add a deploy key to your repository.
|
||||
If you don't specify the SSH algorithm, then `flux` will generate an RSA 2048 bits key.
|
||||
|
||||
!!! hint "Azure DevOps"
|
||||
Azure DevOps requires a non-default Git implementation (`libgit2`) to be enabled, so that the Git v2 protocol is supported.
|
||||
Note that this implementation does not support shallow cloning, and it is therefore advised to only resort to this option if a
|
||||
@@ -305,20 +315,20 @@ flux create source git flux-system \
|
||||
Additionally, the current implementation of image automation does not support Azure DevOps as has no Git implementation with
|
||||
this protocol. This limitation will likely change in the future.
|
||||
|
||||
If you are using Azure DevOps you need to specify a different git implementation than the default:
|
||||
If you are using Azure DevOps you need to specify a different Git implementation than the default:
|
||||
|
||||
```sh
|
||||
flux create source git flux-system \
|
||||
--git-implementation=libgit2 \
|
||||
--url=git@ssh.dev.azure.com/v3/org/project/repository \
|
||||
--branch=master \
|
||||
--interval=1m
|
||||
```
|
||||
|
||||
```sh
|
||||
flux create source git flux-system \
|
||||
--url= ssh://<host>/<org>/my-repository \
|
||||
--ssh-key-algorithm=ecdsa \
|
||||
--ssh-ecdsa-curve=p521 \
|
||||
--branch=master \
|
||||
--interval=1m \
|
||||
--git-implementation=libgit2
|
||||
```
|
||||
|
||||
You will be prompted to add a deploy key to your repository.
|
||||
If you don't specify the SSH algorithm, then `flux` will generate an RSA 2048 bits key.
|
||||
Note that unlike `git`, Flux does not support the
|
||||
["shorter" scp-like syntax for the SSH protocol](https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_the_ssh_protocol)
|
||||
(e.g. `ssh.dev.azure.com:v3`).
|
||||
Use the [RFC 3986 compatible syntax](https://tools.ietf.org/html/rfc3986#section-3) instead: `ssh.dev.azure.com/v3`.
|
||||
|
||||
If your Git server supports basic auth, you can set the URL to HTTPS and specify the credentials with:
|
||||
|
||||
|
||||
@@ -84,20 +84,19 @@ Depending on what you want to do, some of the following bits might be your first
|
||||
- And if you are completely new to Flux v2 and 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
|
||||
### Upcoming Events
|
||||
- 11 Jan 2021 - [Helm + GitOps = ⚡️⚡️⚡️ with Scott Rigby](https://www.meetup.com/GitOps-Community/events/275348736/)
|
||||
|
||||
### Featured Talks
|
||||
- 14 Dec 2020 - [The Power of GitOps with Flux and Flagger (GitOps Hands-On) with Leigh Capili](https://youtu.be/cB7iXeNLteE)
|
||||
- 30 Nov 2020 - [The Power of GItOps with Flux 2 - Part 3 with Leigh Capili](https://youtu.be/N_K5g7o9JKg)
|
||||
- 24 Nov 2020 - [Flux CD v2 with GitOps Toolkit - Kubernetes Deployment and Sync Mechanism](https://youtu.be/R6OeIgb7lUI)
|
||||
- 19 Oct 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 2 with Leigh Capili](https://youtu.be/fC2YCxQRUwU)
|
||||
- 02 Nov 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 2 with Leigh Capili](https://youtu.be/fC2YCxQRUwU)
|
||||
- 28 Oct 2020 - [The Kubelist Podcast: Flux with Michael Bridgen](https://www.heavybit.com/library/podcasts/the-kubelist-podcast/ep-5-flux-with-michael-bridgen-of-weaveworks/)
|
||||
- 19 Oct 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 1 with Leigh Capili](https://youtu.be/0v5bjysXTL8)
|
||||
- 12 Oct 2020 - [Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
|
||||
- 4 Sep 2020 - [KubeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
|
||||
- 04 Sep 2020 - [KubeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
|
||||
- 25 June 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
|
||||
- 7 May 2020 - [GitOps Days - Community Special: GitOps Toolkit Experimentation with Stefan Prodan](https://youtu.be/WHzxunv4DKk?t=6521)
|
||||
- 07 May 2020 - [GitOps Days - Community Special: GitOps Toolkit Experimentation with Stefan Prodan](https://youtu.be/WHzxunv4DKk?t=6521)
|
||||
|
||||
### Upcoming Events
|
||||
|
||||
- 12-13 Nov 2020 - [GitOps Days EMEA](https://www.gitopsdays.com/) with talks and workshops on migrating to Flux v2 and Helm Controller
|
||||
- 19 Nov 2020 - [KubeCon NA: Progressive Delivery Techniques with Flagger and Flux v2 with Stefan Prodan](https://kccncna20.sched.com/event/1b04f8408b49976b843a5d0019cb8112)
|
||||
|
||||
We are looking forward to seeing you with us!
|
||||
We look forward to seeing you with us!
|
||||
|
||||
9
go.mod
9
go.mod
@@ -4,17 +4,18 @@ go 1.15
|
||||
|
||||
require (
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/fluxcd/helm-controller/api v0.4.3
|
||||
github.com/fluxcd/image-automation-controller/api v0.1.0
|
||||
github.com/cyphar/filepath-securejoin v0.2.2
|
||||
github.com/fluxcd/helm-controller/api v0.4.4
|
||||
github.com/fluxcd/image-automation-controller/api v0.2.0
|
||||
github.com/fluxcd/image-reflector-controller/api v0.1.0
|
||||
github.com/fluxcd/kustomize-controller/api v0.5.0
|
||||
github.com/fluxcd/kustomize-controller/api v0.5.3
|
||||
github.com/fluxcd/notification-controller/api v0.5.0
|
||||
github.com/fluxcd/pkg/apis/meta v0.5.0
|
||||
github.com/fluxcd/pkg/git v0.1.0
|
||||
github.com/fluxcd/pkg/runtime v0.4.0
|
||||
github.com/fluxcd/pkg/ssh v0.0.5
|
||||
github.com/fluxcd/pkg/untar v0.0.5
|
||||
github.com/fluxcd/source-controller/api v0.5.4
|
||||
github.com/fluxcd/source-controller/api v0.5.6
|
||||
github.com/google/go-containerregistry v0.2.0
|
||||
github.com/manifoldco/promptui v0.7.0
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
|
||||
25
go.sum
25
go.sum
@@ -6,7 +6,6 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM=
|
||||
cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
@@ -33,24 +32,20 @@ github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
||||
github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0=
|
||||
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||
github.com/Azure/go-autorest/autorest v0.10.2 h1:NuSF3gXetiHyUbVdneJMEVyPUYAe5wh+aN08JYAf1tI=
|
||||
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
@@ -60,7 +55,6 @@ github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQ
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
||||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
@@ -142,11 +136,12 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
@@ -180,14 +175,14 @@ 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.4.3 h1:uT4M8Zq/zSajfT9Z0m5yrhKJW9AObzOy5jHTGQFXi50=
|
||||
github.com/fluxcd/helm-controller/api v0.4.3/go.mod h1:H3fHkKJWcxPz38L1kxBX/MGm5v9XKzeoKZWNM7+dW2o=
|
||||
github.com/fluxcd/image-automation-controller/api v0.1.0 h1:XN/BbhCRoISEb828rfMt2fNe+3s4Zwc+BwhRi3K1SHA=
|
||||
github.com/fluxcd/image-automation-controller/api v0.1.0/go.mod h1:DHjFNvA+kJlSm7cbTaG+Z5smVjMjLw7xzlJc9brP0zY=
|
||||
github.com/fluxcd/helm-controller/api v0.4.4 h1:WYf7KokS3ALeE1F2SrviMHtEBGfznP7DkxXwo5pP7f8=
|
||||
github.com/fluxcd/helm-controller/api v0.4.4/go.mod h1:H3fHkKJWcxPz38L1kxBX/MGm5v9XKzeoKZWNM7+dW2o=
|
||||
github.com/fluxcd/image-automation-controller/api v0.2.0 h1:1diY89HhpbseqENkd9DLgqsDnkTGZmVDjX5mucNOMr8=
|
||||
github.com/fluxcd/image-automation-controller/api v0.2.0/go.mod h1:DHjFNvA+kJlSm7cbTaG+Z5smVjMjLw7xzlJc9brP0zY=
|
||||
github.com/fluxcd/image-reflector-controller/api v0.1.0 h1:wlqwCy4sZMbbdrgSY9Fd0mfy55kk7dS4Z+icrDlkmmg=
|
||||
github.com/fluxcd/image-reflector-controller/api v0.1.0/go.mod h1:u7vnULekPHXAZgJ35lqCjV2MaJVN0xbD+qt9X9TVCMs=
|
||||
github.com/fluxcd/kustomize-controller/api v0.5.0 h1:HUyB17yxr0wxABOCQTXD9FAc3p4REjiDxvpdEr5X/sg=
|
||||
github.com/fluxcd/kustomize-controller/api v0.5.0/go.mod h1:8Z52j63kRf+NjtVmiJFvI8xLje3ncFTs/uMxcrEJPIA=
|
||||
github.com/fluxcd/kustomize-controller/api v0.5.3 h1:zZiWFBQkNytLffOOJJttGFQk7BIUHpT2yoOCG4nyqII=
|
||||
github.com/fluxcd/kustomize-controller/api v0.5.3/go.mod h1:8Z52j63kRf+NjtVmiJFvI8xLje3ncFTs/uMxcrEJPIA=
|
||||
github.com/fluxcd/notification-controller/api v0.5.0 h1:xKKFnPVsYl2+GEjgKz5a5Mq6vmy+H2q9d2lJ2jmWJZs=
|
||||
github.com/fluxcd/notification-controller/api v0.5.0/go.mod h1:yLd+nrCJUThSkt4U+LLv8TRxqZdR11+gE1S2/bhgqmE=
|
||||
github.com/fluxcd/pkg/apis/meta v0.5.0 h1:FaU++mQY0g4sVVl+hG+vk0CXBLbb4EVfRuzs3IjLXvo=
|
||||
@@ -200,8 +195,8 @@ 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/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
|
||||
github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw=
|
||||
github.com/fluxcd/source-controller/api v0.5.4 h1:V3SzxbBDLULPWBD9nObJqXovCU5TZka8R/vrDIgomj4=
|
||||
github.com/fluxcd/source-controller/api v0.5.4/go.mod h1:/mpW0EM2dUnRey6rffqsSmgNkSAYm+zq9i0GfmTO7I0=
|
||||
github.com/fluxcd/source-controller/api v0.5.6 h1:Nj7WebOP8nTTA/yDPwszyjzW5oYI5tVhS/8XWOT+2wk=
|
||||
github.com/fluxcd/source-controller/api v0.5.6/go.mod h1:/mpW0EM2dUnRey6rffqsSmgNkSAYm+zq9i0GfmTO7I0=
|
||||
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/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
||||
|
||||
45
internal/flags/arch_test.go
Normal file
45
internal/flags/arch_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestArch_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", "amd64", "amd64", false},
|
||||
{"unsupported", "unsupported", "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var a Arch
|
||||
if err := a.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := a.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,10 @@ func (d *DecryptionProvider) String() string {
|
||||
}
|
||||
|
||||
func (d *DecryptionProvider) Set(str string) error {
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return fmt.Errorf("no decryption provider given, must be one of: %s",
|
||||
strings.Join(supportedDecryptionProviders, ", "))
|
||||
}
|
||||
if !utils.ContainsItemString(supportedDecryptionProviders, str) {
|
||||
return fmt.Errorf("unsupported decryption provider '%s', must be one of: %s",
|
||||
str, strings.Join(supportedDecryptionProviders, ", "))
|
||||
|
||||
45
internal/flags/decryption_provider_test.go
Normal file
45
internal/flags/decryption_provider_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecryptionProvider_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", "sops", "sops", false},
|
||||
{"unsupported", "unsupported", "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var p DecryptionProvider
|
||||
if err := p.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := p.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func (c *ECDSACurve) Set(str string) error {
|
||||
*c = ECDSACurve{v}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unsupported curve '%s', should be one of: %s", str, strings.Join(ecdsaCurves(), ", "))
|
||||
return fmt.Errorf("unsupported curve '%s', must be one of: %s", str, strings.Join(ecdsaCurves(), ", "))
|
||||
}
|
||||
|
||||
func (c *ECDSACurve) Type() string {
|
||||
|
||||
45
internal/flags/ecdsa_curve_test.go
Normal file
45
internal/flags/ecdsa_curve_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestECDSACurve_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", "p256", "p256", false},
|
||||
{"unsupported", "unsupported", "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var c ECDSACurve
|
||||
if err := c.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := c.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var supportedHelmChartSourceKinds = []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind, sourcev1.BucketKind}
|
||||
@@ -31,42 +32,43 @@ type HelmChartSource struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (h *HelmChartSource) String() string {
|
||||
if h.Name == "" {
|
||||
func (s *HelmChartSource) String() string {
|
||||
if s.Name == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", h.Kind, h.Name)
|
||||
return fmt.Sprintf("%s/%s", s.Kind, s.Name)
|
||||
}
|
||||
|
||||
func (h *HelmChartSource) Set(str string) error {
|
||||
func (s *HelmChartSource) Set(str string) error {
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return fmt.Errorf("no helm chart source given, please specify %s",
|
||||
h.Description())
|
||||
s.Description())
|
||||
}
|
||||
|
||||
sourceKind, sourceName := utils.ParseObjectKindName(str)
|
||||
if sourceKind == "" {
|
||||
if sourceKind == "" || sourceName == "" {
|
||||
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",
|
||||
cleanSourceKind, ok := utils.ContainsEqualFoldItemString(supportedHelmChartSourceKinds, sourceKind)
|
||||
if !ok {
|
||||
return fmt.Errorf("source kind '%s' is not supported, must be one of: %s",
|
||||
sourceKind, strings.Join(supportedHelmChartSourceKinds, ", "))
|
||||
}
|
||||
|
||||
h.Name = sourceName
|
||||
h.Kind = sourceKind
|
||||
s.Name = sourceName
|
||||
s.Kind = cleanSourceKind
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HelmChartSource) Type() string {
|
||||
func (s *HelmChartSource) Type() string {
|
||||
return "helmChartSource"
|
||||
}
|
||||
|
||||
func (h *HelmChartSource) Description() string {
|
||||
func (s *HelmChartSource) Description() string {
|
||||
return fmt.Sprintf(
|
||||
"source that contains the chart in the format '<kind>/<name>',"+
|
||||
"where kind can be one of: (%s)",
|
||||
"source that contains the chart in the format '<kind>/<name>', "+
|
||||
"where kind must be one of: (%s)",
|
||||
strings.Join(supportedHelmChartSourceKinds, ", "),
|
||||
)
|
||||
}
|
||||
|
||||
51
internal/flags/helm_chart_source_test.go
Normal file
51
internal/flags/helm_chart_source_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
)
|
||||
|
||||
func TestHelmChartSource_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", fmt.Sprintf("%s/foo", sourcev1.HelmRepositoryKind), fmt.Sprintf("%s/foo", sourcev1.HelmRepositoryKind), false},
|
||||
{"lower case kind", "helmrepository/foo", fmt.Sprintf("%s/foo", sourcev1.HelmRepositoryKind), false},
|
||||
{"unsupported", "Unsupported/kind", "", true},
|
||||
{"invalid format", sourcev1.HelmRepositoryKind, "", true},
|
||||
{"missing name", fmt.Sprintf("%s/", sourcev1.HelmRepositoryKind), "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var s HelmChartSource
|
||||
if err := s.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := s.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -30,42 +30,43 @@ type HelmReleaseValuesFrom struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (h *HelmReleaseValuesFrom) String() string {
|
||||
if h.Name == "" {
|
||||
func (v *HelmReleaseValuesFrom) String() string {
|
||||
if v.Name == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", h.Kind, h.Name)
|
||||
return fmt.Sprintf("%s/%s", v.Kind, v.Name)
|
||||
}
|
||||
|
||||
func (h *HelmReleaseValuesFrom) Set(str string) error {
|
||||
func (v *HelmReleaseValuesFrom) Set(str string) error {
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return fmt.Errorf("no values given, please specify %s",
|
||||
h.Description())
|
||||
v.Description())
|
||||
}
|
||||
|
||||
sourceKind, sourceName := utils.ParseObjectKindName(str)
|
||||
if sourceKind == "" {
|
||||
return fmt.Errorf("invalid Kubernetes object reference '%s', must be in format <kind>/<name>", str)
|
||||
}
|
||||
if !utils.ContainsItemString(supportedHelmReleaseValuesFromKinds, sourceKind) {
|
||||
return fmt.Errorf("reference kind '%s' is not supported, can be one of: %s",
|
||||
cleanSourceKind, ok := utils.ContainsEqualFoldItemString(supportedHelmReleaseValuesFromKinds, sourceKind)
|
||||
if !ok {
|
||||
return fmt.Errorf("reference kind '%s' is not supported, must be one of: %s",
|
||||
sourceKind, strings.Join(supportedHelmReleaseValuesFromKinds, ", "))
|
||||
}
|
||||
|
||||
h.Name = sourceName
|
||||
h.Kind = sourceKind
|
||||
v.Name = sourceName
|
||||
v.Kind = cleanSourceKind
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HelmReleaseValuesFrom) Type() string {
|
||||
func (v *HelmReleaseValuesFrom) Type() string {
|
||||
return "helmReleaseValuesFrom"
|
||||
}
|
||||
|
||||
func (h *HelmReleaseValuesFrom) Description() string {
|
||||
func (v *HelmReleaseValuesFrom) Description() string {
|
||||
return fmt.Sprintf(
|
||||
"Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>',"+
|
||||
"where kind can be one of: (%s)",
|
||||
"Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', "+
|
||||
"where kind must be one of: (%s)",
|
||||
strings.Join(supportedHelmReleaseValuesFromKinds, ", "),
|
||||
)
|
||||
}
|
||||
|
||||
47
internal/flags/helm_release_values_test.go
Normal file
47
internal/flags/helm_release_values_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHelmReleaseValuesFrom_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", "Secret/foo", "Secret/foo", false},
|
||||
{"lower case kind", "secret/foo", "Secret/foo", false},
|
||||
{"unsupported", "Unsupported/kind", "", true},
|
||||
{"invalid format", "Secret", "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var h HelmReleaseValuesFrom
|
||||
if err := h.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := h.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var supportedKustomizationSourceKinds = []string{sourcev1.GitRepositoryKind, sourcev1.BucketKind}
|
||||
@@ -31,42 +32,49 @@ type KustomizationSource struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (k *KustomizationSource) String() string {
|
||||
if k.Name == "" {
|
||||
func (s *KustomizationSource) String() string {
|
||||
if s.Name == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", k.Kind, k.Name)
|
||||
return fmt.Sprintf("%s/%s", s.Kind, s.Name)
|
||||
}
|
||||
|
||||
func (k *KustomizationSource) Set(str string) error {
|
||||
func (s *KustomizationSource) Set(str string) error {
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return fmt.Errorf("no kustomization source given, please specify %s",
|
||||
k.Description())
|
||||
return fmt.Errorf("no Kustomization source given, please specify %s",
|
||||
s.Description())
|
||||
}
|
||||
|
||||
sourceKind, sourceName := utils.ParseObjectKindName(str)
|
||||
if sourceName == "" {
|
||||
return fmt.Errorf("no name given for source of kind '%s'", sourceKind)
|
||||
}
|
||||
if sourceKind == "" {
|
||||
if utils.ContainsItemString(supportedKustomizationSourceKinds, sourceName) {
|
||||
return fmt.Errorf("no name given for source of kind '%s'", sourceName)
|
||||
}
|
||||
sourceKind = sourcev1.GitRepositoryKind
|
||||
}
|
||||
if !utils.ContainsItemString(supportedKustomizationSourceKinds, sourceKind) {
|
||||
return fmt.Errorf("source kind '%s' is not supported, can be one of: %s",
|
||||
cleanSourceKind, ok := utils.ContainsEqualFoldItemString(supportedKustomizationSourceKinds, sourceKind)
|
||||
if !ok {
|
||||
return fmt.Errorf("source kind '%s' is not supported, must be one of: %s",
|
||||
sourceKind, strings.Join(supportedKustomizationSourceKinds, ", "))
|
||||
}
|
||||
|
||||
k.Name = sourceName
|
||||
k.Kind = sourceKind
|
||||
s.Name = sourceName
|
||||
s.Kind = cleanSourceKind
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *KustomizationSource) Type() string {
|
||||
func (s *KustomizationSource) Type() string {
|
||||
return "kustomizationSource"
|
||||
}
|
||||
|
||||
func (k *KustomizationSource) Description() string {
|
||||
func (s *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",
|
||||
"source that contains the Kubernetes manifests in the format '[<kind>/]<name>', "+
|
||||
"where kind must be one of: (%s), if kind is not specified it defaults to GitRepository",
|
||||
strings.Join(supportedKustomizationSourceKinds, ", "),
|
||||
)
|
||||
}
|
||||
|
||||
51
internal/flags/kustomization_source_test.go
Normal file
51
internal/flags/kustomization_source_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
)
|
||||
|
||||
func TestKustomizationSource_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", fmt.Sprintf("%s/foo", sourcev1.GitRepositoryKind), fmt.Sprintf("%s/foo", sourcev1.GitRepositoryKind), false},
|
||||
{"default kind", "foo", fmt.Sprintf("%s/foo", sourcev1.GitRepositoryKind), false},
|
||||
{"lower case kind", "gitrepository/foo", fmt.Sprintf("%s/foo", sourcev1.GitRepositoryKind), false},
|
||||
{"unsupported", "Unsupported/kind", "", true},
|
||||
{"missing name", fmt.Sprintf("%s/", sourcev1.GitRepositoryKind), "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var s KustomizationSource
|
||||
if err := s.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := s.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
45
internal/flags/log_level_test.go
Normal file
45
internal/flags/log_level_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLogLevel_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", "info", "info", false},
|
||||
{"unsupported", "unsupported", "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var l LogLevel
|
||||
if err := l.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := l.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
45
internal/flags/public_key_algorithm_test.go
Normal file
45
internal/flags/public_key_algorithm_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPublicKeyAlgorithm_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", "rsa", "rsa", false},
|
||||
{"unsupported", "unsupported", "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var a PublicKeyAlgorithm
|
||||
if err := a.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := a.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,8 @@ func (b *RSAKeyBits) Set(str string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bits%8 != 0 {
|
||||
return fmt.Errorf("RSA key bit size should be a multiples of 8")
|
||||
if bits == 0 || bits%8 != 0 {
|
||||
return fmt.Errorf("RSA key bit size must be a multiples of 8")
|
||||
}
|
||||
*b = RSAKeyBits(bits)
|
||||
return nil
|
||||
|
||||
46
internal/flags/rsa_key_bits_test.go
Normal file
46
internal/flags/rsa_key_bits_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRSAKeyBits_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", "4096", "4096", false},
|
||||
{"empty (default)", "", "2048", false},
|
||||
{"unsupported", "0", "0", true},
|
||||
{"unsupported", "123", "0", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var b RSAKeyBits
|
||||
if err := b.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := b.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
50
internal/flags/safe_relative_path.go
Normal file
50
internal/flags/safe_relative_path.go
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
)
|
||||
|
||||
type SafeRelativePath string
|
||||
|
||||
func (p *SafeRelativePath) String() string {
|
||||
return string(*p)
|
||||
}
|
||||
|
||||
func (p *SafeRelativePath) Set(str string) error {
|
||||
// The result of secure joining on a relative base dir is a flattened relative path.
|
||||
cleanP, err := securejoin.SecureJoin("./", strings.TrimSpace(str))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid relative path '%s': %w", cleanP, err)
|
||||
}
|
||||
// NB: required, as a secure join of "./" will result in "."
|
||||
cleanP = fmt.Sprintf("./%s", strings.TrimPrefix(cleanP, "."))
|
||||
*p = SafeRelativePath(cleanP)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *SafeRelativePath) Type() string {
|
||||
return "safeRelativePath"
|
||||
}
|
||||
|
||||
func (p *SafeRelativePath) Description() string {
|
||||
return fmt.Sprintf("secure relative path")
|
||||
}
|
||||
49
internal/flags/safe_relative_path_test.go
Normal file
49
internal/flags/safe_relative_path_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRelativePath_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"relative path", "./foo", "./foo", false},
|
||||
{"relative path", "foo", "./foo", false},
|
||||
{"traversing relative path", "./foo/../bar", "./bar", false},
|
||||
{"absolute path", "/foo", "./foo", false},
|
||||
{"traversing absolute path", "/foo/../bar", "./bar", false},
|
||||
{"traversing overflowing absolute path", "/foo/../../../bar", "./bar", false},
|
||||
{"empty", "", "./", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var p SafeRelativePath
|
||||
if err := p.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := p.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -28,29 +28,28 @@ var supportedSourceBucketProviders = []string{sourcev1.GenericBucketProvider, so
|
||||
|
||||
type SourceBucketProvider string
|
||||
|
||||
func (s *SourceBucketProvider) String() string {
|
||||
return string(*s)
|
||||
func (p *SourceBucketProvider) String() string {
|
||||
return string(*p)
|
||||
}
|
||||
|
||||
func (s *SourceBucketProvider) Set(str string) error {
|
||||
func (p *SourceBucketProvider) Set(str string) error {
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return fmt.Errorf("no source bucket provider given, please specify %s",
|
||||
s.Description())
|
||||
p.Description())
|
||||
}
|
||||
|
||||
if !utils.ContainsItemString(supportedSourceBucketProviders, str) {
|
||||
return fmt.Errorf("source bucket provider '%s' is not supported, can be one of: %v",
|
||||
return fmt.Errorf("source bucket provider '%s' is not supported, must be one of: %v",
|
||||
str, strings.Join(supportedSourceBucketProviders, ", "))
|
||||
}
|
||||
|
||||
*p = SourceBucketProvider(str)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SourceBucketProvider) Type() string {
|
||||
func (p *SourceBucketProvider) Type() string {
|
||||
return "sourceBucketProvider"
|
||||
}
|
||||
|
||||
func (s *SourceBucketProvider) Description() string {
|
||||
func (p *SourceBucketProvider) Description() string {
|
||||
return fmt.Sprintf(
|
||||
"the S3 compatible storage provider name, available options are: (%s)",
|
||||
strings.Join(supportedSourceBucketProviders, ", "),
|
||||
|
||||
47
internal/flags/source_bucket_provider_test.go
Normal file
47
internal/flags/source_bucket_provider_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
)
|
||||
|
||||
func TestSourceBucketProvider_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
str string
|
||||
expect string
|
||||
expectErr bool
|
||||
}{
|
||||
{"supported", sourcev1.GenericBucketProvider, sourcev1.GenericBucketProvider, false},
|
||||
{"unsupported", "unsupported", "", true},
|
||||
{"empty", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var s SourceBucketProvider
|
||||
if err := s.Set(tt.str); (err != nil) != tt.expectErr {
|
||||
t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
|
||||
}
|
||||
if str := s.String(); str != tt.expect {
|
||||
t.Errorf("Set() = %v, expect %v", str, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -34,6 +35,7 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
@@ -134,7 +136,7 @@ func ExecTemplate(obj interface{}, tmpl, filename string) error {
|
||||
return file.Sync()
|
||||
}
|
||||
|
||||
func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error) {
|
||||
func KubeConfig(kubeConfigPath string, kubeContext string) (*rest.Config, error) {
|
||||
configFiles := SplitKubeConfigPath(kubeConfigPath)
|
||||
configOverrides := clientcmd.ConfigOverrides{}
|
||||
|
||||
@@ -147,6 +149,15 @@ func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error
|
||||
&configOverrides,
|
||||
).ClientConfig()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kubernetes configuration load failed: %w", err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error) {
|
||||
cfg, err := KubeConfig(kubeConfigPath, kubeContext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||
}
|
||||
@@ -230,6 +241,15 @@ func ContainsItemString(s []string, e string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func ContainsEqualFoldItemString(s []string, e string) (string, bool) {
|
||||
for _, a := range s {
|
||||
if strings.EqualFold(a, e) {
|
||||
return a, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func ParseObjectKindName(input string) (string, string) {
|
||||
kind := ""
|
||||
name := input
|
||||
@@ -237,7 +257,6 @@ func ParseObjectKindName(input string) (string, string) {
|
||||
if len(parts) == 2 {
|
||||
kind, name = parts[0], parts[1]
|
||||
}
|
||||
|
||||
return kind, name
|
||||
}
|
||||
|
||||
@@ -362,3 +381,15 @@ func PrintTable(writer io.Writer, header []string, rows [][]string) {
|
||||
table.AppendBulk(rows)
|
||||
table.Render()
|
||||
}
|
||||
|
||||
func ValidateComponents(components []string) error {
|
||||
defaults := install.MakeDefaultOptions()
|
||||
bootstrapAllComponents := append(defaults.Components, defaults.ComponentsExtra...)
|
||||
for _, component := range components {
|
||||
if !ContainsItemString(bootstrapAllComponents, component) {
|
||||
return fmt.Errorf("component %s is not available", component)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- https://github.com/fluxcd/helm-controller/archive/v0.4.3.zip//helm-controller-0.4.3/config/crd
|
||||
- https://github.com/fluxcd/helm-controller/archive/v0.4.3.zip//helm-controller-0.4.3/config/manager
|
||||
- https://github.com/fluxcd/helm-controller/archive/v0.4.4.zip//helm-controller-0.4.4/config/crd
|
||||
- https://github.com/fluxcd/helm-controller/archive/v0.4.4.zip//helm-controller-0.4.4/config/manager
|
||||
patchesJson6902:
|
||||
- target:
|
||||
group: apps
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- https://github.com/fluxcd/image-automation-controller/archive/v0.1.0.zip//image-automation-controller-0.1.0/config/crd
|
||||
- https://github.com/fluxcd/image-automation-controller/archive/v0.1.0.zip//image-automation-controller-0.1.0/config/manager
|
||||
- https://github.com/fluxcd/image-automation-controller/archive/v0.2.0.zip//image-automation-controller-0.2.0/config/crd
|
||||
- https://github.com/fluxcd/image-automation-controller/archive/v0.2.0.zip//image-automation-controller-0.2.0/config/manager
|
||||
patchesJson6902:
|
||||
- target:
|
||||
group: apps
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- https://github.com/fluxcd/kustomize-controller/archive/v0.5.0.zip//kustomize-controller-0.5.0/config/crd
|
||||
- https://github.com/fluxcd/kustomize-controller/archive/v0.5.0.zip//kustomize-controller-0.5.0/config/manager
|
||||
- https://github.com/fluxcd/kustomize-controller/archive/v0.5.3.zip//kustomize-controller-0.5.3/config/crd
|
||||
- https://github.com/fluxcd/kustomize-controller/archive/v0.5.3.zip//kustomize-controller-0.5.3/config/manager
|
||||
patchesJson6902:
|
||||
- target:
|
||||
group: apps
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- https://github.com/fluxcd/source-controller/archive/v0.5.4.zip//source-controller-0.5.4/config/crd
|
||||
- https://github.com/fluxcd/source-controller/archive/v0.5.4.zip//source-controller-0.5.4/config/manager
|
||||
- https://github.com/fluxcd/source-controller/archive/v0.5.6.zip//source-controller-0.5.6/config/crd
|
||||
- https://github.com/fluxcd/source-controller/archive/v0.5.6.zip//source-controller-0.5.6/config/manager
|
||||
patchesJson6902:
|
||||
- target:
|
||||
group: apps
|
||||
|
||||
@@ -37,12 +37,14 @@ markdown_extensions:
|
||||
- pymdownx.tilde
|
||||
- pymdownx.progressbar
|
||||
- pymdownx.tasklist
|
||||
- pymdownx.superfences
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
||||
|
||||
nav:
|
||||
- Introduction: index.md
|
||||
- Core Concepts: core-concepts/index.md
|
||||
- Get Started: get-started/index.md
|
||||
- Guides:
|
||||
- Installation: guides/installation.md
|
||||
@@ -52,6 +54,7 @@ nav:
|
||||
- Monitoring with Prometheus: guides/monitoring.md
|
||||
- Sealed Secrets: guides/sealed-secrets.md
|
||||
- Mozilla SOPS: guides/mozilla-sops.md
|
||||
- Automate image updates to Git: guides/image-update.md
|
||||
- Migration:
|
||||
- Migrate from Flux v1: guides/flux-v1-migration.md
|
||||
- Migrate from the Helm Operator: guides/helm-operator-migration.md
|
||||
|
||||
@@ -24,6 +24,8 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||
)
|
||||
|
||||
@@ -40,7 +42,10 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
output := path.Join(tmpDir, options.ManifestFile)
|
||||
output, err := securejoin.SecureJoin(tmpDir, options.ManifestFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(options.BaseURL, "http") {
|
||||
if err := build(options.BaseURL, output); err != nil {
|
||||
|
||||
@@ -23,6 +23,7 @@ type Options struct {
|
||||
Version string
|
||||
Namespace string
|
||||
Components []string
|
||||
ComponentsExtra []string
|
||||
EventsAddr string
|
||||
Registry string
|
||||
ImagePullSecret string
|
||||
@@ -34,6 +35,7 @@ type Options struct {
|
||||
ManifestFile string
|
||||
Timeout time.Duration
|
||||
TargetPath string
|
||||
ClusterDomain string
|
||||
}
|
||||
|
||||
func MakeDefaultOptions() Options {
|
||||
@@ -41,6 +43,7 @@ func MakeDefaultOptions() Options {
|
||||
Version: "latest",
|
||||
Namespace: "flux-system",
|
||||
Components: []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"},
|
||||
ComponentsExtra: []string{"image-reflector-controller", "image-automation-controller"},
|
||||
EventsAddr: "",
|
||||
Registry: "ghcr.io/fluxcd",
|
||||
ImagePullSecret: "",
|
||||
@@ -53,6 +56,7 @@ func MakeDefaultOptions() Options {
|
||||
ManifestFile: "gotk-components.yaml",
|
||||
Timeout: time.Minute,
|
||||
TargetPath: "",
|
||||
ClusterDomain: "cluster.local",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ var kustomizationTmpl = `---
|
||||
{{- $registry := .Registry }}
|
||||
{{- $arch := .Arch }}
|
||||
{{- $logLevel := .LogLevel }}
|
||||
{{- $clusterDomain := .ClusterDomain }}
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: {{.Namespace}}
|
||||
@@ -67,6 +68,25 @@ patchesJson6902:
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/args/1
|
||||
value: --log-level={{$logLevel}}
|
||||
{{- else if eq $component "source-controller" }}
|
||||
- target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: {{$component}}
|
||||
patch: |-
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/args/0
|
||||
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}}
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/args/6
|
||||
value: --storage-adv-addr=source-controller.$(RUNTIME_NAMESPACE).svc.{{$clusterDomain}}.
|
||||
{{- else }}
|
||||
- target:
|
||||
group: apps
|
||||
|
||||
@@ -20,8 +20,9 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
)
|
||||
|
||||
// Manifest holds the data of a multi-doc YAML
|
||||
@@ -36,14 +37,17 @@ type Manifest struct {
|
||||
// If the file does not exist, WriteFile creates it with permissions perm,
|
||||
// otherwise WriteFile overwrites the file, without changing permissions.
|
||||
func (m *Manifest) WriteFile(rootDir string) (string, error) {
|
||||
if err := os.MkdirAll(path.Join(rootDir, filepath.Dir(m.Path)), os.ModePerm); err != nil {
|
||||
output, err := securejoin.SecureJoin(rootDir, m.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(output), os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("unable to create dir, error: %w", err)
|
||||
}
|
||||
|
||||
filePath := path.Join(rootDir, m.Path)
|
||||
if err := ioutil.WriteFile(filePath, []byte(m.Content), os.ModePerm); err != nil {
|
||||
if err := ioutil.WriteFile(output, []byte(m.Content), os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("unable to write file, error: %w", err)
|
||||
}
|
||||
|
||||
return filePath, nil
|
||||
return output, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user