Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c61cfcbd18 | ||
|
|
eba2dd36e0 | ||
|
|
2a75754561 | ||
|
|
d03944893d | ||
|
|
884c6ebd37 | ||
|
|
331ac3f031 | ||
|
|
ccc84a8367 | ||
|
|
daeb41c31b | ||
|
|
17bda9c110 | ||
|
|
febedaad8f | ||
|
|
d1357dff1f | ||
|
|
102552427f | ||
|
|
f33898265d | ||
|
|
57bdaf939a | ||
|
|
981fed111b | ||
|
|
3a4a2002d4 | ||
|
|
b8d4af5538 | ||
|
|
0646538cef | ||
|
|
70a87247e2 | ||
|
|
61129c6b6a | ||
|
|
c158f95130 | ||
|
|
ad90d37f14 | ||
|
|
73ba754481 | ||
|
|
7dcfbdbb29 | ||
|
|
f453507fcc | ||
|
|
c5465de000 | ||
|
|
352b864636 | ||
|
|
c034befbb5 | ||
|
|
572cdf40fc | ||
|
|
0c0d353e9c | ||
|
|
bcc90afba2 | ||
|
|
a919703011 | ||
|
|
3300a45c39 | ||
|
|
f1cfae8f26 | ||
|
|
62763961be | ||
|
|
f1dab2279d | ||
|
|
ea337cf839 | ||
|
|
27277136f8 | ||
|
|
dd0b807fe4 | ||
|
|
ed09dd57b6 | ||
|
|
58b4c980c1 | ||
|
|
dd5165dcbf | ||
|
|
6da22613fe | ||
|
|
d0926776a5 | ||
|
|
14dc39e8d2 | ||
|
|
f0f2a79384 | ||
|
|
7b6f875920 | ||
|
|
52cec044b8 | ||
|
|
07dd59892f | ||
|
|
ffeaa683c5 | ||
|
|
1301bf7c15 | ||
|
|
69387fd2a4 | ||
|
|
12a0ebe3ba | ||
|
|
3de81827eb | ||
|
|
a7362b60e7 | ||
|
|
5d4bb3a43f | ||
|
|
d02d507812 | ||
|
|
554de7ba6f | ||
|
|
5d9ccc973d | ||
|
|
53ffb8aa00 | ||
|
|
c4da4a81aa | ||
|
|
b824ea8858 | ||
|
|
22e26efec1 | ||
|
|
679490e8f4 | ||
|
|
15f17ed36d | ||
|
|
c8265fb80c | ||
|
|
3883e92631 | ||
|
|
aa9bc4ce8b | ||
|
|
37c14e8088 | ||
|
|
439fbafc01 | ||
|
|
1b8e980519 | ||
|
|
567acb6291 | ||
|
|
996bfe87ff | ||
|
|
3c1793b6c5 | ||
|
|
1a7f253767 | ||
|
|
f188e59b21 | ||
|
|
5ea4e814f5 | ||
|
|
008b3b8408 | ||
|
|
7ae3dee900 | ||
|
|
2395ab6e14 | ||
|
|
8efe053ffa | ||
|
|
612600b88c | ||
|
|
4d7df52dbe | ||
|
|
b6c63a1aa4 | ||
|
|
a4788ce6bb | ||
|
|
0ba6fc1b36 | ||
|
|
0e35c209d9 | ||
|
|
3b9b2cbe9f | ||
|
|
45240bdb71 | ||
|
|
22a5ac7f0f | ||
|
|
d55d185044 | ||
|
|
2bb09697ce | ||
|
|
f316aff2d3 | ||
|
|
433628791b | ||
|
|
4f52b77563 | ||
|
|
52145c045d | ||
|
|
512761080e | ||
|
|
037a5b71fd | ||
|
|
b66bdec61a | ||
|
|
16f52610ab | ||
|
|
b2f018e29b | ||
|
|
1f497cac44 | ||
|
|
4abe69f90a | ||
|
|
75023011d3 | ||
|
|
09f145d880 | ||
|
|
811cd4248f |
3
.github/workflows/bootstrap.yaml
vendored
3
.github/workflows/bootstrap.yaml
vendored
@@ -49,8 +49,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||
- name: uninstall
|
||||
run: |
|
||||
./bin/flux suspend kustomization flux-system
|
||||
./bin/flux uninstall --resources --crds -s
|
||||
./bin/flux uninstall --resources --crds -s --timeout=10m
|
||||
- name: bootstrap reinstall
|
||||
run: |
|
||||
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
||||
|
||||
11
.github/workflows/e2e.yaml
vendored
11
.github/workflows/e2e.yaml
vendored
@@ -146,12 +146,21 @@ jobs:
|
||||
--chart=podinfo \
|
||||
--chart-version="5.0.x" \
|
||||
--service-account=dev-team
|
||||
- name: flux2-kustomize-helm-example
|
||||
run: |
|
||||
./bin/flux create source git flux-system \
|
||||
--url=https://github.com/fluxcd/flux2-kustomize-helm-example \
|
||||
--branch=main
|
||||
./bin/flux create kustomization flux-system \
|
||||
--source=flux-system \
|
||||
--path=./clusters/staging
|
||||
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=2m
|
||||
- name: flux check
|
||||
run: |
|
||||
./bin/flux check
|
||||
- name: flux uninstall
|
||||
run: |
|
||||
./bin/flux uninstall --crds --silent
|
||||
./bin/flux uninstall --crds --silent --timeout=10m
|
||||
- name: Debug failure
|
||||
if: failure()
|
||||
run: |
|
||||
|
||||
2
.github/workflows/update.yml
vendored
2
.github/workflows/update.yml
vendored
@@ -40,6 +40,8 @@ jobs:
|
||||
bump_version kustomize-controller
|
||||
bump_version source-controller
|
||||
bump_version notification-controller
|
||||
bump_version image-reflector-controller
|
||||
bump_version image-automation-controller
|
||||
|
||||
# add missing and remove unused modules
|
||||
go mod tidy
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Contributing
|
||||
|
||||
Flux is [Apache 2.0
|
||||
licensed](https://github.com/fluxcd/flux2/blob/main/LICENSE) and
|
||||
Flux is [Apache 2.0 licensed](https://github.com/fluxcd/flux2/blob/main/LICENSE) and
|
||||
accepts contributions via GitHub pull requests. This document outlines
|
||||
some of the conventions on to make it easier to get your contribution
|
||||
accepted.
|
||||
@@ -14,9 +13,18 @@ code.
|
||||
By contributing to this project you agree to the Developer Certificate of
|
||||
Origin (DCO). This document was created by the Linux Kernel community and is a
|
||||
simple statement that you, as a contributor, have the legal right to make the
|
||||
contribution. No action from you is required, but it's a good idea to see the
|
||||
[DCO](DCO) file for details before you start contributing code to FluxCD
|
||||
organization.
|
||||
contribution.
|
||||
|
||||
We require all commits to be signed. By signing off with your signature, you
|
||||
certify that you wrote the patch or otherwise have the right to contribute the
|
||||
material by the rules of the [DCO](DCO):
|
||||
|
||||
`Signed-off-by: Jane Doe <jane.doe@example.com>`
|
||||
|
||||
The signature must contain your real name
|
||||
(sorry, no pseudonyms or anonymous contributions)
|
||||
If your `user.name` and `user.email` are configured in your Git config,
|
||||
you can sign your commit automatically with `git commit -s`.
|
||||
|
||||
## Communications
|
||||
|
||||
|
||||
14
README.md
14
README.md
@@ -108,17 +108,17 @@ Depending on what you want to do, some of the following bits might be your first
|
||||
- Check out [how to contribute](CONTRIBUTING.md) to the project
|
||||
|
||||
### Upcoming Events
|
||||
|
||||
- 14 Dec 2020 - [The Power of GitOps with Flux and Flagger with Leigh Capili](https://www.meetup.com/GitOps-Community/events/274924513/)
|
||||
- 11 Jan 2021 - [Helm + GitOps = ⚡️⚡️⚡️ with Scott Rigby](https://www.meetup.com/GitOps-Community/events/275348736/)
|
||||
|
||||
### Featured Talks
|
||||
|
||||
- 14 Dec 2020 - [The Power of GitOps with Flux and Flagger (GitOps Hands-On) with Leigh Capili](https://youtu.be/cB7iXeNLteE)
|
||||
- 30 Nov 2020 - [The Power of GitOps with Flux 2 - Part 3 with Leigh Capili](https://youtu.be/N_K5g7o9JKg)
|
||||
- 24 Nov 2020 - [Flux CD v2 with GitOps Toolkit - Kubernetes Deployment and Sync Mechanism](https://youtu.be/R6OeIgb7lUI)
|
||||
- 02 Nov 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 2 with Leigh Capili](https://youtu.be/fC2YCxQRUwU)
|
||||
- 28 Oct 2020 - [The Kubelist Podcast: Flux with Michael Bridgen](https://www.heavybit.com/library/podcasts/the-kubelist-podcast/ep-5-flux-with-michael-bridgen-of-weaveworks/)
|
||||
- 19 Oct 2020 - [The Power of GitOps with Flux & GitOps Toolkit - Part 1 with Leigh Capili](https://youtu.be/0v5bjysXTL8)
|
||||
- 30 Nov 2020 - [The Power of GitOps with Flux 2 - Part 3 with Leigh Capili](https://youtu.be/N_K5g7o9JKg)
|
||||
- 12 Oct 2020 - [Rawkode Live: Introduction to GitOps Toolkit with Stefan Prodan](https://youtu.be/HqTzuOBP0eY)
|
||||
- 4 Sep 2020 - [KubeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
|
||||
- 25 June 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
|
||||
- 04 Sep 2020 - [KubeCon Europe: The road to Flux v2 and Progressive Delivery with Stefan Prodan & Hidde Beydals](https://youtu.be/8v94nUkXsxU)
|
||||
- 25 Jun 2020 - [Cloud Native Nordics: Introduction to GitOps & GitOps Toolkit with Alexis Richardson & Stefan Prodan](https://youtu.be/qQBtSkgl7tI)
|
||||
|
||||
We are looking forward to seeing you with us!
|
||||
We look forward to seeing you with us!
|
||||
|
||||
@@ -10,6 +10,16 @@ Usage:
|
||||
run: flux -v
|
||||
```
|
||||
|
||||
This action places the `flux` binary inside your repository root under `bin/flux`.
|
||||
You should add `bin/flux` to your `.gitignore` file, as in the following example:
|
||||
|
||||
```gitignore
|
||||
# ignore flux binary
|
||||
bin/flux
|
||||
```
|
||||
|
||||
Note that this action can only be used on GitHub **Linux AMD64** runners.
|
||||
|
||||
### Automate Flux updates
|
||||
|
||||
Example workflow for updating Flux's components generated with `flux bootstrap --arch=amd64 --path=clusters/production`:
|
||||
|
||||
@@ -29,7 +29,7 @@ curl -sL $BIN_URL | tar xz
|
||||
|
||||
# Copy binary to GitHub runner
|
||||
mkdir -p $GITHUB_WORKSPACE/bin
|
||||
cp ./flux $GITHUB_WORKSPACE/bin
|
||||
mv ./flux $GITHUB_WORKSPACE/bin
|
||||
chmod +x $GITHUB_WORKSPACE/bin/flux
|
||||
|
||||
# Print version
|
||||
|
||||
@@ -47,7 +47,8 @@ var bootstrapCmd = &cobra.Command{
|
||||
|
||||
var (
|
||||
bootstrapVersion string
|
||||
bootstrapComponents []string
|
||||
bootstrapDefaultComponents []string
|
||||
bootstrapExtraComponents []string
|
||||
bootstrapRegistry string
|
||||
bootstrapImagePullSecret string
|
||||
bootstrapBranch string
|
||||
@@ -58,6 +59,7 @@ var (
|
||||
bootstrapLogLevel = flags.LogLevel(defaults.LogLevel)
|
||||
bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"}
|
||||
bootstrapTokenAuth bool
|
||||
bootstrapClusterDomain string
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -67,8 +69,10 @@ const (
|
||||
func init() {
|
||||
bootstrapCmd.PersistentFlags().StringVarP(&bootstrapVersion, "version", "v", defaults.Version,
|
||||
"toolkit version")
|
||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapComponents, "components", defaults.Components,
|
||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapDefaultComponents, "components", defaults.Components,
|
||||
"list of components, accepts comma-separated values")
|
||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapExtraComponents, "components-extra", nil,
|
||||
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapRegistry, "registry", "ghcr.io/fluxcd",
|
||||
"container registry where the toolkit images are published")
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapImagePullSecret, "image-pull-secret", "",
|
||||
@@ -84,17 +88,27 @@ func init() {
|
||||
"when enabled, the personal access token will be used instead of SSH deploy key")
|
||||
bootstrapCmd.PersistentFlags().Var(&bootstrapLogLevel, "log-level", bootstrapLogLevel.Description())
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory")
|
||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapClusterDomain, "cluster-domain", defaults.ClusterDomain, "internal cluster domain")
|
||||
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
||||
rootCmd.AddCommand(bootstrapCmd)
|
||||
}
|
||||
|
||||
func bootstrapComponents() []string {
|
||||
return append(bootstrapDefaultComponents, bootstrapExtraComponents...)
|
||||
}
|
||||
|
||||
func bootstrapValidate() error {
|
||||
components := bootstrapComponents()
|
||||
for _, component := range bootstrapRequiredComponents {
|
||||
if !utils.ContainsItemString(bootstrapComponents, component) {
|
||||
if !utils.ContainsItemString(components, component) {
|
||||
return fmt.Errorf("component %s is required", component)
|
||||
}
|
||||
}
|
||||
|
||||
if err := utils.ValidateComponents(components); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -103,7 +117,7 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
||||
BaseURL: localManifests,
|
||||
Version: bootstrapVersion,
|
||||
Namespace: namespace,
|
||||
Components: bootstrapComponents,
|
||||
Components: bootstrapComponents(),
|
||||
Registry: bootstrapRegistry,
|
||||
ImagePullSecret: bootstrapImagePullSecret,
|
||||
Arch: bootstrapArch.String(),
|
||||
@@ -114,6 +128,7 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
||||
ManifestFile: defaults.ManifestFile,
|
||||
Timeout: timeout,
|
||||
TargetPath: targetPath,
|
||||
ClusterDomain: bootstrapClusterDomain,
|
||||
}
|
||||
|
||||
if localManifests == "" {
|
||||
@@ -125,12 +140,11 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
||||
}
|
||||
|
||||
if filePath, err := output.WriteFile(tmpDir); err != nil {
|
||||
filePath, err := output.WriteFile(tmpDir)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
||||
} else {
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
|
||||
@@ -148,7 +162,7 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) error {
|
||||
func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) (string, error) {
|
||||
opts := sync.Options{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
@@ -161,22 +175,22 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
|
||||
|
||||
manifest, err := sync.Generate(opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating install manifests failed: %w", err)
|
||||
return "", fmt.Errorf("generating install manifests failed: %w", err)
|
||||
}
|
||||
|
||||
if _, err := manifest.WriteFile(tmpDir); err != nil {
|
||||
return err
|
||||
output, err := manifest.WriteFile(tmpDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
|
||||
return err
|
||||
outputDir := filepath.Dir(output)
|
||||
if err := utils.GenerateKustomizationYaml(outputDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nil
|
||||
return outputDir, nil
|
||||
}
|
||||
|
||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error {
|
||||
kubectlArgs := []string{"apply", "-k", filepath.Join(tmpDir, targetPath, namespace)}
|
||||
func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, manifestsPath string) error {
|
||||
kubectlArgs := []string{"apply", "-k", manifestsPath}
|
||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -225,7 +239,7 @@ func shouldCreateDeployKey(ctx context.Context, kubeClient client.Client, namesp
|
||||
}
|
||||
|
||||
func generateDeployKey(ctx context.Context, kubeClient client.Client, url *url.URL, namespace string) (string, error) {
|
||||
pair, err := generateKeyPair(ctx)
|
||||
pair, err := generateKeyPair(ctx, sourceGitKeyAlgorithm, sourceGitRSABits, sourceGitECDSACurve)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -252,3 +266,20 @@ func generateDeployKey(ctx context.Context, kubeClient client.Client, url *url.U
|
||||
|
||||
return string(pair.PublicKey), nil
|
||||
}
|
||||
|
||||
func checkIfBootstrapPathDiffers(ctx context.Context, kubeClient client.Client, namespace string, path string) (string, bool) {
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: namespace,
|
||||
Namespace: namespace,
|
||||
}
|
||||
var fluxSystemKustomization kustomizev1.Kustomization
|
||||
err := kubeClient.Get(ctx, namespacedName, &fluxSystemKustomization)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
if fluxSystemKustomization.Spec.Path == path {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return fluxSystemKustomization.Spec.Path, true
|
||||
}
|
||||
|
||||
@@ -23,14 +23,17 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/git"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var bootstrapGitHubCmd = &cobra.Command{
|
||||
@@ -75,7 +78,7 @@ var (
|
||||
ghPersonal bool
|
||||
ghPrivate bool
|
||||
ghHostname string
|
||||
ghPath string
|
||||
ghPath flags.SafeRelativePath
|
||||
ghTeams []string
|
||||
ghDelete bool
|
||||
ghSSHHostname string
|
||||
@@ -94,7 +97,7 @@ func init() {
|
||||
bootstrapGitHubCmd.Flags().DurationVar(&ghInterval, "interval", time.Minute, "sync interval")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghHostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghSSHHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||
bootstrapGitHubCmd.Flags().StringVar(&ghPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
|
||||
bootstrapGitHubCmd.Flags().Var(&ghPath, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||
|
||||
bootstrapGitHubCmd.Flags().BoolVar(&ghDelete, "delete", false, "delete repository (used for testing only)")
|
||||
bootstrapGitHubCmd.Flags().MarkHidden("delete")
|
||||
@@ -112,6 +115,20 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, namespace, filepath.ToSlash(ghPath.String()))
|
||||
|
||||
if bootstrapPathDiffers {
|
||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
||||
}
|
||||
|
||||
repository, err := git.NewRepository(ghRepository, ghOwner, ghHostname, ghToken, "flux", ghOwner+"@users.noreply.github.com")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -132,9 +149,6 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
if ghDelete {
|
||||
if err := provider.DeleteRepository(ctx, repository); err != nil {
|
||||
return err
|
||||
@@ -174,13 +188,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// generate install manifests
|
||||
logger.Generatef("generating manifests")
|
||||
manifest, err := generateInstallManifests(ghPath, namespace, tmpDir, bootstrapManifestsPath)
|
||||
installManifest, err := generateInstallManifests(ghPath.String(), namespace, tmpDir, bootstrapManifestsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage install manifests
|
||||
changed, err = repository.Commit(ctx, path.Join(ghPath, namespace), "Add manifests")
|
||||
changed, err = repository.Commit(ctx, path.Join(ghPath.String(), namespace), "Add manifests")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -195,18 +209,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
logger.Successf("components are up to date")
|
||||
}
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// determine if repo synchronization is working
|
||||
isInstall := shouldInstallManifests(ctx, kubeClient, namespace)
|
||||
|
||||
if isInstall {
|
||||
// apply install manifests
|
||||
logger.Actionf("installing components in %s namespace", namespace)
|
||||
if err := applyInstallManifests(ctx, manifest, bootstrapComponents); err != nil {
|
||||
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("install completed")
|
||||
@@ -259,12 +268,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// configure repo synchronization
|
||||
logger.Actionf("generating sync manifests")
|
||||
if err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, ghPath, tmpDir, ghInterval); err != nil {
|
||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, filepath.ToSlash(ghPath.String()), tmpDir, ghInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// commit and push manifests
|
||||
if changed, err = repository.Commit(ctx, path.Join(ghPath, namespace), "Add manifests"); err != nil {
|
||||
if changed, err = repository.Commit(ctx, path.Join(ghPath.String(), namespace), "Add manifests"); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
@@ -275,7 +285,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// apply manifests and waiting for sync
|
||||
logger.Actionf("applying sync manifests")
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, ghPath, tmpDir); err != nil {
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,18 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/git"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var bootstrapGitLabCmd = &cobra.Command{
|
||||
@@ -65,6 +69,10 @@ the bootstrap command will perform an upgrade if needed.`,
|
||||
RunE: bootstrapGitLabCmdRun,
|
||||
}
|
||||
|
||||
const (
|
||||
gitlabProjectRegex = `\A[[:alnum:]\x{00A9}-\x{1f9ff}_][[:alnum:]\p{Pd}\x{00A9}-\x{1f9ff}_\.]*\z`
|
||||
)
|
||||
|
||||
var (
|
||||
glOwner string
|
||||
glRepository string
|
||||
@@ -73,7 +81,7 @@ var (
|
||||
glPrivate bool
|
||||
glHostname string
|
||||
glSSHHostname string
|
||||
glPath string
|
||||
glPath flags.SafeRelativePath
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -84,7 +92,7 @@ func init() {
|
||||
bootstrapGitLabCmd.Flags().DurationVar(&glInterval, "interval", time.Minute, "sync interval")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&glHostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&glSSHHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||
bootstrapGitLabCmd.Flags().StringVar(&glPath, "path", "", "repository path, when specified the cluster sync will be scoped to this path")
|
||||
bootstrapGitLabCmd.Flags().Var(&glPath, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||
|
||||
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
||||
}
|
||||
@@ -95,10 +103,32 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("%s environment variable not found", git.GitLabTokenName)
|
||||
}
|
||||
|
||||
projectNameIsValid, err := regexp.MatchString(gitlabProjectRegex, glRepository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !projectNameIsValid {
|
||||
return fmt.Errorf("%s is an invalid project name for gitlab.\nIt can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.", glRepository)
|
||||
}
|
||||
|
||||
if err := bootstrapValidate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, namespace, filepath.ToSlash(glPath.String()))
|
||||
|
||||
if bootstrapPathDiffers {
|
||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
||||
}
|
||||
|
||||
repository, err := git.NewRepository(glRepository, glOwner, glHostname, glToken, "flux", glOwner+"@users.noreply.gitlab.com")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -113,20 +143,12 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
IsPersonal: glPersonal,
|
||||
}
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
// create GitLab project if doesn't exists
|
||||
logger.Actionf("connecting to %s", glHostname)
|
||||
changed, err := provider.CreateRepository(ctx, repository)
|
||||
@@ -145,13 +167,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// generate install manifests
|
||||
logger.Generatef("generating manifests")
|
||||
manifest, err := generateInstallManifests(glPath, namespace, tmpDir, bootstrapManifestsPath)
|
||||
installManifest, err := generateInstallManifests(glPath.String(), namespace, tmpDir, bootstrapManifestsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stage install manifests
|
||||
changed, err = repository.Commit(ctx, path.Join(glPath, namespace), "Add manifests")
|
||||
changed, err = repository.Commit(ctx, path.Join(glPath.String(), namespace), "Add manifests")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -172,7 +194,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if isInstall {
|
||||
// apply install manifests
|
||||
logger.Actionf("installing components in %s namespace", namespace)
|
||||
if err := applyInstallManifests(ctx, manifest, bootstrapComponents); err != nil {
|
||||
if err := applyInstallManifests(ctx, installManifest, bootstrapComponents()); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("install completed")
|
||||
@@ -225,12 +247,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// configure repo synchronization
|
||||
logger.Actionf("generating sync manifests")
|
||||
if err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, glPath, tmpDir, glInterval); err != nil {
|
||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapBranch, namespace, namespace, filepath.ToSlash(glPath.String()), tmpDir, glInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// commit and push manifests
|
||||
if changed, err = repository.Commit(ctx, path.Join(glPath, namespace), "Add manifests"); err != nil {
|
||||
if changed, err = repository.Commit(ctx, path.Join(glPath.String(), namespace), "Add manifests"); err != nil {
|
||||
return err
|
||||
} else if changed {
|
||||
if err := repository.Push(ctx); err != nil {
|
||||
@@ -241,7 +264,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// apply manifests and waiting for sync
|
||||
logger.Actionf("applying sync manifests")
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, glPath, tmpDir); err != nil {
|
||||
if err := applySyncManifests(ctx, kubeClient, namespace, namespace, syncManifests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
@@ -133,7 +132,7 @@ func kubectlCheck(ctx context.Context, version string) bool {
|
||||
}
|
||||
|
||||
func kubernetesCheck(version string) bool {
|
||||
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||
cfg, err := utils.KubeConfig(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||
return false
|
||||
|
||||
@@ -37,7 +37,7 @@ command -v flux >/dev/null && . <(flux completion zsh) && compdef _flux flux
|
||||
or write a cached file in one of the completion directories in your ${fpath}:
|
||||
|
||||
echo "${fpath// /\n}" | grep -i completion
|
||||
flux completions zsh > _flux
|
||||
flux completion zsh > _flux
|
||||
|
||||
mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
|
||||
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
||||
|
||||
@@ -17,13 +17,19 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var createCmd = &cobra.Command{
|
||||
@@ -46,6 +52,78 @@ func init() {
|
||||
rootCmd.AddCommand(createCmd)
|
||||
}
|
||||
|
||||
// upsertable is an interface for values that can be used in `upsert`.
|
||||
type upsertable interface {
|
||||
adapter
|
||||
named
|
||||
}
|
||||
|
||||
// upsert updates or inserts an object. Instead of providing the
|
||||
// object itself, you provide a named (as in Name and Namespace)
|
||||
// template value, and a mutate function which sets the values you
|
||||
// want to update. The mutate function is nullary -- you mutate a
|
||||
// value in the closure, e.g., by doing this:
|
||||
//
|
||||
// var existing Value
|
||||
// existing.Name = name
|
||||
// existing.Namespace = ns
|
||||
// upsert(ctx, client, valueAdapter{&value}, func() error {
|
||||
// value.Spec = onePreparedEarlier
|
||||
// })
|
||||
func (names apiType) upsert(ctx context.Context, kubeClient client.Client, object upsertable, mutate func() error) (types.NamespacedName, error) {
|
||||
nsname := types.NamespacedName{
|
||||
Namespace: object.GetNamespace(),
|
||||
Name: object.GetName(),
|
||||
}
|
||||
|
||||
op, err := controllerutil.CreateOrUpdate(ctx, kubeClient, object.asRuntimeObject(), mutate)
|
||||
if err != nil {
|
||||
return nsname, err
|
||||
}
|
||||
|
||||
switch op {
|
||||
case controllerutil.OperationResultCreated:
|
||||
logger.Successf("%s created", names.kind)
|
||||
case controllerutil.OperationResultUpdated:
|
||||
logger.Successf("%s updated", names.kind)
|
||||
}
|
||||
return nsname, nil
|
||||
}
|
||||
|
||||
type upsertWaitable interface {
|
||||
upsertable
|
||||
statusable
|
||||
}
|
||||
|
||||
// upsertAndWait encodes the pattern of creating or updating a
|
||||
// resource, then waiting for it to reconcile. See the note on
|
||||
// `upsert` for how to work with the `mutate` argument.
|
||||
func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) // NB globals
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Generatef("generating %s", names.kind)
|
||||
logger.Actionf("applying %s", names.kind)
|
||||
|
||||
namespacedName, err := imageRepositoryType.upsert(ctx, kubeClient, object, mutate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for %s reconciliation", names.kind)
|
||||
if err := wait.PollImmediate(pollInterval, timeout,
|
||||
isReady(ctx, kubeClient, namespacedName, object)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("%s reconciliation completed", names.kind)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseLabels() (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
for _, label := range labels {
|
||||
|
||||
38
cmd/flux/create_image.go
Normal file
38
cmd/flux/create_image.go
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const createImageLong = `
|
||||
The create image sub-commands work with image automation objects; that is,
|
||||
object controlling updates to git based on e.g., new container images
|
||||
being available.`
|
||||
|
||||
var createImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Create or update resources dealing with image automation",
|
||||
Long: strings.TrimSpace(createImageLong),
|
||||
}
|
||||
|
||||
func init() {
|
||||
createCmd.AddCommand(createImageCmd)
|
||||
}
|
||||
110
cmd/flux/create_image_policy.go
Normal file
110
cmd/flux/create_image_policy.go
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var createImagePolicyCmd = &cobra.Command{
|
||||
Use: "policy <name>",
|
||||
Short: "Create or update an ImagePolicy object",
|
||||
Long: `The create image policy command generates an ImagePolicy resource.
|
||||
An ImagePolicy object calculates a "latest image" given an image
|
||||
repository and a policy, e.g., semver.
|
||||
|
||||
The image that sorts highest according to the policy is recorded in
|
||||
the status of the object.`,
|
||||
RunE: createImagePolicyRun}
|
||||
|
||||
type imagePolicyFlags struct {
|
||||
imageRef string
|
||||
semver string
|
||||
}
|
||||
|
||||
var imagePolicyArgs = imagePolicyFlags{}
|
||||
|
||||
func init() {
|
||||
flags := createImagePolicyCmd.Flags()
|
||||
flags.StringVar(&imagePolicyArgs.imageRef, "image-ref", "", "the name of an image repository object")
|
||||
flags.StringVar(&imagePolicyArgs.semver, "semver", "", "a semver range to apply to tags; e.g., '1.x'")
|
||||
|
||||
createImageCmd.AddCommand(createImagePolicyCmd)
|
||||
}
|
||||
|
||||
// getObservedGeneration is implemented here, since it's not
|
||||
// (presently) needed elsewhere.
|
||||
func (obj imagePolicyAdapter) getObservedGeneration() int64 {
|
||||
return obj.ImagePolicy.Status.ObservedGeneration
|
||||
}
|
||||
|
||||
func createImagePolicyRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("ImagePolicy name is required")
|
||||
}
|
||||
objectName := args[0]
|
||||
|
||||
if imagePolicyArgs.imageRef == "" {
|
||||
return fmt.Errorf("the name of an ImageRepository in the namespace is required (--image-ref)")
|
||||
}
|
||||
|
||||
labels, err := parseLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var policy = imagev1.ImagePolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: objectName,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: imagev1.ImagePolicySpec{
|
||||
ImageRepositoryRef: corev1.LocalObjectReference{
|
||||
Name: imagePolicyArgs.imageRef,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
switch {
|
||||
case imagePolicyArgs.semver != "":
|
||||
policy.Spec.Policy.SemVer = &imagev1.SemVerPolicy{
|
||||
Range: imagePolicyArgs.semver,
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("a policy must be provided with --semver")
|
||||
}
|
||||
|
||||
if export {
|
||||
return printExport(exportImagePolicy(&policy))
|
||||
}
|
||||
|
||||
var existing imagev1.ImagePolicy
|
||||
copyName(&existing, &policy)
|
||||
err = imagePolicyType.upsertAndWait(imagePolicyAdapter{&existing}, func() error {
|
||||
existing.Spec = policy.Spec
|
||||
existing.SetLabels(policy.Labels)
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
110
cmd/flux/create_image_repository.go
Normal file
110
cmd/flux/create_image_repository.go
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var createImageRepositoryCmd = &cobra.Command{
|
||||
Use: "repository <name>",
|
||||
Short: "Create or update an ImageRepository object",
|
||||
Long: `The create image repository command generates an ImageRepository resource.
|
||||
An ImageRepository object specifies an image repository to scan.`,
|
||||
RunE: createImageRepositoryRun,
|
||||
}
|
||||
|
||||
type imageRepoFlags struct {
|
||||
image string
|
||||
secretRef string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
var imageRepoArgs = imageRepoFlags{}
|
||||
|
||||
func init() {
|
||||
flags := createImageRepositoryCmd.Flags()
|
||||
flags.StringVar(&imageRepoArgs.image, "image", "", "the image repository to scan; e.g., library/alpine")
|
||||
flags.StringVar(&imageRepoArgs.secretRef, "secret-ref", "", "the name of a docker-registry secret to use for credentials")
|
||||
// NB there is already a --timeout in the global flags, for
|
||||
// controlling timeout on operations while e.g., creating objects.
|
||||
flags.DurationVar(&imageRepoArgs.timeout, "scan-timeout", 0, "a timeout for scanning; this defaults to the interval if not set")
|
||||
|
||||
createImageCmd.AddCommand(createImageRepositoryCmd)
|
||||
}
|
||||
|
||||
func createImageRepositoryRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("ImageRepository name is required")
|
||||
}
|
||||
objectName := args[0]
|
||||
|
||||
if imageRepoArgs.image == "" {
|
||||
return fmt.Errorf("an image repository (--image) is required")
|
||||
}
|
||||
|
||||
if _, err := name.NewRepository(imageRepoArgs.image); err != nil {
|
||||
return fmt.Errorf("unable to parse image value: %w", err)
|
||||
}
|
||||
|
||||
labels, err := parseLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var repo = imagev1.ImageRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: objectName,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: imagev1.ImageRepositorySpec{
|
||||
Image: imageRepoArgs.image,
|
||||
Interval: metav1.Duration{Duration: interval},
|
||||
},
|
||||
}
|
||||
if imageRepoArgs.timeout != 0 {
|
||||
repo.Spec.Timeout = &metav1.Duration{Duration: imageRepoArgs.timeout}
|
||||
}
|
||||
if imageRepoArgs.secretRef != "" {
|
||||
repo.Spec.SecretRef = &corev1.LocalObjectReference{
|
||||
Name: imageRepoArgs.secretRef,
|
||||
}
|
||||
}
|
||||
|
||||
if export {
|
||||
return printExport(exportImageRepository(&repo))
|
||||
}
|
||||
|
||||
// a temp value for use with the rest
|
||||
var existing imagev1.ImageRepository
|
||||
copyName(&existing, &repo)
|
||||
err = imageRepositoryType.upsertAndWait(imageRepositoryAdapter{&existing}, func() error {
|
||||
existing.Spec = repo.Spec
|
||||
existing.Labels = repo.Labels
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
113
cmd/flux/create_image_updateauto.go
Normal file
113
cmd/flux/create_image_updateauto.go
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var createImageUpdateCmd = &cobra.Command{
|
||||
Use: "update <name>",
|
||||
Short: "Create or update an ImageUpdateAutomation object",
|
||||
Long: `The create image update command generates an ImageUpdateAutomation resource.
|
||||
An ImageUpdateAutomation object specifies an automated update to images
|
||||
mentioned in YAMLs in a git repository.`,
|
||||
RunE: createImageUpdateRun,
|
||||
}
|
||||
|
||||
type imageUpdateFlags struct {
|
||||
// git checkout spec
|
||||
gitRepoRef string
|
||||
branch string
|
||||
// commit spec
|
||||
commitTemplate string
|
||||
authorName string
|
||||
authorEmail string
|
||||
}
|
||||
|
||||
var imageUpdateArgs = imageUpdateFlags{}
|
||||
|
||||
func init() {
|
||||
flags := createImageUpdateCmd.Flags()
|
||||
flags.StringVar(&imageUpdateArgs.gitRepoRef, "git-repo-ref", "", "the name of a GitRepository resource with details of the upstream git repository")
|
||||
flags.StringVar(&imageUpdateArgs.branch, "branch", "", "the branch to push commits to")
|
||||
flags.StringVar(&imageUpdateArgs.commitTemplate, "commit-template", "", "a template for commit messages")
|
||||
flags.StringVar(&imageUpdateArgs.authorName, "author-name", "", "the name to use for commit author")
|
||||
flags.StringVar(&imageUpdateArgs.authorEmail, "author-email", "", "the email to use for commit author")
|
||||
|
||||
createImageCmd.AddCommand(createImageUpdateCmd)
|
||||
}
|
||||
|
||||
func createImageUpdateRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("ImageUpdateAutomation name is required")
|
||||
}
|
||||
objectName := args[0]
|
||||
|
||||
if imageUpdateArgs.gitRepoRef == "" {
|
||||
return fmt.Errorf("a reference to a GitRepository is required (--git-repo-ref)")
|
||||
}
|
||||
|
||||
labels, err := parseLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var update = autov1.ImageUpdateAutomation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: objectName,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: autov1.ImageUpdateAutomationSpec{
|
||||
Checkout: autov1.GitCheckoutSpec{
|
||||
GitRepositoryRef: corev1.LocalObjectReference{
|
||||
Name: imageUpdateArgs.gitRepoRef,
|
||||
},
|
||||
Branch: imageUpdateArgs.branch,
|
||||
},
|
||||
Interval: metav1.Duration{Duration: interval},
|
||||
Update: autov1.UpdateStrategy{
|
||||
Setters: &autov1.SettersStrategy{},
|
||||
},
|
||||
Commit: autov1.CommitSpec{
|
||||
AuthorName: imageUpdateArgs.authorName,
|
||||
AuthorEmail: imageUpdateArgs.authorEmail,
|
||||
MessageTemplate: imageUpdateArgs.commitTemplate,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if export {
|
||||
return printExport(exportImageUpdate(&update))
|
||||
}
|
||||
|
||||
var existing autov1.ImageUpdateAutomation
|
||||
copyName(&existing, &update)
|
||||
err = imageUpdateAutomationType.upsertAndWait(imageUpdateAutomationAdapter{&existing}, func() error {
|
||||
existing.Spec = update.Spec
|
||||
existing.Labels = update.Labels
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -74,7 +74,7 @@ var createKsCmd = &cobra.Command{
|
||||
|
||||
var (
|
||||
ksSource flags.KustomizationSource
|
||||
ksPath string
|
||||
ksPath flags.SafeRelativePath = "./"
|
||||
ksPrune bool
|
||||
ksDependsOn []string
|
||||
ksValidation string
|
||||
@@ -88,7 +88,7 @@ var (
|
||||
|
||||
func init() {
|
||||
createKsCmd.Flags().Var(&ksSource, "source", ksSource.Description())
|
||||
createKsCmd.Flags().StringVar(&ksPath, "path", "./", "path to the directory containing a kustomization.yaml file")
|
||||
createKsCmd.Flags().Var(&ksPath, "path", "path to the directory containing a kustomization.yaml file")
|
||||
createKsCmd.Flags().BoolVar(&ksPrune, "prune", false, "enable garbage collection")
|
||||
createKsCmd.Flags().StringArrayVar(&ksHealthCheck, "health-check", nil, "workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'")
|
||||
createKsCmd.Flags().DurationVar(&ksHealthTimeout, "health-check-timeout", 2*time.Minute, "timeout of health checking operations")
|
||||
@@ -110,7 +110,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if ksPath == "" {
|
||||
return fmt.Errorf("path is required")
|
||||
}
|
||||
if !strings.HasPrefix(ksPath, "./") {
|
||||
if !strings.HasPrefix(ksPath.String(), "./") {
|
||||
return fmt.Errorf("path must begin with ./")
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
Interval: metav1.Duration{
|
||||
Duration: interval,
|
||||
},
|
||||
Path: ksPath,
|
||||
Path: ksPath.String(),
|
||||
Prune: ksPrune,
|
||||
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
||||
Kind: ksSource.Kind,
|
||||
|
||||
@@ -17,11 +17,15 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -35,6 +39,32 @@ func init() {
|
||||
createCmd.AddCommand(createSecretCmd)
|
||||
}
|
||||
|
||||
func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.Secret) error {
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: secret.GetNamespace(),
|
||||
Name: secret.GetName(),
|
||||
}
|
||||
|
||||
var existing corev1.Secret
|
||||
err := kubeClient.Get(ctx, namespacedName, &existing)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if err := kubeClient.Create(ctx, &secret); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
existing.StringData = secret.StringData
|
||||
if err := kubeClient.Update(ctx, &existing); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func exportSecret(secret corev1.Secret) error {
|
||||
secret.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"crypto/elliptic"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/ssh"
|
||||
)
|
||||
|
||||
var createSecretGitCmd = &cobra.Command{
|
||||
@@ -53,7 +55,7 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
|
||||
# Create a Git SSH secret on disk and print the deploy key
|
||||
flux create secret git podinfo-auth \
|
||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||
--export > podinfo-auth.yaml
|
||||
--export > podinfo-auth.yaml
|
||||
|
||||
yq read podinfo-auth.yaml 'data."identity.pub"' | base64 --decode
|
||||
|
||||
@@ -61,7 +63,7 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
|
||||
flux create secret git podinfo-auth \
|
||||
--namespace=apps \
|
||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||
--export > podinfo-auth.yaml
|
||||
--export > podinfo-auth.yaml
|
||||
|
||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||
--in-place podinfo-auth.yaml
|
||||
@@ -82,9 +84,9 @@ func init() {
|
||||
createSecretGitCmd.Flags().StringVar(&secretGitURL, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
||||
createSecretGitCmd.Flags().StringVarP(&secretGitUsername, "username", "u", "", "basic authentication username")
|
||||
createSecretGitCmd.Flags().StringVarP(&secretGitPassword, "password", "p", "", "basic authentication password")
|
||||
createSecretGitCmd.Flags().Var(&secretGitKeyAlgorithm, "ssh-key-algorithm", sourceGitKeyAlgorithm.Description())
|
||||
createSecretGitCmd.Flags().Var(&secretGitRSABits, "ssh-rsa-bits", sourceGitRSABits.Description())
|
||||
createSecretGitCmd.Flags().Var(&secretGitECDSACurve, "ssh-ecdsa-curve", sourceGitECDSACurve.Description())
|
||||
createSecretGitCmd.Flags().Var(&secretGitKeyAlgorithm, "ssh-key-algorithm", secretGitKeyAlgorithm.Description())
|
||||
createSecretGitCmd.Flags().Var(&secretGitRSABits, "ssh-rsa-bits", secretGitRSABits.Description())
|
||||
createSecretGitCmd.Flags().Var(&secretGitECDSACurve, "ssh-ecdsa-curve", secretGitECDSACurve.Description())
|
||||
|
||||
createSecretCmd.AddCommand(createSecretGitCmd)
|
||||
}
|
||||
@@ -122,7 +124,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
switch u.Scheme {
|
||||
case "ssh":
|
||||
pair, err := generateKeyPair(ctx)
|
||||
pair, err := generateKeyPair(ctx, secretGitKeyAlgorithm, secretGitRSABits, secretGitECDSACurve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -171,3 +173,34 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateKeyPair(ctx context.Context, alg flags.PublicKeyAlgorithm, rsa flags.RSAKeyBits, ecdsa flags.ECDSACurve) (*ssh.KeyPair, error) {
|
||||
var keyGen ssh.KeyPairGenerator
|
||||
switch algorithm := alg.String(); algorithm {
|
||||
case "rsa":
|
||||
keyGen = ssh.NewRSAGenerator(int(rsa))
|
||||
case "ecdsa":
|
||||
keyGen = ssh.NewECDSAGenerator(ecdsa.Curve)
|
||||
case "ed25519":
|
||||
keyGen = ssh.NewEd25519Generator()
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported public key algorithm: %s", algorithm)
|
||||
}
|
||||
pair, err := keyGen.Generate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("key pair generation failed, error: %w", err)
|
||||
}
|
||||
return pair, nil
|
||||
}
|
||||
|
||||
func scanHostKey(ctx context.Context, url *url.URL) ([]byte, error) {
|
||||
host := url.Host
|
||||
if url.Port() == "" {
|
||||
host = host + ":22"
|
||||
}
|
||||
hostKey, err := ssh.ScanHostKey(host, 30*time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SSH key scan for host %s failed, error: %w", host, err)
|
||||
}
|
||||
return hostKey, nil
|
||||
}
|
||||
|
||||
141
cmd/flux/create_secret_helm.go
Normal file
141
cmd/flux/create_secret_helm.go
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2021 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var createSecretHelmCmd = &cobra.Command{
|
||||
Use: "helm [name]",
|
||||
Short: "Create or update a Kubernetes secret for Helm repository authentication",
|
||||
Long: `
|
||||
The create secret helm command generates a Kubernetes secret with basic authentication credentials.`,
|
||||
Example: ` # Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
|
||||
|
||||
flux create secret helm repo-auth \
|
||||
--namespace=my-namespace \
|
||||
--username=my-username \
|
||||
--password=my-password \
|
||||
--export > repo-auth.yaml
|
||||
|
||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||
--in-place repo-auth.yaml
|
||||
|
||||
# Create an authentication secret using a custom TLS cert
|
||||
flux create secret helm repo-auth \
|
||||
--username=username \
|
||||
--password=password \
|
||||
--cert-file=./cert.crt \
|
||||
--key-file=./key.crt \
|
||||
--ca-file=./ca.crt
|
||||
`,
|
||||
RunE: createSecretHelmCmdRun,
|
||||
}
|
||||
|
||||
var (
|
||||
secretHelmUsername string
|
||||
secretHelmPassword string
|
||||
secretHelmCertFile string
|
||||
secretHelmKeyFile string
|
||||
secretHelmCAFile string
|
||||
)
|
||||
|
||||
func init() {
|
||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmUsername, "username", "u", "", "basic authentication username")
|
||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmPassword, "password", "p", "", "basic authentication password")
|
||||
createSecretHelmCmd.Flags().StringVar(&secretHelmCertFile, "cert-file", "", "TLS authentication cert file path")
|
||||
createSecretHelmCmd.Flags().StringVar(&secretHelmKeyFile, "key-file", "", "TLS authentication key file path")
|
||||
createSecretHelmCmd.Flags().StringVar(&secretHelmCAFile, "ca-file", "", "TLS authentication CA file path")
|
||||
|
||||
createSecretCmd.AddCommand(createSecretHelmCmd)
|
||||
}
|
||||
|
||||
func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("secret name is required")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
secretLabels, err := parseLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Labels: secretLabels,
|
||||
},
|
||||
StringData: map[string]string{},
|
||||
}
|
||||
|
||||
if secretHelmUsername != "" && secretHelmPassword != "" {
|
||||
secret.StringData["username"] = secretHelmUsername
|
||||
secret.StringData["password"] = secretHelmPassword
|
||||
}
|
||||
|
||||
if secretHelmCertFile != "" && secretHelmKeyFile != "" {
|
||||
cert, err := ioutil.ReadFile(secretHelmCertFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository cert file '%s': %w", secretHelmCertFile, err)
|
||||
}
|
||||
secret.StringData["certFile"] = string(cert)
|
||||
|
||||
key, err := ioutil.ReadFile(secretHelmKeyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository key file '%s': %w", secretHelmKeyFile, err)
|
||||
}
|
||||
secret.StringData["keyFile"] = string(key)
|
||||
}
|
||||
|
||||
if secretHelmCAFile != "" {
|
||||
ca, err := ioutil.ReadFile(secretHelmCAFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read repository CA file '%s': %w", secretHelmCAFile, err)
|
||||
}
|
||||
secret.StringData["caFile"] = string(ca)
|
||||
}
|
||||
|
||||
if export {
|
||||
return exportSecret(secret)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Actionf("secret '%s' created in '%s' namespace", name, namespace)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -23,13 +23,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -40,7 +34,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/ssh"
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||
)
|
||||
|
||||
var createSourceGitCmd = &cobra.Command{
|
||||
@@ -195,7 +192,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
withAuth = true
|
||||
} else if u.Scheme == "ssh" {
|
||||
logger.Generatef("generating deploy key pair")
|
||||
pair, err := generateKeyPair(ctx)
|
||||
pair, err := generateKeyPair(ctx, sourceGitKeyAlgorithm, sourceGitRSABits, sourceGitECDSACurve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -288,63 +285,6 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateKeyPair(ctx context.Context) (*ssh.KeyPair, error) {
|
||||
var keyGen ssh.KeyPairGenerator
|
||||
switch algorithm := sourceGitKeyAlgorithm.String(); algorithm {
|
||||
case "rsa":
|
||||
keyGen = ssh.NewRSAGenerator(int(sourceGitRSABits))
|
||||
case "ecdsa":
|
||||
keyGen = ssh.NewECDSAGenerator(sourceGitECDSACurve.Curve)
|
||||
case "ed25519":
|
||||
keyGen = ssh.NewEd25519Generator()
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported public key algorithm: %s", algorithm)
|
||||
}
|
||||
pair, err := keyGen.Generate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("key pair generation failed, error: %w", err)
|
||||
}
|
||||
return pair, nil
|
||||
}
|
||||
|
||||
func scanHostKey(ctx context.Context, url *url.URL) ([]byte, error) {
|
||||
host := url.Host
|
||||
if url.Port() == "" {
|
||||
host = host + ":22"
|
||||
}
|
||||
hostKey, err := ssh.ScanHostKey(host, 30*time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SSH key scan for host %s failed, error: %w", host, err)
|
||||
}
|
||||
return hostKey, nil
|
||||
}
|
||||
|
||||
func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.Secret) error {
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: secret.GetNamespace(),
|
||||
Name: secret.GetName(),
|
||||
}
|
||||
|
||||
var existing corev1.Secret
|
||||
err := kubeClient.Get(ctx, namespacedName, &existing)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if err := kubeClient.Create(ctx, &secret); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
existing.StringData = secret.StringData
|
||||
if err := kubeClient.Update(ctx, &existing); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func upsertGitRepository(ctx context.Context, kubeClient client.Client,
|
||||
gitRepository *sourcev1.GitRepository) (types.NamespacedName, error) {
|
||||
namespacedName := types.NamespacedName{
|
||||
|
||||
@@ -17,7 +17,14 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -36,3 +43,52 @@ func init() {
|
||||
|
||||
rootCmd.AddCommand(deleteCmd)
|
||||
}
|
||||
|
||||
type deleteCommand struct {
|
||||
apiType
|
||||
object adapter // for getting the value, and later deleting it
|
||||
}
|
||||
|
||||
func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("%s name is required", del.humanKind)
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
err = kubeClient.Get(ctx, namespacedName, del.object.asRuntimeObject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !deleteSilent {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Are you sure you want to delete this " + del.humanKind,
|
||||
IsConfirm: true,
|
||||
}
|
||||
if _, err := prompt.Run(); err != nil {
|
||||
return fmt.Errorf("aborting")
|
||||
}
|
||||
}
|
||||
|
||||
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, namespace)
|
||||
err = kubeClient.Delete(ctx, del.object.asRuntimeObject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("%s deleted", del.humanKind)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
31
cmd/flux/delete_image.go
Normal file
31
cmd/flux/delete_image.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var deleteAutoCmd = &cobra.Command{
|
||||
Use: "auto",
|
||||
Short: "Delete automation objects",
|
||||
Long: "The delete auto sub-commands delete automation objects.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteCmd.AddCommand(deleteAutoCmd)
|
||||
}
|
||||
40
cmd/flux/delete_image_policy.go
Normal file
40
cmd/flux/delete_image_policy.go
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var deleteImagePolicyCmd = &cobra.Command{
|
||||
Use: "image-policy [name]",
|
||||
Short: "Delete an ImagePolicy object",
|
||||
Long: "The delete auto image-policy command deletes the given ImagePolicy from the cluster.",
|
||||
Example: ` # Delete an image policy
|
||||
flux delete auto image-policy alpine3.x
|
||||
`,
|
||||
RunE: deleteCommand{
|
||||
apiType: imagePolicyType,
|
||||
object: universalAdapter{&imagev1.ImagePolicy{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteAutoCmd.AddCommand(deleteImagePolicyCmd)
|
||||
}
|
||||
40
cmd/flux/delete_image_repository.go
Normal file
40
cmd/flux/delete_image_repository.go
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var deleteImageRepositoryCmd = &cobra.Command{
|
||||
Use: "image-repository [name]",
|
||||
Short: "Delete an ImageRepository object",
|
||||
Long: "The delete auto image-repository command deletes the given ImageRepository from the cluster.",
|
||||
Example: ` # Delete an image repository
|
||||
flux delete auto image-repository alpine
|
||||
`,
|
||||
RunE: deleteCommand{
|
||||
apiType: imageRepositoryType,
|
||||
object: universalAdapter{&imagev1.ImageRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteAutoCmd.AddCommand(deleteImageRepositoryCmd)
|
||||
}
|
||||
40
cmd/flux/delete_image_updateauto.go
Normal file
40
cmd/flux/delete_image_updateauto.go
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var deleteImageUpdateCmd = &cobra.Command{
|
||||
Use: "image-update [name]",
|
||||
Short: "Delete an ImageUpdateAutomation object",
|
||||
Long: "The delete auto image-update command deletes the given ImageUpdateAutomation from the cluster.",
|
||||
Example: ` # Delete an image update automation
|
||||
flux delete auto image-update latest-images
|
||||
`,
|
||||
RunE: deleteCommand{
|
||||
apiType: imageUpdateAutomationType,
|
||||
object: universalAdapter{&autov1.ImageUpdateAutomation{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
deleteAutoCmd.AddCommand(deleteImageUpdateCmd)
|
||||
}
|
||||
@@ -18,8 +18,15 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var exportCmd = &cobra.Command{
|
||||
@@ -38,6 +45,80 @@ func init() {
|
||||
rootCmd.AddCommand(exportCmd)
|
||||
}
|
||||
|
||||
// exportable represents a type that you can fetch from the Kubernetes
|
||||
// API, then tidy up for serialising.
|
||||
type exportable interface {
|
||||
adapter
|
||||
export() interface{}
|
||||
}
|
||||
|
||||
// exportableList represents a type that has a list of values, each of
|
||||
// which is exportable.
|
||||
type exportableList interface {
|
||||
adapter
|
||||
len() int
|
||||
exportItem(i int) interface{}
|
||||
}
|
||||
|
||||
type exportCommand struct {
|
||||
object exportable
|
||||
list exportableList
|
||||
}
|
||||
|
||||
func (export exportCommand) run(cmd *cobra.Command, args []string) error {
|
||||
if !exportAll && len(args) < 1 {
|
||||
return fmt.Errorf("name is required")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exportAll {
|
||||
err = kubeClient.List(ctx, export.list.asRuntimeObject(), client.InNamespace(namespace))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if export.list.len() == 0 {
|
||||
logger.Failuref("no objects found in %s namespace", namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < export.list.len(); i++ {
|
||||
if err = printExport(export.list.exportItem(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
name := args[0]
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
err = kubeClient.Get(ctx, namespacedName, export.object.asRuntimeObject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printExport(export.object.export())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printExport(export interface{}) error {
|
||||
data, err := yaml.Marshal(export)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("---")
|
||||
fmt.Println(resourceToString(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceToString(data []byte) string {
|
||||
data = bytes.Replace(data, []byte(" creationTimestamp: null\n"), []byte(""), 1)
|
||||
data = bytes.Replace(data, []byte("status: {}\n"), []byte(""), 1)
|
||||
|
||||
31
cmd/flux/export_image.go
Normal file
31
cmd/flux/export_image.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var exportImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Export image automation objects",
|
||||
Long: "The export image sub-commands export image automation objects in YAML format.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
exportCmd.AddCommand(exportImageCmd)
|
||||
}
|
||||
72
cmd/flux/export_image_policy.go
Normal file
72
cmd/flux/export_image_policy.go
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var exportImagePolicyCmd = &cobra.Command{
|
||||
Use: "policy [name]",
|
||||
Short: "Export ImagePolicy resources in YAML format",
|
||||
Long: "The export image policy command exports one or all ImagePolicy resources in YAML format.",
|
||||
Example: ` # Export all ImagePolicy resources
|
||||
flux export image policy --all > image-policies.yaml
|
||||
|
||||
# Export a specific policy
|
||||
flux export image policy alpine1x > alpine1x.yaml
|
||||
`,
|
||||
RunE: exportCommand{
|
||||
object: imagePolicyAdapter{&imagev1.ImagePolicy{}},
|
||||
list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
exportImageCmd.AddCommand(exportImagePolicyCmd)
|
||||
}
|
||||
|
||||
// Export returns a ImagePolicy value which has extraneous information
|
||||
// stripped out.
|
||||
func exportImagePolicy(item *imagev1.ImagePolicy) interface{} {
|
||||
gvk := imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)
|
||||
export := imagev1.ImagePolicy{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: gvk.Kind,
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: item.Name,
|
||||
Namespace: item.Namespace,
|
||||
Labels: item.Labels,
|
||||
Annotations: item.Annotations,
|
||||
},
|
||||
Spec: item.Spec,
|
||||
}
|
||||
return export
|
||||
}
|
||||
|
||||
func (ex imagePolicyAdapter) export() interface{} {
|
||||
return exportImagePolicy(ex.ImagePolicy)
|
||||
}
|
||||
|
||||
func (ex imagePolicyListAdapter) exportItem(i int) interface{} {
|
||||
return exportImagePolicy(&ex.ImagePolicyList.Items[i])
|
||||
}
|
||||
70
cmd/flux/export_image_repository.go
Normal file
70
cmd/flux/export_image_repository.go
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var exportImageRepositoryCmd = &cobra.Command{
|
||||
Use: "repository [name]",
|
||||
Short: "Export ImageRepository resources in YAML format",
|
||||
Long: "The export image repository command exports one or all ImageRepository resources in YAML format.",
|
||||
Example: ` # Export all ImageRepository resources
|
||||
flux export image repository --all > image-repositories.yaml
|
||||
|
||||
# Export a specific ImageRepository resource
|
||||
flux export image repository alpine > alpine.yaml
|
||||
`,
|
||||
RunE: exportCommand{
|
||||
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
||||
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
exportImageCmd.AddCommand(exportImageRepositoryCmd)
|
||||
}
|
||||
|
||||
func exportImageRepository(repo *imagev1.ImageRepository) interface{} {
|
||||
gvk := imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)
|
||||
export := imagev1.ImageRepository{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: gvk.Kind,
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: repo.Name,
|
||||
Namespace: repo.Namespace,
|
||||
Labels: repo.Labels,
|
||||
Annotations: repo.Annotations,
|
||||
},
|
||||
Spec: repo.Spec,
|
||||
}
|
||||
return export
|
||||
}
|
||||
|
||||
func (ex imageRepositoryAdapter) export() interface{} {
|
||||
return exportImageRepository(ex.ImageRepository)
|
||||
}
|
||||
|
||||
func (ex imageRepositoryListAdapter) exportItem(i int) interface{} {
|
||||
return exportImageRepository(&ex.ImageRepositoryList.Items[i])
|
||||
}
|
||||
72
cmd/flux/export_image_updateauto.go
Normal file
72
cmd/flux/export_image_updateauto.go
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var exportImageUpdateCmd = &cobra.Command{
|
||||
Use: "update [name]",
|
||||
Short: "Export ImageUpdateAutomation resources in YAML format",
|
||||
Long: "The export image update command exports one or all ImageUpdateAutomation resources in YAML format.",
|
||||
Example: ` # Export all ImageUpdateAutomation resources
|
||||
flux export image update --all > updates.yaml
|
||||
|
||||
# Export a specific automation
|
||||
flux export image update latest-images > latest.yaml
|
||||
`,
|
||||
RunE: exportCommand{
|
||||
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
||||
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
exportImageCmd.AddCommand(exportImageUpdateCmd)
|
||||
}
|
||||
|
||||
// exportImageUpdate returns a value which has extraneous information
|
||||
// stripped out.
|
||||
func exportImageUpdate(item *autov1.ImageUpdateAutomation) interface{} {
|
||||
gvk := autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)
|
||||
export := autov1.ImageUpdateAutomation{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: gvk.Kind,
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: item.Name,
|
||||
Namespace: item.Namespace,
|
||||
Labels: item.Labels,
|
||||
Annotations: item.Annotations,
|
||||
},
|
||||
Spec: item.Spec,
|
||||
}
|
||||
return export
|
||||
}
|
||||
|
||||
func (ex imageUpdateAutomationAdapter) export() interface{} {
|
||||
return exportImageUpdate(ex.ImageUpdateAutomation)
|
||||
}
|
||||
|
||||
func (ex imageUpdateAutomationListAdapter) exportItem(i int) interface{} {
|
||||
return exportImageUpdate(&ex.ImageUpdateAutomationList.Items[i])
|
||||
}
|
||||
@@ -17,7 +17,17 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var getCmd = &cobra.Command{
|
||||
@@ -33,3 +43,66 @@ func init() {
|
||||
"list the requested object(s) across all namespaces")
|
||||
rootCmd.AddCommand(getCmd)
|
||||
}
|
||||
|
||||
type summarisable interface {
|
||||
adapter
|
||||
len() int
|
||||
summariseItem(i int, includeNamespace bool) []string
|
||||
headers(includeNamespace bool) []string
|
||||
}
|
||||
|
||||
// --- these help with implementations of summarisable
|
||||
|
||||
func statusAndMessage(conditions []metav1.Condition) (string, string) {
|
||||
if c := apimeta.FindStatusCondition(conditions, meta.ReadyCondition); c != nil {
|
||||
return string(c.Status), c.Message
|
||||
}
|
||||
return string(metav1.ConditionFalse), "waiting to be reconciled"
|
||||
}
|
||||
|
||||
func nameColumns(item named, includeNamespace bool) []string {
|
||||
if includeNamespace {
|
||||
return []string{item.GetNamespace(), item.GetName()}
|
||||
}
|
||||
return []string{item.GetName()}
|
||||
}
|
||||
|
||||
var namespaceHeader = []string{"Namespace"}
|
||||
|
||||
type getCommand struct {
|
||||
apiType
|
||||
list summarisable
|
||||
}
|
||||
|
||||
func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var listOpts []client.ListOption
|
||||
if !allNamespaces {
|
||||
listOpts = append(listOpts, client.InNamespace(namespace))
|
||||
}
|
||||
err = kubeClient.List(ctx, get.list.asRuntimeObject(), listOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if get.list.len() == 0 {
|
||||
logger.Failuref("no %s objects found in %s namespace", get.kind, namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
header := get.list.headers(allNamespaces)
|
||||
var rows [][]string
|
||||
for i := 0; i < get.list.len(); i++ {
|
||||
row := get.list.summariseItem(i, allNamespaces)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
utils.PrintTable(os.Stdout, header, rows)
|
||||
return nil
|
||||
}
|
||||
|
||||
31
cmd/flux/get_image.go
Normal file
31
cmd/flux/get_image.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var getImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Get image automation object status",
|
||||
Long: "The get image sub-commands print the status of image automation objects.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
getCmd.AddCommand(getImageCmd)
|
||||
}
|
||||
57
cmd/flux/get_image_policy.go
Normal file
57
cmd/flux/get_image_policy.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var getImagePolicyCmd = &cobra.Command{
|
||||
Use: "policy",
|
||||
Short: "Get ImagePolicy status",
|
||||
Long: "The get image policy command prints the status of ImagePolicy objects.",
|
||||
Example: ` # List all image policies and their status
|
||||
flux get image policy
|
||||
|
||||
# List image policies from all namespaces
|
||||
flux get image policy --all-namespaces
|
||||
`,
|
||||
RunE: getCommand{
|
||||
apiType: imagePolicyType,
|
||||
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
getImageCmd.AddCommand(getImagePolicyCmd)
|
||||
}
|
||||
|
||||
func (s imagePolicyListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||
item := s.Items[i]
|
||||
status, msg := statusAndMessage(item.Status.Conditions)
|
||||
return append(nameColumns(&item, includeNamespace), status, msg, item.Status.LatestImage)
|
||||
}
|
||||
|
||||
func (s imagePolicyListAdapter) headers(includeNamespace bool) []string {
|
||||
headers := []string{"Name", "Ready", "Message", "Latest image"}
|
||||
if includeNamespace {
|
||||
return append(namespaceHeader, headers...)
|
||||
}
|
||||
return headers
|
||||
}
|
||||
66
cmd/flux/get_image_repository.go
Normal file
66
cmd/flux/get_image_repository.go
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var getImageRepositoryCmd = &cobra.Command{
|
||||
Use: "repository",
|
||||
Short: "Get ImageRepository status",
|
||||
Long: "The get image repository command prints the status of ImageRepository objects.",
|
||||
Example: ` # List all image repositories and their status
|
||||
flux get image repository
|
||||
|
||||
# List image repositories from all namespaces
|
||||
flux get image repository --all-namespaces
|
||||
`,
|
||||
RunE: getCommand{
|
||||
apiType: imageRepositoryType,
|
||||
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
getImageCmd.AddCommand(getImageRepositoryCmd)
|
||||
}
|
||||
|
||||
func (s imageRepositoryListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||
item := s.Items[i]
|
||||
status, msg := statusAndMessage(item.Status.Conditions)
|
||||
var lastScan string
|
||||
if item.Status.LastScanResult != nil {
|
||||
lastScan = item.Status.LastScanResult.ScanTime.Time.Format(time.RFC3339)
|
||||
}
|
||||
return append(nameColumns(&item, includeNamespace),
|
||||
status, msg, lastScan, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||
}
|
||||
|
||||
func (s imageRepositoryListAdapter) headers(includeNamespace bool) []string {
|
||||
headers := []string{"Name", "Ready", "Message", "Last scan", "Suspended"}
|
||||
if includeNamespace {
|
||||
return append(namespaceHeader, headers...)
|
||||
}
|
||||
return headers
|
||||
}
|
||||
65
cmd/flux/get_image_updateauto.go
Normal file
65
cmd/flux/get_image_updateauto.go
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var getImageUpdateCmd = &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Get ImageUpdateAutomation status",
|
||||
Long: "The get image update command prints the status of ImageUpdateAutomation objects.",
|
||||
Example: ` # List all image update automation object and their status
|
||||
flux get image update
|
||||
|
||||
# List image update automations from all namespaces
|
||||
flux get image update --all-namespaces
|
||||
`,
|
||||
RunE: getCommand{
|
||||
apiType: imageUpdateAutomationType,
|
||||
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
getImageCmd.AddCommand(getImageUpdateCmd)
|
||||
}
|
||||
|
||||
func (s imageUpdateAutomationListAdapter) summariseItem(i int, includeNamespace bool) []string {
|
||||
item := s.Items[i]
|
||||
status, msg := statusAndMessage(item.Status.Conditions)
|
||||
var lastRun string
|
||||
if item.Status.LastAutomationRunTime != nil {
|
||||
lastRun = item.Status.LastAutomationRunTime.Time.Format(time.RFC3339)
|
||||
}
|
||||
return append(nameColumns(&item, includeNamespace), status, msg, lastRun, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
|
||||
}
|
||||
|
||||
func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []string {
|
||||
headers := []string{"Name", "Ready", "Message", "Last run", "Suspended"}
|
||||
if includeNamespace {
|
||||
return append(namespaceHeader, headers...)
|
||||
}
|
||||
return headers
|
||||
}
|
||||
115
cmd/flux/image.go
Normal file
115
cmd/flux/image.go
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
// These are general-purpose adapters for attaching methods to, for
|
||||
// the various commands. The *List adapters implement len(), since
|
||||
// it's used in at least a couple of commands.
|
||||
|
||||
// imagev1.ImageRepository
|
||||
|
||||
var imageRepositoryType = apiType{
|
||||
kind: imagev1.ImageRepositoryKind,
|
||||
humanKind: "image repository",
|
||||
}
|
||||
|
||||
type imageRepositoryAdapter struct {
|
||||
*imagev1.ImageRepository
|
||||
}
|
||||
|
||||
func (a imageRepositoryAdapter) asRuntimeObject() runtime.Object {
|
||||
return a.ImageRepository
|
||||
}
|
||||
|
||||
// imagev1.ImageRepositoryList
|
||||
|
||||
type imageRepositoryListAdapter struct {
|
||||
*imagev1.ImageRepositoryList
|
||||
}
|
||||
|
||||
func (a imageRepositoryListAdapter) asRuntimeObject() runtime.Object {
|
||||
return a.ImageRepositoryList
|
||||
}
|
||||
|
||||
func (a imageRepositoryListAdapter) len() int {
|
||||
return len(a.ImageRepositoryList.Items)
|
||||
}
|
||||
|
||||
// imagev1.ImagePolicy
|
||||
|
||||
var imagePolicyType = apiType{
|
||||
kind: imagev1.ImagePolicyKind,
|
||||
humanKind: "image policy",
|
||||
}
|
||||
|
||||
type imagePolicyAdapter struct {
|
||||
*imagev1.ImagePolicy
|
||||
}
|
||||
|
||||
func (a imagePolicyAdapter) asRuntimeObject() runtime.Object {
|
||||
return a.ImagePolicy
|
||||
}
|
||||
|
||||
// imagev1.ImagePolicyList
|
||||
|
||||
type imagePolicyListAdapter struct {
|
||||
*imagev1.ImagePolicyList
|
||||
}
|
||||
|
||||
func (a imagePolicyListAdapter) asRuntimeObject() runtime.Object {
|
||||
return a.ImagePolicyList
|
||||
}
|
||||
|
||||
func (a imagePolicyListAdapter) len() int {
|
||||
return len(a.ImagePolicyList.Items)
|
||||
}
|
||||
|
||||
// autov1.ImageUpdateAutomation
|
||||
|
||||
var imageUpdateAutomationType = apiType{
|
||||
kind: autov1.ImageUpdateAutomationKind,
|
||||
humanKind: "image update automation",
|
||||
}
|
||||
|
||||
type imageUpdateAutomationAdapter struct {
|
||||
*autov1.ImageUpdateAutomation
|
||||
}
|
||||
|
||||
func (a imageUpdateAutomationAdapter) asRuntimeObject() runtime.Object {
|
||||
return a.ImageUpdateAutomation
|
||||
}
|
||||
|
||||
// autov1.ImageUpdateAutomationList
|
||||
|
||||
type imageUpdateAutomationListAdapter struct {
|
||||
*autov1.ImageUpdateAutomationList
|
||||
}
|
||||
|
||||
func (a imageUpdateAutomationListAdapter) asRuntimeObject() runtime.Object {
|
||||
return a.ImageUpdateAutomationList
|
||||
}
|
||||
|
||||
func (a imageUpdateAutomationListAdapter) len() int {
|
||||
return len(a.ImageUpdateAutomationList.Items)
|
||||
}
|
||||
@@ -56,13 +56,15 @@ var (
|
||||
installDryRun bool
|
||||
installManifestsPath string
|
||||
installVersion string
|
||||
installComponents []string
|
||||
installDefaultComponents []string
|
||||
installExtraComponents []string
|
||||
installRegistry string
|
||||
installImagePullSecret string
|
||||
installWatchAllNamespaces bool
|
||||
installNetworkPolicy bool
|
||||
installArch = flags.Arch(defaults.Arch)
|
||||
installLogLevel = flags.LogLevel(defaults.LogLevel)
|
||||
installClusterDomain string
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -72,8 +74,10 @@ func init() {
|
||||
"only print the object that would be applied")
|
||||
installCmd.Flags().StringVarP(&installVersion, "version", "v", defaults.Version,
|
||||
"toolkit version")
|
||||
installCmd.Flags().StringSliceVar(&installComponents, "components", defaults.Components,
|
||||
installCmd.Flags().StringSliceVar(&installDefaultComponents, "components", defaults.Components,
|
||||
"list of components, accepts comma-separated values")
|
||||
installCmd.Flags().StringSliceVar(&installExtraComponents, "components-extra", nil,
|
||||
"list of components in addition to those supplied or defaulted, accepts comma-separated values")
|
||||
installCmd.Flags().StringVar(&installManifestsPath, "manifests", "", "path to the manifest directory")
|
||||
installCmd.Flags().MarkHidden("manifests")
|
||||
installCmd.Flags().StringVar(&installRegistry, "registry", defaults.Registry,
|
||||
@@ -86,6 +90,7 @@ func init() {
|
||||
installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description())
|
||||
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", defaults.NetworkPolicy,
|
||||
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
||||
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", defaults.ClusterDomain, "internal cluster domain")
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
@@ -103,11 +108,17 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
logger.Generatef("generating manifests")
|
||||
}
|
||||
|
||||
components := append(installDefaultComponents, installExtraComponents...)
|
||||
|
||||
if err := utils.ValidateComponents(components); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := install.Options{
|
||||
BaseURL: installManifestsPath,
|
||||
Version: installVersion,
|
||||
Namespace: namespace,
|
||||
Components: installComponents,
|
||||
Components: components,
|
||||
Registry: installRegistry,
|
||||
ImagePullSecret: installImagePullSecret,
|
||||
Arch: installArch.String(),
|
||||
@@ -117,6 +128,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
NotificationController: defaults.NotificationController,
|
||||
ManifestFile: fmt.Sprintf("%s.yaml", namespace),
|
||||
Timeout: timeout,
|
||||
ClusterDomain: installClusterDomain,
|
||||
}
|
||||
|
||||
if installManifestsPath == "" {
|
||||
@@ -137,7 +149,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
} else if installExport {
|
||||
fmt.Println("---")
|
||||
fmt.Println("# GitOps Toolkit revision", installVersion)
|
||||
fmt.Println("# Components:", strings.Join(installComponents, ","))
|
||||
fmt.Println("# Components:", strings.Join(components, ","))
|
||||
fmt.Print(manifest.Content)
|
||||
fmt.Println("---")
|
||||
return nil
|
||||
@@ -167,7 +179,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("verifying installation")
|
||||
for _, deployment := range installComponents {
|
||||
for _, deployment := range components {
|
||||
kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
|
||||
if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubeconfig, kubecontext, kubectlArgs...); err != nil {
|
||||
return fmt.Errorf("install failed")
|
||||
|
||||
64
cmd/flux/object.go
Normal file
64
cmd/flux/object.go
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Most commands need one or both of the kind (e.g.,
|
||||
// `"ImageRepository"`) and a human-palatable name for the kind (e.g.,
|
||||
// `"image repository"`), to be interpolated into output. It's
|
||||
// convenient to package these up ahead of time, then the command
|
||||
// implementation can pick whichever it wants to use.
|
||||
type apiType struct {
|
||||
kind, humanKind string
|
||||
}
|
||||
|
||||
// adapter is an interface for a wrapper or alias from which we can
|
||||
// get a controller-runtime deserialisable value. This is used so that
|
||||
// you can wrap an API type to give it other useful methods, but still
|
||||
// use values of the wrapper with `client.Client`, which only deals
|
||||
// with types that have been added to the schema.
|
||||
type adapter interface {
|
||||
asRuntimeObject() runtime.Object
|
||||
}
|
||||
|
||||
// universalAdapter is an adapter for any runtime.Object. Use this if
|
||||
// there are no other methods needed.
|
||||
type universalAdapter struct {
|
||||
obj runtime.Object
|
||||
}
|
||||
|
||||
func (c universalAdapter) asRuntimeObject() runtime.Object {
|
||||
return c.obj
|
||||
}
|
||||
|
||||
// named is for adapters that have Name and Namespace fields, which
|
||||
// are sometimes handy to get hold of. ObjectMeta implements these, so
|
||||
// they shouldn't need any extra work.
|
||||
type named interface {
|
||||
GetName() string
|
||||
GetNamespace() string
|
||||
SetName(string)
|
||||
SetNamespace(string)
|
||||
}
|
||||
|
||||
func copyName(target, source named) {
|
||||
target.SetName(source.GetName())
|
||||
target.SetNamespace(source.GetNamespace())
|
||||
}
|
||||
@@ -17,7 +17,20 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var reconcileCmd = &cobra.Command{
|
||||
@@ -29,3 +42,101 @@ var reconcileCmd = &cobra.Command{
|
||||
func init() {
|
||||
rootCmd.AddCommand(reconcileCmd)
|
||||
}
|
||||
|
||||
type reconcileCommand struct {
|
||||
apiType
|
||||
object reconcilable
|
||||
}
|
||||
|
||||
type reconcilable interface {
|
||||
adapter // to be able to load from the cluster
|
||||
suspendable // to tell if it's suspended
|
||||
|
||||
// these are implemented by anything embedding metav1.ObjectMeta
|
||||
GetAnnotations() map[string]string
|
||||
SetAnnotations(map[string]string)
|
||||
|
||||
// this is usually implemented by GOTK types, since it's used for meta.SetResourceCondition
|
||||
GetStatusConditions() *[]metav1.Condition
|
||||
|
||||
lastHandledReconcileRequest() string // what was the last handled reconcile request?
|
||||
successMessage() string // what do you want to tell people when successfully reconciled?
|
||||
}
|
||||
|
||||
func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("%s name is required", reconcile.kind)
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
err = kubeClient.Get(ctx, namespacedName, reconcile.object.asRuntimeObject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reconcile.object.isSuspended() {
|
||||
return fmt.Errorf("resource is suspended")
|
||||
}
|
||||
|
||||
logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, namespace)
|
||||
if err := requestReconciliation(ctx, kubeClient, namespacedName, reconcile.object); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("%s annotated", reconcile.kind)
|
||||
|
||||
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
|
||||
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
|
||||
if err := wait.PollImmediate(pollInterval, timeout,
|
||||
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("%s reconciliation completed", reconcile.kind)
|
||||
|
||||
if apimeta.IsStatusConditionFalse(*reconcile.object.GetStatusConditions(), meta.ReadyCondition) {
|
||||
return fmt.Errorf("%s reconciliation failed", reconcile.kind)
|
||||
}
|
||||
logger.Successf(reconcile.object.successMessage())
|
||||
return nil
|
||||
}
|
||||
|
||||
func reconciliationHandled(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, obj reconcilable, lastHandledReconcileAt string) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, obj.asRuntimeObject())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return obj.lastHandledReconcileRequest() != lastHandledReconcileAt, nil
|
||||
}
|
||||
}
|
||||
|
||||
func requestReconciliation(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, obj reconcilable) error {
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
||||
if err := kubeClient.Get(ctx, namespacedName, obj.asRuntimeObject()); err != nil {
|
||||
return err
|
||||
}
|
||||
if ann := obj.GetAnnotations(); ann == nil {
|
||||
obj.SetAnnotations(map[string]string{
|
||||
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
|
||||
})
|
||||
} else {
|
||||
ann[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
|
||||
obj.SetAnnotations(ann)
|
||||
}
|
||||
return kubeClient.Update(ctx, obj.asRuntimeObject())
|
||||
})
|
||||
}
|
||||
|
||||
31
cmd/flux/reconcile_image.go
Normal file
31
cmd/flux/reconcile_image.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var reconcileImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Reconcile image automation objects",
|
||||
Long: "The reconcile sub-commands trigger a reconciliation of image automation objects.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
reconcileCmd.AddCommand(reconcileImageCmd)
|
||||
}
|
||||
50
cmd/flux/reconcile_image_repository.go
Normal file
50
cmd/flux/reconcile_image_repository.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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var reconcileImageRepositoryCmd = &cobra.Command{
|
||||
Use: "repository [name]",
|
||||
Short: "Reconcile an ImageRepository",
|
||||
Long: `The reconcile image repository command triggers a reconciliation of an ImageRepository resource and waits for it to finish.`,
|
||||
Example: ` # Trigger an scan for an existing image repository
|
||||
flux reconcile image repository alpine
|
||||
`,
|
||||
RunE: reconcileCommand{
|
||||
apiType: imageRepositoryType,
|
||||
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
reconcileImageCmd.AddCommand(reconcileImageRepositoryCmd)
|
||||
}
|
||||
|
||||
func (obj imageRepositoryAdapter) lastHandledReconcileRequest() string {
|
||||
return obj.Status.GetLastHandledReconcileRequest()
|
||||
}
|
||||
|
||||
func (obj imageRepositoryAdapter) successMessage() string {
|
||||
return fmt.Sprintf("scan fetched %d tags", obj.Status.LastScanResult.TagCount)
|
||||
}
|
||||
62
cmd/flux/reconcile_image_updateauto.go
Normal file
62
cmd/flux/reconcile_image_updateauto.go
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
|
||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||
meta "github.com/fluxcd/pkg/apis/meta"
|
||||
)
|
||||
|
||||
var reconcileImageUpdateCmd = &cobra.Command{
|
||||
Use: "update [name]",
|
||||
Short: "Reconcile an ImageUpdateAutomation",
|
||||
Long: `The reconcile image update command triggers a reconciliation of an ImageUpdateAutomation resource and waits for it to finish.`,
|
||||
Example: ` # Trigger an automation run for an existing image update automation
|
||||
flux reconcile image update latest-images
|
||||
`,
|
||||
RunE: reconcileCommand{
|
||||
apiType: imageUpdateAutomationType,
|
||||
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
reconcileImageCmd.AddCommand(reconcileImageUpdateCmd)
|
||||
}
|
||||
|
||||
func (obj imageUpdateAutomationAdapter) suspended() bool {
|
||||
return obj.ImageUpdateAutomation.Spec.Suspend
|
||||
}
|
||||
|
||||
func (obj imageUpdateAutomationAdapter) lastHandledReconcileRequest() string {
|
||||
return obj.Status.GetLastHandledReconcileRequest()
|
||||
}
|
||||
|
||||
func (obj imageUpdateAutomationAdapter) successMessage() string {
|
||||
if rc := apimeta.FindStatusCondition(obj.Status.Conditions, meta.ReadyCondition); rc != nil {
|
||||
return rc.Message
|
||||
}
|
||||
if obj.Status.LastAutomationRunTime != nil {
|
||||
return "last run " + obj.Status.LastAutomationRunTime.Time.Format(time.RFC3339)
|
||||
}
|
||||
return "automation not yet run"
|
||||
}
|
||||
@@ -17,7 +17,14 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var resumeCmd = &cobra.Command{
|
||||
@@ -29,3 +36,56 @@ var resumeCmd = &cobra.Command{
|
||||
func init() {
|
||||
rootCmd.AddCommand(resumeCmd)
|
||||
}
|
||||
|
||||
type resumable interface {
|
||||
adapter
|
||||
statusable
|
||||
setUnsuspended()
|
||||
successMessage() string
|
||||
}
|
||||
|
||||
type resumeCommand struct {
|
||||
apiType
|
||||
object resumable
|
||||
}
|
||||
|
||||
func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("%s name is required", resume.humanKind)
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
err = kubeClient.Get(ctx, namespacedName, resume.object.asRuntimeObject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, name, namespace)
|
||||
resume.object.setUnsuspended()
|
||||
if err := kubeClient.Update(ctx, resume.object.asRuntimeObject()); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("%s resumed", resume.humanKind)
|
||||
|
||||
logger.Waitingf("waiting for %s reconciliation", resume.kind)
|
||||
if err := wait.PollImmediate(pollInterval, timeout,
|
||||
isReady(ctx, kubeClient, namespacedName, resume.object)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("%s reconciliation completed", resume.kind)
|
||||
logger.Successf(resume.object.successMessage())
|
||||
return nil
|
||||
}
|
||||
|
||||
31
cmd/flux/resume_image.go
Normal file
31
cmd/flux/resume_image.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var resumeImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Resume image automation objects",
|
||||
Long: "The resume image sub-commands resume suspended image automation objects.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeCmd.AddCommand(resumeImageCmd)
|
||||
}
|
||||
48
cmd/flux/resume_image_repository.go
Normal file
48
cmd/flux/resume_image_repository.go
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var resumeImageRepositoryCmd = &cobra.Command{
|
||||
Use: "repository [name]",
|
||||
Short: "Resume a suspended ImageRepository",
|
||||
Long: `The resume command marks a previously suspended ImageRepository resource for reconciliation and waits for it to finish.`,
|
||||
Example: ` # Resume reconciliation for an existing ImageRepository
|
||||
flux resume image repository alpine
|
||||
`,
|
||||
RunE: resumeCommand{
|
||||
apiType: imageRepositoryType,
|
||||
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeImageCmd.AddCommand(resumeImageRepositoryCmd)
|
||||
}
|
||||
|
||||
func (obj imageRepositoryAdapter) getObservedGeneration() int64 {
|
||||
return obj.ImageRepository.Status.ObservedGeneration
|
||||
}
|
||||
|
||||
func (obj imageRepositoryAdapter) setUnsuspended() {
|
||||
obj.ImageRepository.Spec.Suspend = false
|
||||
}
|
||||
48
cmd/flux/resume_image_updateauto.go
Normal file
48
cmd/flux/resume_image_updateauto.go
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var resumeImageUpdateCmd = &cobra.Command{
|
||||
Use: "update [name]",
|
||||
Short: "Resume a suspended ImageUpdateAutomation",
|
||||
Long: `The resume command marks a previously suspended ImageUpdateAutomation resource for reconciliation and waits for it to finish.`,
|
||||
Example: ` # Resume reconciliation for an existing ImageUpdateAutomation
|
||||
flux resume image update latest-images
|
||||
`,
|
||||
RunE: resumeCommand{
|
||||
apiType: imageUpdateAutomationType,
|
||||
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
resumeImageCmd.AddCommand(resumeImageUpdateCmd)
|
||||
}
|
||||
|
||||
func (obj imageUpdateAutomationAdapter) setUnsuspended() {
|
||||
obj.ImageUpdateAutomation.Spec.Suspend = false
|
||||
}
|
||||
|
||||
func (obj imageUpdateAutomationAdapter) getObservedGeneration() int64 {
|
||||
return obj.ImageUpdateAutomation.Status.ObservedGeneration
|
||||
}
|
||||
65
cmd/flux/status.go
Normal file
65
cmd/flux/status.go
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
)
|
||||
|
||||
// statusable is used to see if a resource is considered ready in the usual way
|
||||
type statusable interface {
|
||||
adapter
|
||||
// this is implemented by ObjectMeta
|
||||
GetGeneration() int64
|
||||
getObservedGeneration() int64
|
||||
// this is usually implemented by GOTK API objects because it's used by pkg/apis/meta
|
||||
GetStatusConditions() *[]metav1.Condition
|
||||
}
|
||||
|
||||
func isReady(ctx context.Context, kubeClient client.Client,
|
||||
namespacedName types.NamespacedName, object statusable) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
err := kubeClient.Get(ctx, namespacedName, object.asRuntimeObject())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Confirm the state we are observing is for the current generation
|
||||
if object.GetGeneration() != object.getObservedGeneration() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if c := apimeta.FindStatusCondition(*object.GetStatusConditions(), meta.ReadyCondition); c != nil {
|
||||
switch c.Status {
|
||||
case metav1.ConditionTrue:
|
||||
return true, nil
|
||||
case metav1.ConditionFalse:
|
||||
return false, fmt.Errorf(c.Message)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,13 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
)
|
||||
|
||||
var suspendCmd = &cobra.Command{
|
||||
@@ -29,3 +35,47 @@ var suspendCmd = &cobra.Command{
|
||||
func init() {
|
||||
rootCmd.AddCommand(suspendCmd)
|
||||
}
|
||||
|
||||
type suspendable interface {
|
||||
adapter
|
||||
isSuspended() bool
|
||||
setSuspended()
|
||||
}
|
||||
|
||||
type suspendCommand struct {
|
||||
apiType
|
||||
object suspendable
|
||||
}
|
||||
|
||||
func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("%s name is required", suspend.humanKind)
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfig, kubecontext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
err = kubeClient.Get(ctx, namespacedName, suspend.object.asRuntimeObject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Actionf("suspending %s %s in %s namespace", suspend.humanKind, name, namespace)
|
||||
suspend.object.setSuspended()
|
||||
if err := kubeClient.Update(ctx, suspend.object.asRuntimeObject()); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("%s suspended", suspend.humanKind)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
31
cmd/flux/suspend_image.go
Normal file
31
cmd/flux/suspend_image.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var suspendImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Suspend image automation objects",
|
||||
Long: "The suspend image sub-commands suspend the reconciliation of an image automation object.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendCmd.AddCommand(suspendImageCmd)
|
||||
}
|
||||
48
cmd/flux/suspend_image_repository.go
Normal file
48
cmd/flux/suspend_image_repository.go
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var suspendImageRepositoryCmd = &cobra.Command{
|
||||
Use: "repository [name]",
|
||||
Short: "Suspend reconciliation of an ImageRepository",
|
||||
Long: "The suspend image repository command disables the reconciliation of a ImageRepository resource.",
|
||||
Example: ` # Suspend reconciliation for an existing ImageRepository
|
||||
flux suspend image repository alpine
|
||||
`,
|
||||
RunE: suspendCommand{
|
||||
apiType: imageRepositoryType,
|
||||
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendImageCmd.AddCommand(suspendImageRepositoryCmd)
|
||||
}
|
||||
|
||||
func (obj imageRepositoryAdapter) isSuspended() bool {
|
||||
return obj.ImageRepository.Spec.Suspend
|
||||
}
|
||||
|
||||
func (obj imageRepositoryAdapter) setSuspended() {
|
||||
obj.ImageRepository.Spec.Suspend = true
|
||||
}
|
||||
48
cmd/flux/suspend_image_updateauto.go
Normal file
48
cmd/flux/suspend_image_updateauto.go
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
var suspendImageUpdateCmd = &cobra.Command{
|
||||
Use: "update [name]",
|
||||
Short: "Suspend reconciliation of an ImageUpdateAutomation",
|
||||
Long: "The suspend image update command disables the reconciliation of a ImageUpdateAutomation resource.",
|
||||
Example: ` # Suspend reconciliation for an existing ImageUpdateAutomation
|
||||
flux suspend image update latest-images
|
||||
`,
|
||||
RunE: suspendCommand{
|
||||
apiType: imageUpdateAutomationType,
|
||||
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
|
||||
}.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
suspendImageCmd.AddCommand(suspendImageUpdateCmd)
|
||||
}
|
||||
|
||||
func (update imageUpdateAutomationAdapter) isSuspended() bool {
|
||||
return update.ImageUpdateAutomation.Spec.Suspend
|
||||
}
|
||||
|
||||
func (update imageUpdateAutomationAdapter) setSuspended() {
|
||||
update.ImageUpdateAutomation.Spec.Suspend = true
|
||||
}
|
||||
@@ -11,7 +11,9 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
|
||||
```
|
||||
--arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
|
||||
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
|
||||
--cluster-domain string internal cluster domain (default "cluster.local")
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
-h, --help help for bootstrap
|
||||
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
|
||||
--log-level logLevel log level, available options are: (debug, info, error) (default info)
|
||||
|
||||
@@ -46,16 +46,16 @@ flux bootstrap github [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for github
|
||||
--hostname string GitHub hostname (default "github.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitHub user or organization name
|
||||
--path string repository path, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitHub repository name
|
||||
--ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
--team stringArray GitHub team to be given maintainer access
|
||||
-h, --help help for github
|
||||
--hostname string GitHub hostname (default "github.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitHub user or organization name
|
||||
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitHub repository name
|
||||
--ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
--team stringArray GitHub team to be given maintainer access
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -63,7 +63,9 @@ flux bootstrap github [flags]
|
||||
```
|
||||
--arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
|
||||
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
|
||||
--cluster-domain string internal cluster domain (default "cluster.local")
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
--context string kubernetes context to use
|
||||
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
|
||||
@@ -43,15 +43,15 @@ flux bootstrap gitlab [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for gitlab
|
||||
--hostname string GitLab hostname (default "gitlab.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitLab user or group name
|
||||
--path string repository path, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitLab repository name
|
||||
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
-h, --help help for gitlab
|
||||
--hostname string GitLab hostname (default "gitlab.com")
|
||||
--interval duration sync interval (default 1m0s)
|
||||
--owner string GitLab user or group name
|
||||
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||
--personal is personal repository
|
||||
--private is private repository (default true)
|
||||
--repository string GitLab repository name
|
||||
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -59,7 +59,9 @@ flux bootstrap gitlab [flags]
|
||||
```
|
||||
--arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
|
||||
--branch string default branch (for GitHub this must match the default branch setting for the organization) (default "main")
|
||||
--cluster-domain string internal cluster domain (default "cluster.local")
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
--context string kubernetes context to use
|
||||
--image-pull-secret string Kubernetes secret name used for pulling the toolkit images from a private registry
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
|
||||
@@ -25,7 +25,7 @@ command -v flux >/dev/null && . <(flux completion zsh) && compdef _flux flux
|
||||
or write a cached file in one of the completion directories in your ${fpath}:
|
||||
|
||||
echo "${fpath// /\n}" | grep -i completion
|
||||
flux completions zsh > _flux
|
||||
flux completion zsh > _flux
|
||||
|
||||
mv _flux ~/.oh-my-zsh/completions # oh-my-zsh
|
||||
mv _flux ~/.zprezto/modules/completion/external/src/ # zprezto
|
||||
|
||||
@@ -31,6 +31,7 @@ The create sub-commands generate sources and resources.
|
||||
* [flux create alert](flux_create_alert.md) - Create or update a Alert resource
|
||||
* [flux create alert-provider](flux_create_alert-provider.md) - Create or update a Provider resource
|
||||
* [flux create helmrelease](flux_create_helmrelease.md) - Create or update a HelmRelease resource
|
||||
* [flux create image](flux_create_image.md) - Create or update resources dealing with image automation
|
||||
* [flux create kustomization](flux_create_kustomization.md) - Create or update a Kustomization resource
|
||||
* [flux create receiver](flux_create_receiver.md) - Create or update a Receiver resource
|
||||
* [flux create secret](flux_create_secret.md) - Create or update Kubernetes secrets
|
||||
|
||||
@@ -76,10 +76,10 @@ flux create helmrelease [name] [flags]
|
||||
-h, --help help for helmrelease
|
||||
--release-name string name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'
|
||||
--service-account string the name of the service account to impersonate when reconciling this HelmRelease
|
||||
--source helmChartSource source that contains the chart in the format '<kind>/<name>',where kind can be one of: (HelmRepository, GitRepository, Bucket)
|
||||
--source helmChartSource source that contains the chart in the format '<kind>/<name>', where kind must be one of: (HelmRepository, GitRepository, Bucket)
|
||||
--target-namespace string namespace to install this release, defaults to the HelmRelease namespace
|
||||
--values string local path to the values.yaml file
|
||||
--values-from helmReleaseValuesFrom Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>',where kind can be one of: (Secret, ConfigMap)
|
||||
--values-from helmReleaseValuesFrom Kubernetes object reference that contains the values.yaml data key in the format '<kind>/<name>', where kind must be one of: (Secret, ConfigMap)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
36
docs/cmd/flux_create_image.md
Normal file
36
docs/cmd/flux_create_image.md
Normal file
@@ -0,0 +1,36 @@
|
||||
## flux create image
|
||||
|
||||
Create or update resources dealing with image automation
|
||||
|
||||
### Synopsis
|
||||
|
||||
The create image sub-commands work with image automation objects; that is,
|
||||
object controlling updates to git based on e.g., new container images
|
||||
being available.
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--export export in YAML format to stdout
|
||||
--interval duration source sync interval (default 1m0s)
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux create](flux_create.md) - Create or update sources and resources
|
||||
* [flux create image policy](flux_create_image_policy.md) - Create or update an ImagePolicy object
|
||||
* [flux create image repository](flux_create_image_repository.md) - Create or update an ImageRepository object
|
||||
* [flux create image update](flux_create_image_update.md) - Create or update an ImageUpdateAutomation object
|
||||
|
||||
42
docs/cmd/flux_create_image_policy.md
Normal file
42
docs/cmd/flux_create_image_policy.md
Normal file
@@ -0,0 +1,42 @@
|
||||
## flux create image policy
|
||||
|
||||
Create or update an ImagePolicy object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The create image policy command generates an ImagePolicy resource.
|
||||
An ImagePolicy object calculates a "latest image" given an image
|
||||
repository and a policy, e.g., semver.
|
||||
|
||||
The image that sorts highest according to the policy is recorded in
|
||||
the status of the object.
|
||||
|
||||
```
|
||||
flux create image policy <name> [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for policy
|
||||
--image-ref string the name of an image repository object
|
||||
--semver string a semver range to apply to tags; e.g., '1.x'
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--export export in YAML format to stdout
|
||||
--interval duration source sync interval (default 1m0s)
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux create image](flux_create_image.md) - Create or update resources dealing with image automation
|
||||
|
||||
39
docs/cmd/flux_create_image_repository.md
Normal file
39
docs/cmd/flux_create_image_repository.md
Normal file
@@ -0,0 +1,39 @@
|
||||
## flux create image repository
|
||||
|
||||
Create or update an ImageRepository object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The create image repository command generates an ImageRepository resource.
|
||||
An ImageRepository object specifies an image repository to scan.
|
||||
|
||||
```
|
||||
flux create image repository <name> [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for repository
|
||||
--image string the image repository to scan; e.g., library/alpine
|
||||
--scan-timeout duration a timeout for scanning; this defaults to the interval if not set
|
||||
--secret-ref string the name of a docker-registry secret to use for credentials
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--export export in YAML format to stdout
|
||||
--interval duration source sync interval (default 1m0s)
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux create image](flux_create_image.md) - Create or update resources dealing with image automation
|
||||
|
||||
42
docs/cmd/flux_create_image_update.md
Normal file
42
docs/cmd/flux_create_image_update.md
Normal file
@@ -0,0 +1,42 @@
|
||||
## flux create image update
|
||||
|
||||
Create or update an ImageUpdateAutomation object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The create image update command generates an ImageUpdateAutomation resource.
|
||||
An ImageUpdateAutomation object specifies an automated update to images
|
||||
mentioned in YAMLs in a git repository.
|
||||
|
||||
```
|
||||
flux create image update <name> [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--author-email string the email to use for commit author
|
||||
--author-name string the name to use for commit author
|
||||
--branch string the branch to push commits to
|
||||
--commit-template string a template for commit messages
|
||||
--git-repo-ref string the name of a GitRepository resource with details of the upstream git repository
|
||||
-h, --help help for update
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--export export in YAML format to stdout
|
||||
--interval duration source sync interval (default 1m0s)
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux create image](flux_create_image.md) - Create or update resources dealing with image automation
|
||||
|
||||
@@ -50,10 +50,10 @@ flux create kustomization [name] [flags]
|
||||
--health-check stringArray workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'
|
||||
--health-check-timeout duration timeout of health checking operations (default 2m0s)
|
||||
-h, --help help for kustomization
|
||||
--path string path to the directory containing a kustomization.yaml file (default "./")
|
||||
--path safeRelativePath path to the directory containing a kustomization.yaml file (default ./)
|
||||
--prune enable garbage collection
|
||||
--service-account string the name of the service account to impersonate when reconciling this Kustomization
|
||||
--source kustomizationSource source that contains the Kubernetes manifests in the format '[<kind>/]<name>',where kind can be one of: (GitRepository, Bucket), if kind is not specified it defaults to GitRepository
|
||||
--source kustomizationSource source that contains the Kubernetes manifests in the format '[<kind>/]<name>', where kind must be one of: (GitRepository, Bucket), if kind is not specified it defaults to GitRepository
|
||||
--target-namespace string overrides the namespace of all Kustomization objects reconciled by this Kustomization
|
||||
--validation string validate the manifests before applying them on the cluster, can be 'client' or 'server'
|
||||
```
|
||||
|
||||
@@ -29,4 +29,5 @@ The create source sub-commands generate Kubernetes secrets specific to Flux.
|
||||
|
||||
* [flux create](flux_create.md) - Create or update sources and resources
|
||||
* [flux create secret git](flux_create_secret_git.md) - Create or update a Kubernetes secret for Git authentication
|
||||
* [flux create secret helm](flux_create_secret_helm.md) - Create or update a Kubernetes secret for Helm repository authentication
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ flux create secret git [name] [flags]
|
||||
# Create a Git SSH secret on disk and print the deploy key
|
||||
flux create secret git podinfo-auth \
|
||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||
--export > podinfo-auth.yaml
|
||||
--export > podinfo-auth.yaml
|
||||
|
||||
yq read podinfo-auth.yaml 'data."identity.pub"' | base64 --decode
|
||||
|
||||
@@ -40,7 +40,7 @@ flux create secret git [name] [flags]
|
||||
flux create secret git podinfo-auth \
|
||||
--namespace=apps \
|
||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||
--export > podinfo-auth.yaml
|
||||
--export > podinfo-auth.yaml
|
||||
|
||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||
--in-place podinfo-auth.yaml
|
||||
|
||||
65
docs/cmd/flux_create_secret_helm.md
Normal file
65
docs/cmd/flux_create_secret_helm.md
Normal file
@@ -0,0 +1,65 @@
|
||||
## flux create secret helm
|
||||
|
||||
Create or update a Kubernetes secret for Helm repository authentication
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
The create secret helm command generates a Kubernetes secret with basic authentication credentials.
|
||||
|
||||
```
|
||||
flux create secret helm [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
|
||||
|
||||
flux create secret helm repo-auth \
|
||||
--namespace=my-namespace \
|
||||
--username=my-username \
|
||||
--password=my-password \
|
||||
--export > repo-auth.yaml
|
||||
|
||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||
--in-place repo-auth.yaml
|
||||
|
||||
# Create an authentication secret using a custom TLS cert
|
||||
flux create secret helm repo-auth \
|
||||
--username=username \
|
||||
--password=password \
|
||||
--cert-file=./cert.crt \
|
||||
--key-file=./key.crt \
|
||||
--ca-file=./ca.crt
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--ca-file string TLS authentication CA file path
|
||||
--cert-file string TLS authentication cert file path
|
||||
-h, --help help for helm
|
||||
--key-file string TLS authentication key file path
|
||||
-p, --password string basic authentication password
|
||||
-u, --username string basic authentication username
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--export export in YAML format to stdout
|
||||
--interval duration source sync interval (default 1m0s)
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
--label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux create secret](flux_create_secret.md) - Create or update Kubernetes secrets
|
||||
|
||||
@@ -28,6 +28,7 @@ The delete sub-commands delete sources and resources.
|
||||
* [flux](flux.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
* [flux delete alert](flux_delete_alert.md) - Delete a Alert resource
|
||||
* [flux delete alert-provider](flux_delete_alert-provider.md) - Delete a Provider resource
|
||||
* [flux delete auto](flux_delete_auto.md) - Delete automation objects
|
||||
* [flux delete helmrelease](flux_delete_helmrelease.md) - Delete a HelmRelease resource
|
||||
* [flux delete kustomization](flux_delete_kustomization.md) - Delete a Kustomization resource
|
||||
* [flux delete receiver](flux_delete_receiver.md) - Delete a Receiver resource
|
||||
|
||||
32
docs/cmd/flux_delete_auto.md
Normal file
32
docs/cmd/flux_delete_auto.md
Normal file
@@ -0,0 +1,32 @@
|
||||
## flux delete auto
|
||||
|
||||
Delete automation objects
|
||||
|
||||
### Synopsis
|
||||
|
||||
The delete auto sub-commands delete automation objects.
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for auto
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
-s, --silent delete resource without asking for confirmation
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux delete](flux_delete.md) - Delete sources and resources
|
||||
* [flux delete auto image-policy](flux_delete_auto_image-policy.md) - Delete an ImagePolicy object
|
||||
* [flux delete auto image-repository](flux_delete_auto_image-repository.md) - Delete an ImageRepository object
|
||||
* [flux delete auto image-update](flux_delete_auto_image-update.md) - Delete an ImageUpdateAutomation object
|
||||
|
||||
41
docs/cmd/flux_delete_auto_image-policy.md
Normal file
41
docs/cmd/flux_delete_auto_image-policy.md
Normal file
@@ -0,0 +1,41 @@
|
||||
## flux delete auto image-policy
|
||||
|
||||
Delete an ImagePolicy object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The delete auto image-policy command deletes the given ImagePolicy from the cluster.
|
||||
|
||||
```
|
||||
flux delete auto image-policy [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Delete an image policy
|
||||
flux delete auto image-policy alpine3.x
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image-policy
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
-s, --silent delete resource without asking for confirmation
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux delete auto](flux_delete_auto.md) - Delete automation objects
|
||||
|
||||
41
docs/cmd/flux_delete_auto_image-repository.md
Normal file
41
docs/cmd/flux_delete_auto_image-repository.md
Normal file
@@ -0,0 +1,41 @@
|
||||
## flux delete auto image-repository
|
||||
|
||||
Delete an ImageRepository object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The delete auto image-repository command deletes the given ImageRepository from the cluster.
|
||||
|
||||
```
|
||||
flux delete auto image-repository [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Delete an image repository
|
||||
flux delete auto image-repository alpine
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image-repository
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
-s, --silent delete resource without asking for confirmation
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux delete auto](flux_delete_auto.md) - Delete automation objects
|
||||
|
||||
41
docs/cmd/flux_delete_auto_image-update.md
Normal file
41
docs/cmd/flux_delete_auto_image-update.md
Normal file
@@ -0,0 +1,41 @@
|
||||
## flux delete auto image-update
|
||||
|
||||
Delete an ImageUpdateAutomation object
|
||||
|
||||
### Synopsis
|
||||
|
||||
The delete auto image-update command deletes the given ImageUpdateAutomation from the cluster.
|
||||
|
||||
```
|
||||
flux delete auto image-update [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Delete an image update automation
|
||||
flux delete auto image-update latest-images
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image-update
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
-s, --silent delete resource without asking for confirmation
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux delete auto](flux_delete_auto.md) - Delete automation objects
|
||||
|
||||
@@ -29,6 +29,7 @@ The export sub-commands export resources in YAML format.
|
||||
* [flux export alert](flux_export_alert.md) - Export Alert resources in YAML format
|
||||
* [flux export alert-provider](flux_export_alert-provider.md) - Export Provider resources in YAML format
|
||||
* [flux export helmrelease](flux_export_helmrelease.md) - Export HelmRelease resources in YAML format
|
||||
* [flux export image](flux_export_image.md) - Export image automation objects
|
||||
* [flux export kustomization](flux_export_kustomization.md) - Export Kustomization resources in YAML format
|
||||
* [flux export receiver](flux_export_receiver.md) - Export Receiver resources in YAML format
|
||||
* [flux export source](flux_export_source.md) - Export sources
|
||||
|
||||
32
docs/cmd/flux_export_image.md
Normal file
32
docs/cmd/flux_export_image.md
Normal file
@@ -0,0 +1,32 @@
|
||||
## flux export image
|
||||
|
||||
Export image automation objects
|
||||
|
||||
### Synopsis
|
||||
|
||||
The export image sub-commands export image automation objects in YAML format.
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--all select all resources
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux export](flux_export.md) - Export resources in YAML format
|
||||
* [flux export image policy](flux_export_image_policy.md) - Export ImagePolicy resources in YAML format
|
||||
* [flux export image repository](flux_export_image_repository.md) - Export ImageRepository resources in YAML format
|
||||
* [flux export image update](flux_export_image_update.md) - Export ImageUpdateAutomation resources in YAML format
|
||||
|
||||
44
docs/cmd/flux_export_image_policy.md
Normal file
44
docs/cmd/flux_export_image_policy.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## flux export image policy
|
||||
|
||||
Export ImagePolicy resources in YAML format
|
||||
|
||||
### Synopsis
|
||||
|
||||
The export image policy command exports one or all ImagePolicy resources in YAML format.
|
||||
|
||||
```
|
||||
flux export image policy [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Export all ImagePolicy resources
|
||||
flux export image policy --all > image-policies.yaml
|
||||
|
||||
# Export a specific policy
|
||||
flux export image policy alpine1x > alpine1x.yaml
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for policy
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--all select all resources
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux export image](flux_export_image.md) - Export image automation objects
|
||||
|
||||
44
docs/cmd/flux_export_image_repository.md
Normal file
44
docs/cmd/flux_export_image_repository.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## flux export image repository
|
||||
|
||||
Export ImageRepository resources in YAML format
|
||||
|
||||
### Synopsis
|
||||
|
||||
The export image repository command exports one or all ImageRepository resources in YAML format.
|
||||
|
||||
```
|
||||
flux export image repository [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Export all ImageRepository resources
|
||||
flux export image repository --all > image-repositories.yaml
|
||||
|
||||
# Export a specific ImageRepository resource
|
||||
flux export image repository alpine > alpine.yaml
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for repository
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--all select all resources
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux export image](flux_export_image.md) - Export image automation objects
|
||||
|
||||
44
docs/cmd/flux_export_image_update.md
Normal file
44
docs/cmd/flux_export_image_update.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## flux export image update
|
||||
|
||||
Export ImageUpdateAutomation resources in YAML format
|
||||
|
||||
### Synopsis
|
||||
|
||||
The export image update command exports one or all ImageUpdateAutomation resources in YAML format.
|
||||
|
||||
```
|
||||
flux export image update [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Export all ImageUpdateAutomation resources
|
||||
flux export image update --all > updates.yaml
|
||||
|
||||
# Export a specific automation
|
||||
flux export image update latest-images > latest.yaml
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for update
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--all select all resources
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux export image](flux_export_image.md) - Export image automation objects
|
||||
|
||||
@@ -29,6 +29,7 @@ The get sub-commands print the statuses of sources and resources.
|
||||
* [flux get alert-providers](flux_get_alert-providers.md) - Get Provider statuses
|
||||
* [flux get alerts](flux_get_alerts.md) - Get Alert statuses
|
||||
* [flux get helmreleases](flux_get_helmreleases.md) - Get HelmRelease statuses
|
||||
* [flux get image](flux_get_image.md) - Get image automation object status
|
||||
* [flux get kustomizations](flux_get_kustomizations.md) - Get Kustomization statuses
|
||||
* [flux get receivers](flux_get_receivers.md) - Get Receiver statuses
|
||||
* [flux get sources](flux_get_sources.md) - Get source statuses
|
||||
|
||||
32
docs/cmd/flux_get_image.md
Normal file
32
docs/cmd/flux_get_image.md
Normal file
@@ -0,0 +1,32 @@
|
||||
## flux get image
|
||||
|
||||
Get image automation object status
|
||||
|
||||
### Synopsis
|
||||
|
||||
The get image sub-commands print the status of image automation objects.
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-A, --all-namespaces list the requested object(s) across all namespaces
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux get](flux_get.md) - Get sources and resources
|
||||
* [flux get image policy](flux_get_image_policy.md) - Get ImagePolicy status
|
||||
* [flux get image repository](flux_get_image_repository.md) - Get ImageRepository status
|
||||
* [flux get image update](flux_get_image_update.md) - Get ImageUpdateAutomation status
|
||||
|
||||
44
docs/cmd/flux_get_image_policy.md
Normal file
44
docs/cmd/flux_get_image_policy.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## flux get image policy
|
||||
|
||||
Get ImagePolicy status
|
||||
|
||||
### Synopsis
|
||||
|
||||
The get image policy command prints the status of ImagePolicy objects.
|
||||
|
||||
```
|
||||
flux get image policy [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# List all image policies and their status
|
||||
flux get image policy
|
||||
|
||||
# List image policies from all namespaces
|
||||
flux get image policy --all-namespaces
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for policy
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-A, --all-namespaces list the requested object(s) across all namespaces
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux get image](flux_get_image.md) - Get image automation object status
|
||||
|
||||
44
docs/cmd/flux_get_image_repository.md
Normal file
44
docs/cmd/flux_get_image_repository.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## flux get image repository
|
||||
|
||||
Get ImageRepository status
|
||||
|
||||
### Synopsis
|
||||
|
||||
The get image repository command prints the status of ImageRepository objects.
|
||||
|
||||
```
|
||||
flux get image repository [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# List all image repositories and their status
|
||||
flux get image repository
|
||||
|
||||
# List image repositories from all namespaces
|
||||
flux get image repository --all-namespaces
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for repository
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-A, --all-namespaces list the requested object(s) across all namespaces
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux get image](flux_get_image.md) - Get image automation object status
|
||||
|
||||
44
docs/cmd/flux_get_image_update.md
Normal file
44
docs/cmd/flux_get_image_update.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## flux get image update
|
||||
|
||||
Get ImageUpdateAutomation status
|
||||
|
||||
### Synopsis
|
||||
|
||||
The get image update command prints the status of ImageUpdateAutomation objects.
|
||||
|
||||
```
|
||||
flux get image update [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# List all image update automation object and their status
|
||||
flux get image update
|
||||
|
||||
# List image update automations from all namespaces
|
||||
flux get image update --all-namespaces
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for update
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-A, --all-namespaces list the requested object(s) across all namespaces
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux get image](flux_get_image.md) - Get image automation object status
|
||||
|
||||
@@ -32,7 +32,9 @@ flux install [flags]
|
||||
|
||||
```
|
||||
--arch arch cluster architecture, available options are: (amd64, arm, arm64) (default amd64)
|
||||
--cluster-domain string internal cluster domain (default "cluster.local")
|
||||
--components strings list of components, accepts comma-separated values (default [source-controller,kustomize-controller,helm-controller,notification-controller])
|
||||
--components-extra strings list of components in addition to those supplied or defaulted, accepts comma-separated values
|
||||
--dry-run only print the object that would be applied
|
||||
--export write the install manifests to stdout and exit
|
||||
-h, --help help for install
|
||||
|
||||
@@ -28,6 +28,7 @@ The reconcile sub-commands trigger a reconciliation of sources and resources.
|
||||
* [flux reconcile alert](flux_reconcile_alert.md) - Reconcile an Alert
|
||||
* [flux reconcile alert-provider](flux_reconcile_alert-provider.md) - Reconcile a Provider
|
||||
* [flux reconcile helmrelease](flux_reconcile_helmrelease.md) - Reconcile a HelmRelease resource
|
||||
* [flux reconcile image](flux_reconcile_image.md) - Reconcile image automation objects
|
||||
* [flux reconcile kustomization](flux_reconcile_kustomization.md) - Reconcile a Kustomization resource
|
||||
* [flux reconcile receiver](flux_reconcile_receiver.md) - Reconcile a Receiver
|
||||
* [flux reconcile source](flux_reconcile_source.md) - Reconcile sources
|
||||
|
||||
30
docs/cmd/flux_reconcile_image.md
Normal file
30
docs/cmd/flux_reconcile_image.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## flux reconcile image
|
||||
|
||||
Reconcile image automation objects
|
||||
|
||||
### Synopsis
|
||||
|
||||
The reconcile sub-commands trigger a reconciliation of image automation objects.
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux reconcile](flux_reconcile.md) - Reconcile sources and resources
|
||||
* [flux reconcile image repository](flux_reconcile_image_repository.md) - Reconcile an ImageRepository
|
||||
* [flux reconcile image update](flux_reconcile_image_update.md) - Reconcile an ImageUpdateAutomation
|
||||
|
||||
40
docs/cmd/flux_reconcile_image_repository.md
Normal file
40
docs/cmd/flux_reconcile_image_repository.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## flux reconcile image repository
|
||||
|
||||
Reconcile an ImageRepository
|
||||
|
||||
### Synopsis
|
||||
|
||||
The reconcile image repository command triggers a reconciliation of an ImageRepository resource and waits for it to finish.
|
||||
|
||||
```
|
||||
flux reconcile image repository [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Trigger an scan for an existing image repository
|
||||
flux reconcile image repository alpine
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for repository
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux reconcile image](flux_reconcile_image.md) - Reconcile image automation objects
|
||||
|
||||
40
docs/cmd/flux_reconcile_image_update.md
Normal file
40
docs/cmd/flux_reconcile_image_update.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## flux reconcile image update
|
||||
|
||||
Reconcile an ImageUpdateAutomation
|
||||
|
||||
### Synopsis
|
||||
|
||||
The reconcile image update command triggers a reconciliation of an ImageUpdateAutomation resource and waits for it to finish.
|
||||
|
||||
```
|
||||
flux reconcile image update [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Trigger an automation run for an existing image update automation
|
||||
flux reconcile image update latest-images
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for update
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux reconcile image](flux_reconcile_image.md) - Reconcile image automation objects
|
||||
|
||||
@@ -27,6 +27,7 @@ The resume sub-commands resume a suspended resource.
|
||||
* [flux](flux.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
* [flux resume alert](flux_resume_alert.md) - Resume a suspended Alert
|
||||
* [flux resume helmrelease](flux_resume_helmrelease.md) - Resume a suspended HelmRelease
|
||||
* [flux resume image](flux_resume_image.md) - Resume image automation objects
|
||||
* [flux resume kustomization](flux_resume_kustomization.md) - Resume a suspended Kustomization
|
||||
* [flux resume receiver](flux_resume_receiver.md) - Resume a suspended Receiver
|
||||
* [flux resume source](flux_resume_source.md) - Resume sources
|
||||
|
||||
30
docs/cmd/flux_resume_image.md
Normal file
30
docs/cmd/flux_resume_image.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## flux resume image
|
||||
|
||||
Resume image automation objects
|
||||
|
||||
### Synopsis
|
||||
|
||||
The resume image sub-commands resume suspended image automation objects.
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux resume](flux_resume.md) - Resume suspended resources
|
||||
* [flux resume image repository](flux_resume_image_repository.md) - Resume a suspended ImageRepository
|
||||
* [flux resume image update](flux_resume_image_update.md) - Resume a suspended ImageUpdateAutomation
|
||||
|
||||
40
docs/cmd/flux_resume_image_repository.md
Normal file
40
docs/cmd/flux_resume_image_repository.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## flux resume image repository
|
||||
|
||||
Resume a suspended ImageRepository
|
||||
|
||||
### Synopsis
|
||||
|
||||
The resume command marks a previously suspended ImageRepository resource for reconciliation and waits for it to finish.
|
||||
|
||||
```
|
||||
flux resume image repository [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Resume reconciliation for an existing ImageRepository
|
||||
flux resume image repository alpine
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for repository
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux resume image](flux_resume_image.md) - Resume image automation objects
|
||||
|
||||
40
docs/cmd/flux_resume_image_update.md
Normal file
40
docs/cmd/flux_resume_image_update.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## flux resume image update
|
||||
|
||||
Resume a suspended ImageUpdateAutomation
|
||||
|
||||
### Synopsis
|
||||
|
||||
The resume command marks a previously suspended ImageUpdateAutomation resource for reconciliation and waits for it to finish.
|
||||
|
||||
```
|
||||
flux resume image update [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Resume reconciliation for an existing ImageUpdateAutomation
|
||||
flux resume image update latest-images
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for update
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux resume image](flux_resume_image.md) - Resume image automation objects
|
||||
|
||||
@@ -27,6 +27,7 @@ The suspend sub-commands suspend the reconciliation of a resource.
|
||||
* [flux](flux.md) - Command line utility for assembling Kubernetes CD pipelines
|
||||
* [flux suspend alert](flux_suspend_alert.md) - Suspend reconciliation of Alert
|
||||
* [flux suspend helmrelease](flux_suspend_helmrelease.md) - Suspend reconciliation of HelmRelease
|
||||
* [flux suspend image](flux_suspend_image.md) - Suspend image automation objects
|
||||
* [flux suspend kustomization](flux_suspend_kustomization.md) - Suspend reconciliation of Kustomization
|
||||
* [flux suspend receiver](flux_suspend_receiver.md) - Suspend reconciliation of Receiver
|
||||
* [flux suspend source](flux_suspend_source.md) - Suspend sources
|
||||
|
||||
30
docs/cmd/flux_suspend_image.md
Normal file
30
docs/cmd/flux_suspend_image.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## flux suspend image
|
||||
|
||||
Suspend image automation objects
|
||||
|
||||
### Synopsis
|
||||
|
||||
The suspend image sub-commands suspend the reconciliation of an image automation object.
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for image
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux suspend](flux_suspend.md) - Suspend resources
|
||||
* [flux suspend image repository](flux_suspend_image_repository.md) - Suspend reconciliation of an ImageRepository
|
||||
* [flux suspend image update](flux_suspend_image_update.md) - Suspend reconciliation of an ImageUpdateAutomation
|
||||
|
||||
40
docs/cmd/flux_suspend_image_repository.md
Normal file
40
docs/cmd/flux_suspend_image_repository.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## flux suspend image repository
|
||||
|
||||
Suspend reconciliation of an ImageRepository
|
||||
|
||||
### Synopsis
|
||||
|
||||
The suspend image repository command disables the reconciliation of a ImageRepository resource.
|
||||
|
||||
```
|
||||
flux suspend image repository [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Suspend reconciliation for an existing ImageRepository
|
||||
flux suspend image repository alpine
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for repository
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux suspend image](flux_suspend_image.md) - Suspend image automation objects
|
||||
|
||||
40
docs/cmd/flux_suspend_image_update.md
Normal file
40
docs/cmd/flux_suspend_image_update.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## flux suspend image update
|
||||
|
||||
Suspend reconciliation of an ImageUpdateAutomation
|
||||
|
||||
### Synopsis
|
||||
|
||||
The suspend image update command disables the reconciliation of a ImageUpdateAutomation resource.
|
||||
|
||||
```
|
||||
flux suspend image update [name] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Suspend reconciliation for an existing ImageUpdateAutomation
|
||||
flux suspend image update latest-images
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for update
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string kubernetes context to use
|
||||
--kubeconfig string path to the kubeconfig file (default "~/.kube/config")
|
||||
-n, --namespace string the namespace scope for this operation (default "flux-system")
|
||||
--timeout duration timeout for this operation (default 5m0s)
|
||||
--verbose print generated objects
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [flux suspend image](flux_suspend_image.md) - Suspend image automation objects
|
||||
|
||||
52
docs/core-concepts/index.md
Normal file
52
docs/core-concepts/index.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Core Concepts
|
||||
|
||||
!!! note "Work in progress"
|
||||
This document is a work in progress.
|
||||
|
||||
These are some core concepts in Flux.
|
||||
|
||||
## GitOps
|
||||
|
||||
GitOps is a way of managing your infrastructure and applications so that whole system is described declaratively and version controlled (most likely in a Git repository), and having an automated process that ensures that the deployed environment matches the state specified in a repository.
|
||||
|
||||
For more information, take a look at ["What is GitOps?"](https://www.gitops.tech/#what-is-gitops).
|
||||
|
||||
## Sources
|
||||
|
||||
A *Source* defines the origin of a source and the requirements to obtain
|
||||
it (e.g. credentials, version selectors). For example, the latest `1.x` tag
|
||||
available from a Git repository over SSH.
|
||||
|
||||
Sources produce an artifact that is consumed by other Flux elements to perform
|
||||
actions, like applying the contents of the artifact on the cluster. A source
|
||||
may be shared by multiple consumers to deduplicate configuration and/or storage.
|
||||
|
||||
The origin of the source is checked for changes on a defined interval, if
|
||||
there is a newer version available that matches the criteria, a new artifact
|
||||
is produced.
|
||||
|
||||
All sources are specified as Custom Resources in a Kubernetes cluster, examples
|
||||
of sources are `GitRepository`, `HelmRepository` and `Bucket` resources.
|
||||
|
||||
For more information, take a look at [the source controller documentation](../components/source/source.md).
|
||||
|
||||
## Reconciliation
|
||||
|
||||
Reconciliation refers to ensuring that a given state (e.g application running in the cluster, infrastructure) matches a desired state declaratively defined somewhere (e.g a git repository). There are various examples of these in flux e.g:
|
||||
|
||||
- HelmRelease reconciliation: ensures the state of the Helm release matches what is defined in the resource, performs a release if this is not the case (including revision changes of a HelmChart resource).
|
||||
- Bucket reconciliation: downloads and archives the contents of the declared bucket on a given interval and stores this as an artifact, records the observed revision of the artifact and the artifact itself in the status of resource.
|
||||
- [Kustomization](#kustomization) reconciliation: ensures the state of the application deployed on a cluster matches resources contained in a git repository.
|
||||
|
||||
## Kustomization
|
||||
|
||||
The kustomization represents a local set of Kubernetes resources that Flux is supposed to reconcile in the cluster. The reconciliation runs every one minute by default but this can be specified in the kustomization. If you make any changes to the cluster using `kubectl edit` or `kubectl patch`, it will be promptly reverted. You either suspend the reconciliation or push your changes to a Git repository.
|
||||
|
||||
For more information, take a look at [this documentation](../components/kustomize/kustomization.md).
|
||||
|
||||
## Bootstrap
|
||||
|
||||
The process of installing the Flux components in a complete GitOps way is called a bootstrap. The manifests are applied to the cluster, a `GitRepository` and `Kustomization` are created for the Flux components, and the manifests are pushed to an existing Git repository (or a new one is created). Flux can manage itself just as it manages other resources.
|
||||
The bootstrap is done using the `flux` CLI `flux bootstrap`.
|
||||
|
||||
For more information, take a look at [the documentation for the bootstrap command](../cmd/flux_bootstrap.md).
|
||||
@@ -17,7 +17,7 @@ On your dev machine install the following tools:
|
||||
* kustomize >= 3.5
|
||||
* docker >= 19.03
|
||||
|
||||
## Install the GitOps Toolkit
|
||||
## Install Flux
|
||||
|
||||
Create a cluster for testing:
|
||||
|
||||
@@ -25,7 +25,7 @@ Create a cluster for testing:
|
||||
kind create cluster --name dev
|
||||
```
|
||||
|
||||
Install the toolkit CLI:
|
||||
Install the Flux CLI:
|
||||
|
||||
```sh
|
||||
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
|
||||
@@ -37,7 +37,7 @@ Verify that your dev machine satisfies the prerequisites with:
|
||||
flux check --pre
|
||||
```
|
||||
|
||||
Install the toolkit controllers on the dev cluster:
|
||||
Install source-controller on the dev cluster:
|
||||
|
||||
```sh
|
||||
flux install
|
||||
@@ -45,13 +45,13 @@ flux install
|
||||
|
||||
## Clone the sample controller
|
||||
|
||||
You'll be using [stefanprodan/source-watcher](https://github.com/stefanprodan/source-watcher) as
|
||||
You'll be using [fluxcd/source-watcher](https://github.com/fluxcd/source-watcher) as
|
||||
a template for developing your own controller. The source-watcher was scaffolded with `kubebuilder init`.
|
||||
|
||||
Clone the source-watcher repo:
|
||||
Clone the source-watcher repository:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/stefanprodan/source-watcher
|
||||
git clone https://github.com/fluxcd/source-watcher
|
||||
cd source-watcher
|
||||
```
|
||||
|
||||
@@ -115,7 +115,7 @@ The source-controller reports the revision under `GitRepository.Status.Artifact.
|
||||
|
||||
## How it works
|
||||
|
||||
The [GitRepositoryWatcher](https://github.com/stefanprodan/source-watcher/blob/master/controllers/gitrepository_watcher.go)
|
||||
The [GitRepositoryWatcher](https://github.com/fluxcd/source-watcher/blob/main/controllers/gitrepository_watcher.go)
|
||||
controller does the following:
|
||||
|
||||
* subscribes to `GitRepository` events
|
||||
@@ -186,8 +186,8 @@ func (r *GitRepositoryWatcher) SetupWithManager(mgr ctrl.Manager) error {
|
||||
|
||||
To add the watcher to an existing project, copy the controller and the revision change predicate to your `controllers` dir:
|
||||
|
||||
* [gitrepository_watcher.go](https://github.com/stefanprodan/source-watcher/blob/master/controllers/gitrepository_watcher.go)
|
||||
* [gitrepository_predicate.go](https://github.com/stefanprodan/source-watcher/blob/master/controllers/gitrepository_predicate.go)
|
||||
* [gitrepository_watcher.go](https://github.com/fluxcd/source-watcher/blob/main/controllers/gitrepository_watcher.go)
|
||||
* [gitrepository_predicate.go](https://github.com/fluxcd/source-watcher/blob/main/controllers/gitrepository_predicate.go)
|
||||
|
||||
In your `main.go` init function, register the Source API schema:
|
||||
|
||||
@@ -224,9 +224,9 @@ Your `go.mod` should require controller-runtime v0.6 or newer:
|
||||
|
||||
```go
|
||||
require (
|
||||
k8s.io/apimachinery v0.18.4
|
||||
k8s.io/client-go v0.18.4
|
||||
sigs.k8s.io/controller-runtime v0.6.0
|
||||
k8s.io/apimachinery v0.19.4
|
||||
k8s.io/client-go v0.19.4
|
||||
sigs.k8s.io/controller-runtime v0.6.4
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
# Get started with Flux v2
|
||||
|
||||
!!! note "Basic knowledge"
|
||||
This guide assumes you have some understanding of the core concepts and have read the introduction to Flux.
|
||||
The core concepts used in this guide are [GitOps](../core-concepts/index.md#gitops), [Sources](../core-concepts/index.md#sources), [Kustomization](../core-concepts/index.md#kustomization).
|
||||
|
||||
In this tutorial, you will deploy an application to a kubernetes cluster with Flux and manage the cluster in a complete GitOps manner. You'll be using a dedicated Git repository e.g. `fleet-infra` to manage the Kubernetes clusters. All the manifest will be pushed to this repository and then applied by Flux.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need two Kubernetes clusters version 1.16 or newer and kubectl version 1.18.
|
||||
In order to follow the guide, you will need one 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 though.
|
||||
|
||||
In order to follow the guide you'll need a GitHub account and a
|
||||
Flux is installed in a complete GitOps way and its manifest will be pushed to the repository, so you will also 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`).
|
||||
that can create repositories (check all permissions under `repo`) to enable Flux do this.
|
||||
|
||||
Export your GitHub personal access token and username:
|
||||
|
||||
@@ -52,24 +58,13 @@ profile:
|
||||
|
||||
`zsh`, `fish`, and `powershell` are also supported with their own sub-commands.
|
||||
|
||||
## GitOps workflow
|
||||
## Install Flux components
|
||||
|
||||
You'll be using a dedicated Git repository e.g. `fleet-infra` to manage one or more Kubernetes clusters.
|
||||
This guide assumes that you have two clusters, one for staging and one for production.
|
||||
|
||||
Using the Flux CLI you'll do the following:
|
||||
|
||||
- configure each cluster to synchronise with a directory inside the fleet repository
|
||||
- register app sources (git repositories) that contain plain Kubernetes manifests or Kustomize overlays
|
||||
- configure app deployments on both clusters (pre-releases on staging, semver releases on production)
|
||||
|
||||
## Staging bootstrap
|
||||
|
||||
Create the staging cluster using Kubernetes kind or set the kubectl context to an existing cluster:
|
||||
Create the cluster using Kubernetes kind or set the kubectl context to an existing cluster:
|
||||
|
||||
```sh
|
||||
kind create cluster --name staging
|
||||
kubectl cluster-info --context kind-staging
|
||||
kind create cluster
|
||||
kubectl cluster-info --context kind-kind
|
||||
```
|
||||
|
||||
Verify that your staging cluster satisfies the prerequisites with:
|
||||
@@ -89,7 +84,7 @@ flux bootstrap github \
|
||||
--owner=$GITHUB_USER \
|
||||
--repository=fleet-infra \
|
||||
--branch=main \
|
||||
--path=staging-cluster \
|
||||
--path=./clusters/my-cluster \
|
||||
--personal
|
||||
```
|
||||
|
||||
@@ -98,8 +93,8 @@ flux bootstrap github \
|
||||
you can use `--arch=arm` for ARMv7 32-bit container images
|
||||
and `--arch=arm64` for ARMv8 64-bit container images.
|
||||
|
||||
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.
|
||||
The bootstrap command creates a repository if one doesn't exist,
|
||||
commits the manifests for the Flux components to the default branch at the specified path, and installs the Flux components.
|
||||
Then it configures the target cluster to synchronize with the specified path inside the repository.
|
||||
|
||||
If you wish to create the repository under a GitHub organization:
|
||||
@@ -111,7 +106,7 @@ flux bootstrap github \
|
||||
--branch=<organization default branch> \
|
||||
--team=<team1-slug> \
|
||||
--team=<team2-slug> \
|
||||
--path=staging-cluster
|
||||
--path=./clusters/my-cluster
|
||||
```
|
||||
|
||||
Example output:
|
||||
@@ -148,102 +143,104 @@ If you prefer GitLab, export `GITLAB_TOKEN` env var and use the command [flux bo
|
||||
You can target a specific Flux [version](https://github.com/fluxcd/flux2/releases)
|
||||
with `flux bootstrap --version=<semver>`.
|
||||
|
||||
## Staging workflow
|
||||
## Clone the git repository
|
||||
|
||||
Clone the repository with:
|
||||
We are going to be managing the application in a GitOps manner with the git repository. The Flux manifests generated by the CLI will be pushed to the git repository. Instead of applying the manifests directly to the cluster, Flux will apply it for us instead :).
|
||||
|
||||
Therefore, we need to clone the repository to our local machine.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/$GITHUB_USER/fleet-infra
|
||||
cd fleet-infra
|
||||
```
|
||||
|
||||
Create a git source pointing to a public repository master branch:
|
||||
|
||||
## Add podinfo repository to Flux
|
||||
|
||||
We will be using a public repository [github.com/stefanprodan/podinfo](https://github.com/stefanprodan/podinfo), podinfo is a tiny web application made with Go.
|
||||
|
||||
Create a GitRepository manifest pointing to the repository's master branch with Flux CLI.
|
||||
|
||||
```sh
|
||||
flux create source git webapp \
|
||||
flux create source git podinfo \
|
||||
--url=https://github.com/stefanprodan/podinfo \
|
||||
--branch=master \
|
||||
--interval=30s \
|
||||
--export > ./staging-cluster/webapp-source.yaml
|
||||
--export > ./clusters/my-cluster/podinfo-source.yaml
|
||||
```
|
||||
|
||||
Create a kustomization for synchronizing the common manifests on the cluster:
|
||||
Commit and push it to the `fleet-infra` repository, then Flux applies it to the cluster.
|
||||
|
||||
```sh
|
||||
flux create kustomization webapp-common \
|
||||
--source=webapp \
|
||||
--path="./deploy/webapp/common" \
|
||||
git add -A && git commit -m "Adds podinfo git source"
|
||||
git push
|
||||
```
|
||||
|
||||
## Deploy podinfo application
|
||||
|
||||
We will create a kustomization manifest for podinfo. This will apply the manifest in the `kustomize` directory in the podinfo repository.
|
||||
|
||||
```sh
|
||||
flux create kustomization podinfo \
|
||||
--source=podinfo \
|
||||
--path="./kustomize" \
|
||||
--prune=true \
|
||||
--validation=client \
|
||||
--interval=1h \
|
||||
--export > ./staging-cluster/webapp-common.yaml
|
||||
--interval=5m \
|
||||
--export > ./clusters/my-cluster/podinfo-kustomization.yaml
|
||||
```
|
||||
|
||||
Create a kustomization for the backend service that depends on common:
|
||||
Commit and push the kustomization manifest to the git repository so that Flux applies it to the cluster.
|
||||
|
||||
```sh
|
||||
flux create kustomization webapp-backend \
|
||||
--depends-on=webapp-common \
|
||||
--source=webapp \
|
||||
--path="./deploy/webapp/backend" \
|
||||
--prune=true \
|
||||
--validation=client \
|
||||
--interval=10m \
|
||||
--health-check="Deployment/backend.webapp" \
|
||||
--health-check-timeout=2m \
|
||||
--export > ./staging-cluster/webapp-backend.yaml
|
||||
git add -A && git commit -m "Adds podinfo kustomization"
|
||||
git push
|
||||
```
|
||||
|
||||
Create a kustomization for the frontend service that depends on backend:
|
||||
|
||||
```sh
|
||||
flux create kustomization webapp-frontend \
|
||||
--depends-on=webapp-backend \
|
||||
--source=webapp \
|
||||
--path="./deploy/webapp/frontend" \
|
||||
--prune=true \
|
||||
--validation=client \
|
||||
--interval=10m \
|
||||
--health-check="Deployment/frontend.webapp" \
|
||||
--health-check-timeout=2m \
|
||||
--export > ./staging-cluster/webapp-frontend.yaml
|
||||
The structure of your repository should look like this:
|
||||
```
|
||||
fleet-infra
|
||||
└── clusters/
|
||||
└── my-cluster/
|
||||
├── flux-system/
|
||||
│ ├── gotk-components.yaml/
|
||||
│ ├── gotk-sync.yaml/
|
||||
│ └── kustomization.yaml/
|
||||
├── podinfo-kustomization.yaml
|
||||
└── podinfo-source.yaml
|
||||
```
|
||||
|
||||
Push changes to origin:
|
||||
|
||||
```sh
|
||||
git add -A && git commit -m "add staging webapp" && git push
|
||||
```
|
||||
## Watch Flux sync the application
|
||||
|
||||
In about 30s the synchronization should start:
|
||||
|
||||
```console
|
||||
$ watch flux get kustomizations
|
||||
NAME READY MESSAGE
|
||||
flux-system True Applied revision: main/6eea299fe9997c8561b826b67950afaf9a476cf8
|
||||
webapp-backend False dependency 'flux-system/webapp-common' is not ready
|
||||
webapp-common True Applied revision: master/7411da595c25183daba255068814b83843fe3395
|
||||
webapp-frontend False dependency 'flux-system/webapp-backend' is not ready
|
||||
flux-system main/fc07af652d3168be329539b30a4c3943a7d12dd8 False True Applied revision: main/fc07af652d3168be329539b30a4c3943a7d12dd8
|
||||
podinfo master/855f7724be13f6146f61a893851522837ad5b634 False True Applied revision: master/855f7724be13f6146f61a893851522837ad5b634
|
||||
```
|
||||
|
||||
When the synchronization finishes you can check that the webapp services are running:
|
||||
When the synchronization finishes you can check that the podinfo has been deployed on your cluster:
|
||||
|
||||
```console
|
||||
$ kubectl -n webapp get deployments,services
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/backend 1/1 1 1 4m1s
|
||||
deployment.apps/frontend 1/1 1 1 3m31s
|
||||
$ kubectl -n default get deployments,services
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/podinfo 2/2 2 2 108s
|
||||
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/backend ClusterIP 10.52.10.22 <none> 9898/TCP,9999/TCP 4m1s
|
||||
service/frontend ClusterIP 10.52.9.85 <none> 80/TCP 3m31s
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/podinfo ClusterIP 10.100.149.126 <none> 9898/TCP,9999/TCP 108s
|
||||
```
|
||||
|
||||
!!! tip
|
||||
From this moment forward, any changes made to the webapp
|
||||
Kubernetes manifests in the master branch will be synchronised with the staging cluster.
|
||||
Kubernetes manifests in the master branch will be synchronised with your cluster.
|
||||
|
||||
If a Kubernetes manifest is removed from the webapp repository, the reconciler will remove it from your cluster.
|
||||
If you delete a kustomization from the cluster, the reconciler will remove all Kubernetes objects that
|
||||
were previously applied from that kustomization.
|
||||
|
||||
If you delete a kustomization from the `fleet-infra` repo, the reconciler will remove all Kubernetes objects that
|
||||
were previously applied from that kustomization.
|
||||
|
||||
@@ -252,121 +249,10 @@ the state described in git. When dealing with an incident, you can pause the rec
|
||||
kustomization with `flux suspend kustomization <name>`. Once the debugging session
|
||||
is over, you can re-enable the reconciliation with `flux resume kustomization <name>`.
|
||||
|
||||
## Production bootstrap
|
||||
## Multi-cluster Setup
|
||||
|
||||
On production clusters, you may wish to deploy stable releases of an application.
|
||||
When creating a git source instead of a branch, you can specify a git tag or a semver expression.
|
||||
To use Flux to manage more than one cluster or promote deployments from staging to production, take a look at the
|
||||
two approaches in the repositories listed below.
|
||||
|
||||
Create the production cluster using Kubernetes kind or set the kubectl context to an existing cluster:
|
||||
|
||||
```sh
|
||||
kind create cluster --name production
|
||||
kubectl cluster-info --context kind-production
|
||||
```
|
||||
|
||||
Run the bootstrap for the production environment:
|
||||
|
||||
```sh
|
||||
flux bootstrap github \
|
||||
--owner=$GITHUB_USER \
|
||||
--repository=fleet-infra \
|
||||
--path=prod-cluster \
|
||||
--personal
|
||||
```
|
||||
Pull the changes locally:
|
||||
|
||||
```sh
|
||||
git pull
|
||||
```
|
||||
|
||||
## Production workflow
|
||||
|
||||
Create a git source using a semver range to target stable releases:
|
||||
|
||||
```sh
|
||||
flux create source git webapp \
|
||||
--url=https://github.com/stefanprodan/podinfo \
|
||||
--tag-semver=">=4.0.0 <4.0.2" \
|
||||
--interval=30s \
|
||||
--export > ./prod-cluster/webapp-source.yaml
|
||||
```
|
||||
|
||||
Create a kustomization for webapp pointing to the production overlay:
|
||||
|
||||
```sh
|
||||
flux create kustomization webapp \
|
||||
--source=webapp \
|
||||
--path="./deploy/overlays/production" \
|
||||
--prune=true \
|
||||
--validation=client \
|
||||
--interval=10m \
|
||||
--health-check="Deployment/frontend.production" \
|
||||
--health-check="Deployment/backend.production" \
|
||||
--health-check-timeout=2m \
|
||||
--export > ./prod-cluster/webapp-production.yaml
|
||||
```
|
||||
|
||||
Push changes to origin:
|
||||
|
||||
```sh
|
||||
git add -A && git commit -m "add prod webapp" && git push
|
||||
```
|
||||
|
||||
List git sources:
|
||||
|
||||
```console
|
||||
$ flux get sources git
|
||||
NAME REVISION READY MESSAGE
|
||||
flux-system main/5ae055e24b2c8a78f981708b61507a97a30bd7a6 True Fetched revision: main/113360052b3153e439a0cf8de76b8e3d2a7bdf27
|
||||
webapp 4.0.1/113360052b3153e439a0cf8de76b8e3d2a7bdf27 True Fetched revision: 4.0.1/113360052b3153e439a0cf8de76b8e3d2a7bdf27
|
||||
```
|
||||
|
||||
The kubectl equivalent is `kubectl -n flux-system get gitrepositories`.
|
||||
|
||||
List kustomization:
|
||||
|
||||
```console
|
||||
$ flux get kustomizations
|
||||
NAME REVISION SUSPENDED READY MESSAGE
|
||||
flux-system main/5ae055e24b2c8a78f981708b61507a97a30bd7a6 False True Applied revision: main/5ae055e24b2c8a78f981708b61507a97a30bd7a6
|
||||
webapp 4.0.1/113360052b3153e439a0cf8de76b8e3d2a7bdf27 False True Applied revision: 4.0.1/113360052b3153e439a0cf8de76b8e3d2a7bdf27
|
||||
```
|
||||
|
||||
The kubectl equivalent is `kubectl -n flux-system get kustomizations`.
|
||||
|
||||
If you want to upgrade to the latest 4.x version, you can change the semver expression to:
|
||||
|
||||
```sh
|
||||
flux create source git webapp \
|
||||
--url=https://github.com/stefanprodan/podinfo \
|
||||
--tag-semver=">=4.0.0 <5.0.0" \
|
||||
--interval=30s \
|
||||
--export > ./prod-cluster/webapp-source.yaml
|
||||
|
||||
git add -A && git commit -m "update prod webapp" && git push
|
||||
```
|
||||
|
||||
Trigger a git sync:
|
||||
|
||||
```console
|
||||
$ flux reconcile ks flux-system --with-source
|
||||
► annotating source flux-system
|
||||
✔ source annotated
|
||||
◎ waiting for reconcilitation
|
||||
✔ git reconciliation completed
|
||||
✔ fetched revision main/d751ea264d48bf0db8b588d1d08184834ac8fec9
|
||||
◎ waiting for kustomization reconcilitation
|
||||
✔ kustomization reconcilitation completed
|
||||
✔ applied revision main/d751ea264d48bf0db8b588d1d08184834ac8fec9
|
||||
```
|
||||
|
||||
The kubectl equivalent is `kubectl -n flux-system annotate gitrepository/flux-system fluxcd.io/reconcileAt="$(date +%s)"`.
|
||||
|
||||
Wait for the webapp to be upgraded:
|
||||
|
||||
```console
|
||||
$ watch flux get kustomizations
|
||||
NAME REVISION SUSPENDED READY MESSAGE
|
||||
flux-system main/d751ea264d48bf0db8b588d1d08184834ac8fec9 False True Applied revision: main/d751ea264d48bf0db8b588d1d08184834ac8fec9
|
||||
webapp 4.0.6/26a630c0b4b3452833d96c511d93f6f2d2e90a99 False True Applied revision: 4.0.6/26a630c0b4b3452833d96c511d93f6f2d2e90a99
|
||||
```
|
||||
1. [https://github.com/fluxcd/flux2-kustomize-helm-example](https://github.com/fluxcd/flux2-kustomize-helm-example)
|
||||
2. [https://github.com/fluxcd/flux2-multi-tenancy](https://github.com/fluxcd/flux2-multi-tenancy)
|
||||
@@ -117,52 +117,79 @@ repository and omits all other files.
|
||||
HTTP/S basic and SSH authentication can be configured for private
|
||||
Git repositories. See the [`GitRepository` CRD docs](../components/source/gitrepositories.md)
|
||||
for more details.
|
||||
|
||||
### Bucket
|
||||
|
||||
Charts from S3 compatible storage buckets can be released by declaring
|
||||
a `Bucket`, the source-controller will fetch the contents of the bucket
|
||||
on an interval and expose it as an artifact.
|
||||
### Cloud Storage
|
||||
|
||||
**There is one caveat you should be aware of:** to make the
|
||||
source-controller produce a new chart artifact, the `version` in the
|
||||
`Chart.yaml` of the chart must be bumped.
|
||||
It is inadvisable while still possible to use a `Bucket` as a source for a `HelmRelease`,
|
||||
as the whole storage bucket will be downloaded by source controller at each sync. The
|
||||
bucket can easily become very large if there are frequent releases of multiple charts
|
||||
that are stored in the same bucket.
|
||||
|
||||
An example `Bucket`:
|
||||
A better option is to use [Chartmuseum](https://github.com/helm/chartmuseum) and run a cluster
|
||||
local Helm repository that can be used by source controller. Chartmuseum has support
|
||||
for multiple different cloud storage solutions such as S3, GCS, and Azure Blob Storage,
|
||||
meaning that you are not limited to only using storage providers that support the S3 protocol.
|
||||
|
||||
You can deploy a Chartmuseum instance with a `HelmRelease` that exposes a Helm repository stored
|
||||
in a S3 bucket. Please refer to [Chartmuseums how to run documentation](https://chartmuseum.com/docs/#how-to-run)
|
||||
for details about how to use other storage backends.
|
||||
|
||||
```yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: Bucket
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: gotk-system
|
||||
name: chartmuseum
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
provider: generic
|
||||
bucketName: podinfo
|
||||
endpoint: minio.minio.svc.cluster.local:9000
|
||||
ignore: |
|
||||
# exclude all
|
||||
/*
|
||||
# include charts directory
|
||||
!/charts/
|
||||
url: https://chartmuseum.github.io/charts
|
||||
interval: 10m
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: chartmuseum
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 5m
|
||||
chart:
|
||||
spec:
|
||||
chart: chartmuseum
|
||||
version: "2.14.2"
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: chartmuseum
|
||||
namespace: flux-system
|
||||
interval: 1m
|
||||
values:
|
||||
env:
|
||||
open:
|
||||
AWS_SDK_LOAD_CONFIG: true
|
||||
STORAGE: amazon
|
||||
STORAGE_AMAZON_BUCKET: "bucket-name"
|
||||
STORAGE_AMAZON_PREFIX: ""
|
||||
STORAGE_AMAZON_REGION: "region-name"
|
||||
serviceAccount:
|
||||
create: true
|
||||
annotations:
|
||||
eks.amazonaws.com/role-arn: "role-arn"
|
||||
securityContext:
|
||||
enabled: true
|
||||
fsGroup: 65534
|
||||
```
|
||||
|
||||
The `interval` defines at which interval the Git repository contents
|
||||
are fetched, and should be at least `1m`. Setting this to a higher
|
||||
value means newer chart versions will be detected at a slower pace,
|
||||
a push-based fetch can be introduced using [webhook receivers](webhook-receivers.md)
|
||||
After Chartmuseum is up and running it should be possible to use the accompanying
|
||||
service as the url for the `HelmRepository`.
|
||||
|
||||
The `provider`, `bucketName` and `endpoint` together define what
|
||||
S3 compatible storage should be connected to. For more information,
|
||||
see the [`Bucket` CRD docs](../components/source/buckets.md).
|
||||
|
||||
|
||||
The `ignore` defines file and folder exclusion for the
|
||||
artifact produced, and follows the [`.gitignore` pattern
|
||||
format](https://git-scm.com/docs/gitignore#_pattern_format).
|
||||
The above example only includes the `charts` directory of the
|
||||
repository and omits all other files.
|
||||
```yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: helm-charts
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
url: http://chartmuseum-chartmuseum:8080
|
||||
```
|
||||
|
||||
## Define a Helm release
|
||||
|
||||
@@ -257,6 +284,11 @@ The definition of the listed keys is as follows:
|
||||
You can read more about the available formats and limitations in
|
||||
the [Helm documentation](https://helm.sh/docs/intro/using_helm/#the-format-and-limitations-of---set).
|
||||
|
||||
!!! warning "`TargetPath` and JSON values"
|
||||
When using `TargetPath` in combination with a JSON string, the
|
||||
[limitations are the same as while using `helm`](https://github.com/helm/helm/issues/5618),
|
||||
and require you to escape the full JSON string (including `=`, `[`, `,`, `.`).
|
||||
|
||||
## Refer to values in `ConfigMaps` generated with Kustomize
|
||||
|
||||
It is possible to use Kustomize [ConfigMap generator](https://kubectl.docs.kubernetes.io/references/kustomize/configmapgenerator/)
|
||||
|
||||
502
docs/guides/image-update.md
Normal file
502
docs/guides/image-update.md
Normal file
@@ -0,0 +1,502 @@
|
||||
# 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"}
|
||||
```
|
||||
|
||||
## ImageRepository cloud providers authentication
|
||||
|
||||
If relying on a cloud provider image repository, you might need to do some extra
|
||||
work in order to configure the ImageRepository resource credentials. Here are
|
||||
some common examples for the most popular cloud provider docker registries.
|
||||
|
||||
!!! warning "Workarounds"
|
||||
The examples below are intended as workaround solutions until native
|
||||
authentication mechanisms are implemented in Flux itself to support this in
|
||||
a more straightforward manner.
|
||||
|
||||
### AWS Elastic Container Registry
|
||||
|
||||
The registry authentication credentials for ECR expire every 12 hours.
|
||||
Considering this limitation, one needs to ensure the credentials are being
|
||||
refreshed before expiration so that the controller can rely on them for
|
||||
authentication.
|
||||
|
||||
The solution proposed is to create a cronjob that runs every 6 hours which would
|
||||
re-create the `docker-registry` secret using a new token.
|
||||
|
||||
Edit and save the following snippet to a file
|
||||
`./clusters/my-cluster/ecr-sync.yaml`, commit and push it to git.
|
||||
|
||||
```yaml
|
||||
kind: Role
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: ecr-credentials-sync
|
||||
namespace: flux-system
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- delete
|
||||
- create
|
||||
---
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: ecr-credentials-sync
|
||||
namespace: flux-system
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: ecr-credentials-sync
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: ecr-credentials-sync
|
||||
apiGroup: ""
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: ecr-credentials-sync
|
||||
# Uncomment and edit if using IRSA
|
||||
# annotations:
|
||||
# eks.amazonaws.com/role-arn: <role arn>
|
||||
---
|
||||
apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: ecr-credentials-sync
|
||||
namespace: flux-system
|
||||
spec:
|
||||
suspend: false
|
||||
schedule: 0 */6 * * *
|
||||
failedJobsHistoryLimit: 1
|
||||
successfulJobsHistoryLimit: 1
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: ecr-credentials-sync
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- name: token
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
initContainers:
|
||||
- image: amazon/aws-cli
|
||||
name: get-token
|
||||
imagePullPolicy: IfNotPresent
|
||||
# You will need to set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables if not using
|
||||
# IRSA. It is recommended to store the values in a Secret and load them in the container using envFrom.
|
||||
# envFrom:
|
||||
# - secretRef:
|
||||
# name: aws-credentials
|
||||
env:
|
||||
- name: REGION
|
||||
value: us-east-1 # change this if ECR repo is in a different region
|
||||
volumeMounts:
|
||||
- mountPath: /token
|
||||
name: token
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ce
|
||||
- aws ecr get-login-password --region ${REGION} > /token/ecr-token
|
||||
containers:
|
||||
- image: bitnami/kubectl
|
||||
name: create-secret
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: SECRET_NAME
|
||||
value: <secret name> # this is the generated Secret name
|
||||
- name:
|
||||
value: <account id>.dkr.ecr.<region>.amazonaws.com # fill in the account id and region
|
||||
volumeMounts:
|
||||
- mountPath: /token
|
||||
name: token
|
||||
command:
|
||||
- /bin/bash
|
||||
- -ce
|
||||
- |-
|
||||
kubectl delete secret --ignore-not-found $SECRET_NAME
|
||||
kubectl create secret docker-registry $SECRET_NAME \
|
||||
--docker-server="$ECR_REGISTRY" \
|
||||
--docker-username=AWS \
|
||||
--docker-password="$(</token/ecr-token)"
|
||||
```
|
||||
|
||||
!!! hint "Using IAM Roles for Service Accounts (IRSA)"
|
||||
If using IRSA, make sure the role attached to the service account has
|
||||
readonly access to ECR. The AWS managed policy
|
||||
`arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly` can be attached
|
||||
to the role.
|
||||
|
||||
Since the cronjob will not create a job right away, after applying the manifest,
|
||||
you can manually create an init job using the following command:
|
||||
|
||||
```console
|
||||
$ kubectl create job --from=cronjob/ecr-credentials-sync -n flux-system ecr-credentials-sync-init
|
||||
```
|
||||
|
||||
## GCP Container Registry
|
||||
|
||||
TODO
|
||||
|
||||
### Azure Container Registry
|
||||
|
||||
TODO
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user