Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
3
.github/workflows/bootstrap.yaml
vendored
3
.github/workflows/bootstrap.yaml
vendored
@@ -49,8 +49,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
- name: uninstall
|
- name: uninstall
|
||||||
run: |
|
run: |
|
||||||
./bin/flux suspend kustomization flux-system
|
./bin/flux uninstall --resources --crds -s --timeout=10m
|
||||||
./bin/flux uninstall --resources --crds -s
|
|
||||||
- name: bootstrap reinstall
|
- name: bootstrap reinstall
|
||||||
run: |
|
run: |
|
||||||
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
./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=podinfo \
|
||||||
--chart-version="5.0.x" \
|
--chart-version="5.0.x" \
|
||||||
--service-account=dev-team
|
--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
|
- name: flux check
|
||||||
run: |
|
run: |
|
||||||
./bin/flux check
|
./bin/flux check
|
||||||
- name: flux uninstall
|
- name: flux uninstall
|
||||||
run: |
|
run: |
|
||||||
./bin/flux uninstall --crds --silent
|
./bin/flux uninstall --crds --silent --timeout=10m
|
||||||
- name: Debug failure
|
- name: Debug failure
|
||||||
if: failure()
|
if: failure()
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -10,6 +10,16 @@ Usage:
|
|||||||
run: flux -v
|
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
|
### Automate Flux updates
|
||||||
|
|
||||||
Example workflow for updating Flux's components generated with `flux bootstrap --arch=amd64 --path=clusters/production`:
|
Example workflow for updating Flux's components generated with `flux bootstrap --arch=amd64 --path=clusters/production`:
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ curl -sL $BIN_URL | tar xz
|
|||||||
|
|
||||||
# Copy binary to GitHub runner
|
# Copy binary to GitHub runner
|
||||||
mkdir -p $GITHUB_WORKSPACE/bin
|
mkdir -p $GITHUB_WORKSPACE/bin
|
||||||
cp ./flux $GITHUB_WORKSPACE/bin
|
mv ./flux $GITHUB_WORKSPACE/bin
|
||||||
chmod +x $GITHUB_WORKSPACE/bin/flux
|
chmod +x $GITHUB_WORKSPACE/bin/flux
|
||||||
|
|
||||||
# Print version
|
# Print version
|
||||||
|
|||||||
@@ -135,12 +135,11 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
|||||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
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)
|
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 {
|
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
|
||||||
@@ -158,7 +157,7 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
|
|||||||
return nil
|
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{
|
opts := sync.Options{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
@@ -171,22 +170,22 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
|
|||||||
|
|
||||||
manifest, err := sync.Generate(opts)
|
manifest, err := sync.Generate(opts)
|
||||||
if err != nil {
|
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 {
|
output, err := manifest.WriteFile(tmpDir)
|
||||||
return err
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
outputDir := filepath.Dir(output)
|
||||||
if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
|
if err := utils.GenerateKustomizationYaml(outputDir); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
return outputDir, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error {
|
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error {
|
||||||
kubectlArgs := []string{"apply", "-k", filepath.Join(tmpDir, targetPath, namespace)}
|
kubectlArgs := []string{"apply", "-k", manifestsPath}
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/git"
|
"github.com/fluxcd/pkg/git"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitHubCmd = &cobra.Command{
|
var bootstrapGitHubCmd = &cobra.Command{
|
||||||
@@ -75,7 +77,7 @@ var (
|
|||||||
ghPersonal bool
|
ghPersonal bool
|
||||||
ghPrivate bool
|
ghPrivate bool
|
||||||
ghHostname string
|
ghHostname string
|
||||||
ghPath string
|
ghPath flags.SafeRelativePath
|
||||||
ghTeams []string
|
ghTeams []string
|
||||||
ghDelete bool
|
ghDelete bool
|
||||||
ghSSHHostname string
|
ghSSHHostname string
|
||||||
@@ -94,7 +96,7 @@ func init() {
|
|||||||
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
|
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
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(&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().BoolVar(&ghDelete, "delete", false, "delete repository (used for testing only)")
|
||||||
bootstrapGitHubCmd.Flags().MarkHidden("delete")
|
bootstrapGitHubCmd.Flags().MarkHidden("delete")
|
||||||
@@ -174,13 +176,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logger.Generatef("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
manifest, err := generateInstallManifests(ghPath, namespace, tmpDir, bootstrapManifestsPath)
|
installManifest, err := generateInstallManifests(ghPath.String(), namespace, tmpDir, bootstrapManifestsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage install manifests
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -206,7 +208,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if isInstall {
|
if isInstall {
|
||||||
// apply install manifests
|
// apply install manifests
|
||||||
logger.Actionf("installing components in %s namespace", namespace)
|
logger.Actionf("installing components in %s namespace", namespace)
|
||||||
if err := applyInstallManifests(ctx, manifest, bootstrapComponents()); err != nil {
|
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("install completed")
|
logger.Successf("install completed")
|
||||||
@@ -259,12 +261,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// configure repo synchronization
|
// configure repo synchronization
|
||||||
logger.Actionf("generating sync manifests")
|
logger.Actionf("generating sync manifests")
|
||||||
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// commit and push manifests
|
||||||
if changed, err = repository.Commit(ctx, path.Join(ghPath, namespace), "Add manifests"); err != nil {
|
if changed, err = repository.Commit(ctx, path.Join(ghPath.String(), namespace), "Add manifests"); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
@@ -275,7 +278,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// apply manifests and waiting for sync
|
// apply manifests and waiting for sync
|
||||||
logger.Actionf("applying sync manifests")
|
logger.Actionf("applying sync manifests")
|
||||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, ghPath, tmpDir); err != nil {
|
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/git"
|
"github.com/fluxcd/pkg/git"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitLabCmd = &cobra.Command{
|
var bootstrapGitLabCmd = &cobra.Command{
|
||||||
@@ -73,7 +75,7 @@ var (
|
|||||||
glPrivate bool
|
glPrivate bool
|
||||||
glHostname string
|
glHostname string
|
||||||
glSSHHostname string
|
glSSHHostname string
|
||||||
glPath string
|
glPath flags.SafeRelativePath
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -84,7 +86,7 @@ func init() {
|
|||||||
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
|
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
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(&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)
|
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
||||||
}
|
}
|
||||||
@@ -145,13 +147,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logger.Generatef("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
manifest, err := generateInstallManifests(glPath, namespace, tmpDir, bootstrapManifestsPath)
|
installManifest, err := generateInstallManifests(glPath.String(), namespace, tmpDir, bootstrapManifestsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage install manifests
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -172,7 +174,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if isInstall {
|
if isInstall {
|
||||||
// apply install manifests
|
// apply install manifests
|
||||||
logger.Actionf("installing components in %s namespace", namespace)
|
logger.Actionf("installing components in %s namespace", namespace)
|
||||||
if err := applyInstallManifests(ctx, manifest, bootstrapComponents()); err != nil {
|
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("install completed")
|
logger.Successf("install completed")
|
||||||
@@ -225,12 +227,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// configure repo synchronization
|
// configure repo synchronization
|
||||||
logger.Actionf("generating sync manifests")
|
logger.Actionf("generating sync manifests")
|
||||||
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// commit and push manifests
|
||||||
if changed, err = repository.Commit(ctx, path.Join(glPath, namespace), "Add manifests"); err != nil {
|
if changed, err = repository.Commit(ctx, path.Join(glPath.String(), namespace), "Add manifests"); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
@@ -241,7 +244,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// apply manifests and waiting for sync
|
// apply manifests and waiting for sync
|
||||||
logger.Actionf("applying sync manifests")
|
logger.Actionf("applying sync manifests")
|
||||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, glPath, tmpDir); err != nil {
|
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var checkCmd = &cobra.Command{
|
var checkCmd = &cobra.Command{
|
||||||
@@ -133,7 +132,7 @@ func kubectlCheck(ctx context.Context, version string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func kubernetesCheck(version string) bool {
|
func kubernetesCheck(version string) bool {
|
||||||
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
cfg, err := utils.KubeConfig(kubeconfig, kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -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}:
|
or write a cached file in one of the completion directories in your ${fpath}:
|
||||||
|
|
||||||
echo "${fpath// /\n}" | grep -i completion
|
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 ~/.oh-my-zsh/completions # oh-my-zsh
|
||||||
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ var createKsCmd = &cobra.Command{
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ksSource flags.KustomizationSource
|
ksSource flags.KustomizationSource
|
||||||
ksPath string
|
ksPath flags.SafeRelativePath = "./"
|
||||||
ksPrune bool
|
ksPrune bool
|
||||||
ksDependsOn []string
|
ksDependsOn []string
|
||||||
ksValidation string
|
ksValidation string
|
||||||
@@ -88,7 +88,7 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createKsCmd.Flags().Var(&ksSource, "source", ksSource.Description())
|
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().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().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")
|
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 == "" {
|
if ksPath == "" {
|
||||||
return fmt.Errorf("path is required")
|
return fmt.Errorf("path is required")
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(ksPath, "./") {
|
if !strings.HasPrefix(ksPath.String(), "./") {
|
||||||
return fmt.Errorf("path must begin with ./")
|
return fmt.Errorf("path must begin with ./")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: interval,
|
Duration: interval,
|
||||||
},
|
},
|
||||||
Path: ksPath,
|
Path: ksPath.String(),
|
||||||
Prune: ksPrune,
|
Prune: ksPrune,
|
||||||
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
||||||
Kind: ksSource.Kind,
|
Kind: ksSource.Kind,
|
||||||
|
|||||||
@@ -46,16 +46,16 @@ flux bootstrap github [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help help for github
|
-h, --help help for github
|
||||||
--hostname string GitHub hostname (default "github.com")
|
--hostname string GitHub hostname (default "github.com")
|
||||||
--interval duration sync interval (default 1m0s)
|
--interval duration sync interval (default 1m0s)
|
||||||
--owner string GitHub user or organization name
|
--owner string GitHub user or organization name
|
||||||
--path string repository path, when specified the cluster sync will be scoped to this path
|
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||||
--personal is personal repository
|
--personal is personal repository
|
||||||
--private is private repository (default true)
|
--private is private repository (default true)
|
||||||
--repository string GitHub repository name
|
--repository string GitHub repository name
|
||||||
--ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one
|
--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
|
--team stringArray GitHub team to be given maintainer access
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|||||||
@@ -43,15 +43,15 @@ flux bootstrap gitlab [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help help for gitlab
|
-h, --help help for gitlab
|
||||||
--hostname string GitLab hostname (default "gitlab.com")
|
--hostname string GitLab hostname (default "gitlab.com")
|
||||||
--interval duration sync interval (default 1m0s)
|
--interval duration sync interval (default 1m0s)
|
||||||
--owner string GitLab user or group name
|
--owner string GitLab user or group name
|
||||||
--path string repository path, when specified the cluster sync will be scoped to this path
|
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||||
--personal is personal repository
|
--personal is personal repository
|
||||||
--private is private repository (default true)
|
--private is private repository (default true)
|
||||||
--repository string GitLab repository name
|
--repository string GitLab repository name
|
||||||
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|||||||
@@ -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}:
|
or write a cached file in one of the completion directories in your ${fpath}:
|
||||||
|
|
||||||
echo "${fpath// /\n}" | grep -i completion
|
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 ~/.oh-my-zsh/completions # oh-my-zsh
|
||||||
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
||||||
|
|||||||
@@ -76,10 +76,10 @@ flux create helmrelease [name] [flags]
|
|||||||
-h, --help help for helmrelease
|
-h, --help help for helmrelease
|
||||||
--release-name string name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'
|
--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
|
--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
|
--target-namespace string namespace to install this release, defaults to the HelmRelease namespace
|
||||||
--values string local path to the values.yaml file
|
--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
|
### 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 stringArray workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'
|
||||||
--health-check-timeout duration timeout of health checking operations (default 2m0s)
|
--health-check-timeout duration timeout of health checking operations (default 2m0s)
|
||||||
-h, --help help for kustomization
|
-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
|
--prune enable garbage collection
|
||||||
--service-account string the name of the service account to impersonate when reconciling this Kustomization
|
--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
|
--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'
|
--validation string validate the manifests before applying them on the cluster, can be 'client' or 'server'
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -257,6 +257,11 @@ The definition of the listed keys is as follows:
|
|||||||
You can read more about the available formats and limitations in
|
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).
|
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
|
## Refer to values in `ConfigMaps` generated with Kustomize
|
||||||
|
|
||||||
It is possible to use Kustomize [ConfigMap generator](https://kubectl.docs.kubernetes.io/references/kustomize/configmapgenerator/)
|
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
|
```sh
|
||||||
flux bootstrap <GIT-PROVIDER> \
|
flux bootstrap <GIT-PROVIDER> \
|
||||||
--components=source-controller,kustomize-controller,helm-controller,notification-controller \
|
--components=source-controller,kustomize-controller,helm-controller,notification-controller \
|
||||||
|
--components-extra=image-reflector-controller,image-automation-controller \
|
||||||
--path=clusters/my-cluster \
|
--path=clusters/my-cluster \
|
||||||
--version=latest
|
--version=latest
|
||||||
```
|
```
|
||||||
@@ -117,6 +118,12 @@ flux bootstrap github \
|
|||||||
--personal
|
--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:
|
Run the bootstrap for a repository owned by a GitHub organization:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -291,13 +298,16 @@ Create a `GitRepository` object on your cluster by specifying the SSH address of
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
flux create source git flux-system \
|
flux create source git flux-system \
|
||||||
--url= ssh://<host>/<org>/my-repository \
|
--url=ssh://git@<host>/<org>/<repository> \
|
||||||
--ssh-key-algorithm=ecdsa \
|
--ssh-key-algorithm=ecdsa \
|
||||||
--ssh-ecdsa-curve=p521 \
|
--ssh-ecdsa-curve=p521 \
|
||||||
--branch=master \
|
--branch=master \
|
||||||
--interval=1m
|
--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"
|
!!! hint "Azure DevOps"
|
||||||
Azure DevOps requires a non-default Git implementation (`libgit2`) to be enabled, so that the Git v2 protocol is supported.
|
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
|
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
|
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.
|
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
|
Note that unlike `git`, Flux does not support the
|
||||||
flux create source git flux-system \
|
["shorter" scp-like syntax for the SSH protocol](https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_the_ssh_protocol)
|
||||||
--url= ssh://<host>/<org>/my-repository \
|
(e.g. `ssh.dev.azure.com:v3`).
|
||||||
--ssh-key-algorithm=ecdsa \
|
Use the [RFC 3986 compatible syntax](https://tools.ietf.org/html/rfc3986#section-3) instead: `ssh.dev.azure.com/v3`.
|
||||||
--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.
|
|
||||||
|
|
||||||
If your Git server supports basic auth, you can set the URL to HTTPS and specify the credentials with:
|
If your Git server supports basic auth, you can set the URL to HTTPS and specify the credentials with:
|
||||||
|
|
||||||
|
|||||||
7
go.mod
7
go.mod
@@ -4,17 +4,18 @@ go 1.15
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/blang/semver/v4 v4.0.0
|
github.com/blang/semver/v4 v4.0.0
|
||||||
github.com/fluxcd/helm-controller/api v0.4.3
|
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.1.0
|
github.com/fluxcd/image-automation-controller/api v0.1.0
|
||||||
github.com/fluxcd/image-reflector-controller/api v0.1.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/notification-controller/api v0.5.0
|
||||||
github.com/fluxcd/pkg/apis/meta v0.5.0
|
github.com/fluxcd/pkg/apis/meta v0.5.0
|
||||||
github.com/fluxcd/pkg/git v0.1.0
|
github.com/fluxcd/pkg/git v0.1.0
|
||||||
github.com/fluxcd/pkg/runtime v0.4.0
|
github.com/fluxcd/pkg/runtime v0.4.0
|
||||||
github.com/fluxcd/pkg/ssh v0.0.5
|
github.com/fluxcd/pkg/ssh v0.0.5
|
||||||
github.com/fluxcd/pkg/untar v0.0.5
|
github.com/fluxcd/pkg/untar v0.0.5
|
||||||
github.com/fluxcd/source-controller/api v0.5.4
|
github.com/fluxcd/source-controller/api v0.5.6
|
||||||
github.com/google/go-containerregistry v0.2.0
|
github.com/google/go-containerregistry v0.2.0
|
||||||
github.com/manifoldco/promptui v0.7.0
|
github.com/manifoldco/promptui v0.7.0
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
|
|||||||
21
go.sum
21
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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
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.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.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.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=
|
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 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.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.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.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 h1:NuSF3gXetiHyUbVdneJMEVyPUYAe5wh+aN08JYAf1tI=
|
||||||
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
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.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.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.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 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/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.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.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 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/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.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.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.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 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
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/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 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
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.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 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
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/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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
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 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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/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/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=
|
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 h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
|
||||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
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/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.4 h1:WYf7KokS3ALeE1F2SrviMHtEBGfznP7DkxXwo5pP7f8=
|
||||||
github.com/fluxcd/helm-controller/api v0.4.3/go.mod h1:H3fHkKJWcxPz38L1kxBX/MGm5v9XKzeoKZWNM7+dW2o=
|
github.com/fluxcd/helm-controller/api v0.4.4/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 h1:XN/BbhCRoISEb828rfMt2fNe+3s4Zwc+BwhRi3K1SHA=
|
||||||
github.com/fluxcd/image-automation-controller/api v0.1.0/go.mod h1:DHjFNvA+kJlSm7cbTaG+Z5smVjMjLw7xzlJc9brP0zY=
|
github.com/fluxcd/image-automation-controller/api v0.1.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 h1:wlqwCy4sZMbbdrgSY9Fd0mfy55kk7dS4Z+icrDlkmmg=
|
||||||
github.com/fluxcd/image-reflector-controller/api v0.1.0/go.mod h1:u7vnULekPHXAZgJ35lqCjV2MaJVN0xbD+qt9X9TVCMs=
|
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.3 h1:zZiWFBQkNytLffOOJJttGFQk7BIUHpT2yoOCG4nyqII=
|
||||||
github.com/fluxcd/kustomize-controller/api v0.5.0/go.mod h1:8Z52j63kRf+NjtVmiJFvI8xLje3ncFTs/uMxcrEJPIA=
|
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 h1:xKKFnPVsYl2+GEjgKz5a5Mq6vmy+H2q9d2lJ2jmWJZs=
|
||||||
github.com/fluxcd/notification-controller/api v0.5.0/go.mod h1:yLd+nrCJUThSkt4U+LLv8TRxqZdR11+gE1S2/bhgqmE=
|
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=
|
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/ssh v0.0.5/go.mod h1:7jXPdXZpc0ttMNz2kD9QuMi3RNn/e0DOFbj0Tij/+Hs=
|
||||||
github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
|
github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
|
||||||
github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw=
|
github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw=
|
||||||
github.com/fluxcd/source-controller/api v0.5.4 h1:V3SzxbBDLULPWBD9nObJqXovCU5TZka8R/vrDIgomj4=
|
github.com/fluxcd/source-controller/api v0.5.6 h1:Nj7WebOP8nTTA/yDPwszyjzW5oYI5tVhS/8XWOT+2wk=
|
||||||
github.com/fluxcd/source-controller/api v0.5.4/go.mod h1:/mpW0EM2dUnRey6rffqsSmgNkSAYm+zq9i0GfmTO7I0=
|
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 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
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 {
|
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) {
|
if !utils.ContainsItemString(supportedDecryptionProviders, str) {
|
||||||
return fmt.Errorf("unsupported decryption provider '%s', must be one of: %s",
|
return fmt.Errorf("unsupported decryption provider '%s', must be one of: %s",
|
||||||
str, strings.Join(supportedDecryptionProviders, ", "))
|
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}
|
*c = ECDSACurve{v}
|
||||||
return nil
|
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 {
|
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"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var supportedHelmChartSourceKinds = []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind, sourcev1.BucketKind}
|
var supportedHelmChartSourceKinds = []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind, sourcev1.BucketKind}
|
||||||
@@ -31,42 +32,43 @@ type HelmChartSource struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HelmChartSource) String() string {
|
func (s *HelmChartSource) String() string {
|
||||||
if h.Name == "" {
|
if s.Name == "" {
|
||||||
return ""
|
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) == "" {
|
if strings.TrimSpace(str) == "" {
|
||||||
return fmt.Errorf("no helm chart source given, please specify %s",
|
return fmt.Errorf("no helm chart source given, please specify %s",
|
||||||
h.Description())
|
s.Description())
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceKind, sourceName := utils.ParseObjectKindName(str)
|
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)
|
return fmt.Errorf("invalid helm chart source '%s', must be in format <kind>/<name>", str)
|
||||||
}
|
}
|
||||||
if !utils.ContainsItemString(supportedHelmChartSourceKinds, sourceKind) {
|
cleanSourceKind, ok := utils.ContainsEqualFoldItemString(supportedHelmChartSourceKinds, sourceKind)
|
||||||
return fmt.Errorf("source kind '%s' is not supported, can be one of: %s",
|
if !ok {
|
||||||
|
return fmt.Errorf("source kind '%s' is not supported, must be one of: %s",
|
||||||
sourceKind, strings.Join(supportedHelmChartSourceKinds, ", "))
|
sourceKind, strings.Join(supportedHelmChartSourceKinds, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Name = sourceName
|
s.Name = sourceName
|
||||||
h.Kind = sourceKind
|
s.Kind = cleanSourceKind
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HelmChartSource) Type() string {
|
func (s *HelmChartSource) Type() string {
|
||||||
return "helmChartSource"
|
return "helmChartSource"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HelmChartSource) Description() string {
|
func (s *HelmChartSource) Description() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"source that contains the chart in the format '<kind>/<name>',"+
|
"source that contains the chart in the format '<kind>/<name>', "+
|
||||||
"where kind can be one of: (%s)",
|
"where kind must be one of: (%s)",
|
||||||
strings.Join(supportedHelmChartSourceKinds, ", "),
|
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
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HelmReleaseValuesFrom) String() string {
|
func (v *HelmReleaseValuesFrom) String() string {
|
||||||
if h.Name == "" {
|
if v.Name == "" {
|
||||||
return ""
|
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) == "" {
|
if strings.TrimSpace(str) == "" {
|
||||||
return fmt.Errorf("no values given, please specify %s",
|
return fmt.Errorf("no values given, please specify %s",
|
||||||
h.Description())
|
v.Description())
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceKind, sourceName := utils.ParseObjectKindName(str)
|
sourceKind, sourceName := utils.ParseObjectKindName(str)
|
||||||
if sourceKind == "" {
|
if sourceKind == "" {
|
||||||
return fmt.Errorf("invalid Kubernetes object reference '%s', must be in format <kind>/<name>", str)
|
return fmt.Errorf("invalid Kubernetes object reference '%s', must be in format <kind>/<name>", str)
|
||||||
}
|
}
|
||||||
if !utils.ContainsItemString(supportedHelmReleaseValuesFromKinds, sourceKind) {
|
cleanSourceKind, ok := utils.ContainsEqualFoldItemString(supportedHelmReleaseValuesFromKinds, sourceKind)
|
||||||
return fmt.Errorf("reference kind '%s' is not supported, can be one of: %s",
|
if !ok {
|
||||||
|
return fmt.Errorf("reference kind '%s' is not supported, must be one of: %s",
|
||||||
sourceKind, strings.Join(supportedHelmReleaseValuesFromKinds, ", "))
|
sourceKind, strings.Join(supportedHelmReleaseValuesFromKinds, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Name = sourceName
|
v.Name = sourceName
|
||||||
h.Kind = sourceKind
|
v.Kind = cleanSourceKind
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HelmReleaseValuesFrom) Type() string {
|
func (v *HelmReleaseValuesFrom) Type() string {
|
||||||
return "helmReleaseValuesFrom"
|
return "helmReleaseValuesFrom"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HelmReleaseValuesFrom) Description() string {
|
func (v *HelmReleaseValuesFrom) Description() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>',"+
|
"Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', "+
|
||||||
"where kind can be one of: (%s)",
|
"where kind must be one of: (%s)",
|
||||||
strings.Join(supportedHelmReleaseValuesFromKinds, ", "),
|
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"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var supportedKustomizationSourceKinds = []string{sourcev1.GitRepositoryKind, sourcev1.BucketKind}
|
var supportedKustomizationSourceKinds = []string{sourcev1.GitRepositoryKind, sourcev1.BucketKind}
|
||||||
@@ -31,42 +32,49 @@ type KustomizationSource struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KustomizationSource) String() string {
|
func (s *KustomizationSource) String() string {
|
||||||
if k.Name == "" {
|
if s.Name == "" {
|
||||||
return ""
|
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) == "" {
|
if strings.TrimSpace(str) == "" {
|
||||||
return fmt.Errorf("no kustomization source given, please specify %s",
|
return fmt.Errorf("no Kustomization source given, please specify %s",
|
||||||
k.Description())
|
s.Description())
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceKind, sourceName := utils.ParseObjectKindName(str)
|
sourceKind, sourceName := utils.ParseObjectKindName(str)
|
||||||
|
if sourceName == "" {
|
||||||
|
return fmt.Errorf("no name given for source of kind '%s'", sourceKind)
|
||||||
|
}
|
||||||
if sourceKind == "" {
|
if sourceKind == "" {
|
||||||
|
if utils.ContainsItemString(supportedKustomizationSourceKinds, sourceName) {
|
||||||
|
return fmt.Errorf("no name given for source of kind '%s'", sourceName)
|
||||||
|
}
|
||||||
sourceKind = sourcev1.GitRepositoryKind
|
sourceKind = sourcev1.GitRepositoryKind
|
||||||
}
|
}
|
||||||
if !utils.ContainsItemString(supportedKustomizationSourceKinds, sourceKind) {
|
cleanSourceKind, ok := utils.ContainsEqualFoldItemString(supportedKustomizationSourceKinds, sourceKind)
|
||||||
return fmt.Errorf("source kind '%s' is not supported, can be one of: %s",
|
if !ok {
|
||||||
|
return fmt.Errorf("source kind '%s' is not supported, must be one of: %s",
|
||||||
sourceKind, strings.Join(supportedKustomizationSourceKinds, ", "))
|
sourceKind, strings.Join(supportedKustomizationSourceKinds, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
k.Name = sourceName
|
s.Name = sourceName
|
||||||
k.Kind = sourceKind
|
s.Kind = cleanSourceKind
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KustomizationSource) Type() string {
|
func (s *KustomizationSource) Type() string {
|
||||||
return "kustomizationSource"
|
return "kustomizationSource"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KustomizationSource) Description() string {
|
func (s *KustomizationSource) Description() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"source that contains the Kubernetes manifests in the format '[<kind>/]<name>',"+
|
"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",
|
"where kind must be one of: (%s), if kind is not specified it defaults to GitRepository",
|
||||||
strings.Join(supportedKustomizationSourceKinds, ", "),
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if bits%8 != 0 {
|
if bits == 0 || bits%8 != 0 {
|
||||||
return fmt.Errorf("RSA key bit size should be a multiples of 8")
|
return fmt.Errorf("RSA key bit size must be a multiples of 8")
|
||||||
}
|
}
|
||||||
*b = RSAKeyBits(bits)
|
*b = RSAKeyBits(bits)
|
||||||
return nil
|
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
|
type SourceBucketProvider string
|
||||||
|
|
||||||
func (s *SourceBucketProvider) String() string {
|
func (p *SourceBucketProvider) String() string {
|
||||||
return string(*s)
|
return string(*p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceBucketProvider) Set(str string) error {
|
func (p *SourceBucketProvider) Set(str string) error {
|
||||||
if strings.TrimSpace(str) == "" {
|
if strings.TrimSpace(str) == "" {
|
||||||
return fmt.Errorf("no source bucket provider given, please specify %s",
|
return fmt.Errorf("no source bucket provider given, please specify %s",
|
||||||
s.Description())
|
p.Description())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !utils.ContainsItemString(supportedSourceBucketProviders, str) {
|
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, ", "))
|
str, strings.Join(supportedSourceBucketProviders, ", "))
|
||||||
}
|
}
|
||||||
|
*p = SourceBucketProvider(str)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceBucketProvider) Type() string {
|
func (p *SourceBucketProvider) Type() string {
|
||||||
return "sourceBucketProvider"
|
return "sourceBucketProvider"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceBucketProvider) Description() string {
|
func (p *SourceBucketProvider) Description() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"the S3 compatible storage provider name, available options are: (%s)",
|
"the S3 compatible storage provider name, available options are: (%s)",
|
||||||
strings.Join(supportedSourceBucketProviders, ", "),
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
@@ -134,7 +135,7 @@ func ExecTemplate(obj interface{}, tmpl, filename string) error {
|
|||||||
return file.Sync()
|
return file.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error) {
|
func KubeConfig(kubeConfigPath string, kubeContext string) (*rest.Config, error) {
|
||||||
configFiles := SplitKubeConfigPath(kubeConfigPath)
|
configFiles := SplitKubeConfigPath(kubeConfigPath)
|
||||||
configOverrides := clientcmd.ConfigOverrides{}
|
configOverrides := clientcmd.ConfigOverrides{}
|
||||||
|
|
||||||
@@ -147,6 +148,15 @@ func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error
|
|||||||
&configOverrides,
|
&configOverrides,
|
||||||
).ClientConfig()
|
).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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -230,6 +240,15 @@ func ContainsItemString(s []string, e string) bool {
|
|||||||
return false
|
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) {
|
func ParseObjectKindName(input string) (string, string) {
|
||||||
kind := ""
|
kind := ""
|
||||||
name := input
|
name := input
|
||||||
@@ -237,7 +256,6 @@ func ParseObjectKindName(input string) (string, string) {
|
|||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
kind, name = parts[0], parts[1]
|
kind, name = parts[0], parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
return kind, name
|
return kind, name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
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.4.zip//helm-controller-0.4.4/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/manager
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
group: apps
|
group: apps
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
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.3.zip//kustomize-controller-0.5.3/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/manager
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
group: apps
|
group: apps
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
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.6.zip//source-controller-0.5.6/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/manager
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
group: apps
|
group: apps
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ markdown_extensions:
|
|||||||
- pymdownx.tilde
|
- pymdownx.tilde
|
||||||
- pymdownx.progressbar
|
- pymdownx.progressbar
|
||||||
- pymdownx.tasklist
|
- pymdownx.tasklist
|
||||||
|
- pymdownx.superfences
|
||||||
- pymdownx.emoji:
|
- pymdownx.emoji:
|
||||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
emoji_generator: !!python/name:materialx.emoji.to_svg
|
||||||
@@ -52,6 +53,7 @@ nav:
|
|||||||
- Monitoring with Prometheus: guides/monitoring.md
|
- Monitoring with Prometheus: guides/monitoring.md
|
||||||
- Sealed Secrets: guides/sealed-secrets.md
|
- Sealed Secrets: guides/sealed-secrets.md
|
||||||
- Mozilla SOPS: guides/mozilla-sops.md
|
- Mozilla SOPS: guides/mozilla-sops.md
|
||||||
|
- Automate image updates to Git: guides/image-update.md
|
||||||
- Migration:
|
- Migration:
|
||||||
- Migrate from Flux v1: guides/flux-v1-migration.md
|
- Migrate from Flux v1: guides/flux-v1-migration.md
|
||||||
- Migrate from the Helm Operator: guides/helm-operator-migration.md
|
- Migrate from the Helm Operator: guides/helm-operator-migration.md
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,7 +42,10 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
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 !strings.HasPrefix(options.BaseURL, "http") {
|
||||||
if err := build(options.BaseURL, output); err != nil {
|
if err := build(options.BaseURL, output); err != nil {
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Manifest holds the data of a multi-doc YAML
|
// 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,
|
// If the file does not exist, WriteFile creates it with permissions perm,
|
||||||
// otherwise WriteFile overwrites the file, without changing permissions.
|
// otherwise WriteFile overwrites the file, without changing permissions.
|
||||||
func (m *Manifest) WriteFile(rootDir string) (string, error) {
|
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)
|
return "", fmt.Errorf("unable to create dir, error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath := path.Join(rootDir, m.Path)
|
if err := ioutil.WriteFile(output, []byte(m.Content), os.ModePerm); err != nil {
|
||||||
if err := ioutil.WriteFile(filePath, []byte(m.Content), os.ModePerm); err != nil {
|
|
||||||
return "", fmt.Errorf("unable to write file, error: %w", err)
|
return "", fmt.Errorf("unable to write file, error: %w", err)
|
||||||
}
|
}
|
||||||
|
return output, nil
|
||||||
return filePath, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user