Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
631201d541 | ||
|
|
0fbeb6d2cd | ||
|
|
11f8e2ffde | ||
|
|
055eb4a61a | ||
|
|
30c1c5c3d3 | ||
|
|
e034ec3207 | ||
|
|
8edc4bd24b | ||
|
|
6e1672f73c | ||
|
|
5e1f6f7317 | ||
|
|
386780ba12 | ||
|
|
e785971ba8 | ||
|
|
daaae07649 | ||
|
|
6cd567dc66 | ||
|
|
a541a7ee85 | ||
|
|
43572bba04 | ||
|
|
2a3a4456c1 | ||
|
|
15f8e6369b | ||
|
|
cfad9a19eb | ||
|
|
e4c3136433 | ||
|
|
73b8a26850 | ||
|
|
aa533b28fb | ||
|
|
9d70e09a57 | ||
|
|
17e18985e6 | ||
|
|
7c39aaf463 | ||
|
|
bae5c125e8 | ||
|
|
1c84fa0d97 | ||
|
|
6f583f9f0e | ||
|
|
217574b75c | ||
|
|
1378530aeb | ||
|
|
0b10ed4d88 | ||
|
|
a2887f5776 | ||
|
|
0f1d27f1e6 | ||
|
|
850ab0942b | ||
|
|
f5ae8f44b4 | ||
|
|
7f98cfd506 | ||
|
|
bc45a79b92 | ||
|
|
5003cf674d | ||
|
|
bc9cbc387c | ||
|
|
60a1e78869 | ||
|
|
37f5587085 | ||
|
|
fa6e3d3706 | ||
|
|
bb8bc875b4 | ||
|
|
b3dca737be | ||
|
|
9094f85487 | ||
|
|
1256bbfbaf | ||
|
|
24fe74f2f6 | ||
|
|
908f501e03 | ||
|
|
35507c7854 | ||
|
|
eb7102ecac | ||
|
|
ade6bfcbca | ||
|
|
fa98403aa8 | ||
|
|
3f0cb1637c | ||
|
|
42011d028e | ||
|
|
307bb0dea1 | ||
|
|
ec2a8347d4 | ||
|
|
e99b1c3ed8 | ||
|
|
99825f2663 | ||
|
|
afffdfbc5c | ||
|
|
cd874acfd5 | ||
|
|
34edbf469e | ||
|
|
d9ed30e436 |
3
.github/workflows/bootstrap.yaml
vendored
3
.github/workflows/bootstrap.yaml
vendored
@@ -49,7 +49,8 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
- name: uninstall
|
- name: uninstall
|
||||||
run: |
|
run: |
|
||||||
./bin/flux uninstall --resources --crds -s --timeout=10m
|
./bin/flux uninstall -s --keep-namespace
|
||||||
|
kubectl delete ns flux-system --timeout=10m --wait=true
|
||||||
- name: bootstrap reinstall
|
- name: bootstrap reinstall
|
||||||
run: |
|
run: |
|
||||||
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
./bin/flux bootstrap github --manifests ./manifests/install/ \
|
||||||
|
|||||||
10
.github/workflows/e2e.yaml
vendored
10
.github/workflows/e2e.yaml
vendored
@@ -171,7 +171,13 @@ jobs:
|
|||||||
./bin/flux create image policy podinfo \
|
./bin/flux create image policy podinfo \
|
||||||
--image-ref=podinfo \
|
--image-ref=podinfo \
|
||||||
--interval=1m \
|
--interval=1m \
|
||||||
--semver=5.0.x
|
--select-semver=5.0.x
|
||||||
|
- name: flux create image policy podinfo-select-alpha
|
||||||
|
run: |
|
||||||
|
./bin/flux create image policy podinfo-alpha \
|
||||||
|
--image-ref=podinfo \
|
||||||
|
--interval=1m \
|
||||||
|
--select-alpha=desc
|
||||||
- name: flux get image policy
|
- name: flux get image policy
|
||||||
run: |
|
run: |
|
||||||
./bin/flux get image policy podinfo | grep '5.0.3'
|
./bin/flux get image policy podinfo | grep '5.0.3'
|
||||||
@@ -189,7 +195,7 @@ jobs:
|
|||||||
./bin/flux check
|
./bin/flux check
|
||||||
- name: flux uninstall
|
- name: flux uninstall
|
||||||
run: |
|
run: |
|
||||||
./bin/flux uninstall --crds --silent --timeout=10m
|
./bin/flux uninstall --silent
|
||||||
- name: Debug failure
|
- name: Debug failure
|
||||||
if: failure()
|
if: failure()
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -60,13 +60,12 @@ To get started with Flux, start [browsing the
|
|||||||
documentation](https://toolkit.fluxcd.io) or get started with one of
|
documentation](https://toolkit.fluxcd.io) or get started with one of
|
||||||
the following guides:
|
the following guides:
|
||||||
|
|
||||||
- [Get started with Flux (deep dive)](https://toolkit.fluxcd.io/get-started/)
|
- [Get started with Flux](https://toolkit.fluxcd.io/get-started/)
|
||||||
- [Installation](https://toolkit.fluxcd.io/guides/installation/)
|
|
||||||
- [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/)
|
- [Manage Helm Releases](https://toolkit.fluxcd.io/guides/helmreleases/)
|
||||||
- [Setup Notifications](https://toolkit.fluxcd.io/guides/notifications/)
|
- [Automate image updates to Git](https://toolkit.fluxcd.io/guides/image-update/)
|
||||||
- [Setup Webhook Receivers](https://toolkit.fluxcd.io/guides/webhook-receivers/)
|
- [Manage Kubernetes secrets with Mozilla SOPS](https://toolkit.fluxcd.io/guides/mozilla-sops/)
|
||||||
|
|
||||||
If you should need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
If you need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
||||||
|
|
||||||
## GitOps Toolkit
|
## GitOps Toolkit
|
||||||
|
|
||||||
@@ -96,17 +95,24 @@ guides](https://toolkit.fluxcd.io/dev-guides/source-watcher/).
|
|||||||
- [Provider CRD](https://toolkit.fluxcd.io/components/notification/provider/)
|
- [Provider CRD](https://toolkit.fluxcd.io/components/notification/provider/)
|
||||||
- [Alert CRD](https://toolkit.fluxcd.io/components/notification/alert/)
|
- [Alert CRD](https://toolkit.fluxcd.io/components/notification/alert/)
|
||||||
- [Receiver CRD](https://toolkit.fluxcd.io/components/notification/receiver/)
|
- [Receiver CRD](https://toolkit.fluxcd.io/components/notification/receiver/)
|
||||||
|
- [Image Automation Controllers](https://toolkit.fluxcd.io/components/image/controller/)
|
||||||
|
- [ImageRepository CRD](https://toolkit.fluxcd.io/components/image/imagerepositories/)
|
||||||
|
- [ImagePolicy CRD](https://toolkit.fluxcd.io/components/image/imagepolicies/)
|
||||||
|
- [ImageUpdateAutomation CRD](https://toolkit.fluxcd.io/components/image/imageupdateautomations/)
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
Need help or want to contribute? Please see the links below. The Flux project is always looking for new contributors and there are a multitude of ways to get involved.
|
Need help or want to contribute? Please see the links below. The Flux project is always looking for
|
||||||
|
new contributors and there are a multitude of ways to get involved.
|
||||||
|
|
||||||
- Getting Started?
|
- Getting Started?
|
||||||
- Look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
|
- Look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
|
||||||
- Need help?
|
- Need help?
|
||||||
- First: Ask questions on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
- First: Ask questions on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||||
- Second: Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
- Second: Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
||||||
- Please follow our [Support Guidelines](https://fluxcd.io/support/) (in short: be nice, be respectful of volunteers' time, understand that maintainers and contributors cannot respond to all DMs, and keep discussions in the public #flux channel as much as possible).
|
- Please follow our [Support Guidelines](https://fluxcd.io/support/)
|
||||||
|
(in short: be nice, be respectful of volunteers' time, understand that maintainers and
|
||||||
|
contributors cannot respond to all DMs, and keep discussions in the public #flux channel as much as possible).
|
||||||
- Have feature proposals or want to contribute?
|
- Have feature proposals or want to contribute?
|
||||||
- Propose features on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
- Propose features on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||||
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
|
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
|
||||||
@@ -115,6 +121,7 @@ Need help or want to contribute? Please see the links below. The Flux project is
|
|||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
Check out our **[events calendar](https://fluxcd.io/community/#talks)**, both with upcoming talks you can attend or past events videos you can watch.
|
Check out our **[events calendar](https://fluxcd.io/community/#talks)**,
|
||||||
|
both with upcoming talks you can attend or past events videos you can watch.
|
||||||
|
|
||||||
We look forward to seeing you with us!
|
We look forward to seeing you with us!
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ type bootstrapFlags struct {
|
|||||||
requiredComponents []string
|
requiredComponents []string
|
||||||
tokenAuth bool
|
tokenAuth bool
|
||||||
clusterDomain string
|
clusterDomain string
|
||||||
|
tolerationKeys []string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -91,6 +92,8 @@ func init() {
|
|||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description())
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description())
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||||
|
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
|
||||||
|
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
|
||||||
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
||||||
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
||||||
rootCmd.AddCommand(bootstrapCmd)
|
rootCmd.AddCommand(bootstrapCmd)
|
||||||
@@ -123,6 +126,14 @@ func bootstrapValidate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
|
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
|
||||||
|
if bootstrapArgs.version == install.MakeDefaultOptions().Version {
|
||||||
|
version, err := install.GetLatestVersion()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
bootstrapArgs.version = version
|
||||||
|
}
|
||||||
|
|
||||||
opts := install.Options{
|
opts := install.Options{
|
||||||
BaseURL: localManifests,
|
BaseURL: localManifests,
|
||||||
Version: bootstrapArgs.version,
|
Version: bootstrapArgs.version,
|
||||||
@@ -138,6 +149,7 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
|
|||||||
Timeout: rootArgs.timeout,
|
Timeout: rootArgs.timeout,
|
||||||
TargetPath: targetPath,
|
TargetPath: targetPath,
|
||||||
ClusterDomain: bootstrapArgs.clusterDomain,
|
ClusterDomain: bootstrapArgs.clusterDomain,
|
||||||
|
TolerationKeys: bootstrapArgs.tolerationKeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
if localManifests == "" {
|
if localManifests == "" {
|
||||||
@@ -162,7 +174,7 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
|
|||||||
return fmt.Errorf("install failed")
|
return fmt.Errorf("install failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
statusChecker, err := NewStatusChecker(time.Second, time.Minute)
|
statusChecker, err := NewStatusChecker(time.Second, rootArgs.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("install failed: %w", err)
|
return fmt.Errorf("install failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -252,7 +264,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) {
|
func generateDeployKey(ctx context.Context, kubeClient client.Client, url *url.URL, namespace string) (string, error) {
|
||||||
pair, err := generateKeyPair(ctx, sourceArgs.GitKeyAlgorithm, sourceArgs.GitRSABits, sourceArgs.GitECDSACurve)
|
pair, err := generateKeyPair(ctx, sourceGitArgs.keyAlgorithm, sourceGitArgs.keyRSABits, sourceGitArgs.keyECDSACurve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,8 +94,8 @@ func init() {
|
|||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.owner, "owner", "", "GitHub user or organization name")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository name")
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.repository, "repository", "", "GitHub repository name")
|
||||||
bootstrapGitHubCmd.Flags().StringArrayVar(&githubArgs.teams, "team", []string{}, "GitHub team to be given maintainer access")
|
bootstrapGitHubCmd.Flags().StringArrayVar(&githubArgs.teams, "team", []string{}, "GitHub team to be given maintainer access")
|
||||||
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "is personal repository")
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.personal, "personal", false, "if true, the owner is assumed to be a GitHub user; otherwise an org")
|
||||||
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "is private repository")
|
bootstrapGitHubCmd.Flags().BoolVar(&githubArgs.private, "private", true, "if true, the repository is assumed to be private")
|
||||||
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
|
bootstrapGitHubCmd.Flags().DurationVar(&githubArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.hostname, "hostname", git.GitHubDefaultHostname, "GitHub hostname")
|
||||||
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
bootstrapGitHubCmd.Flags().StringVar(&githubArgs.sshHostname, "ssh-hostname", "", "GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||||
@@ -125,13 +125,25 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(ctx, kubeClient, rootArgs.namespace, filepath.ToSlash(githubArgs.path.String()))
|
usedPath, bootstrapPathDiffers := checkIfBootstrapPathDiffers(
|
||||||
|
ctx,
|
||||||
|
kubeClient,
|
||||||
|
rootArgs.namespace,
|
||||||
|
filepath.ToSlash(githubArgs.path.String()),
|
||||||
|
)
|
||||||
|
|
||||||
if bootstrapPathDiffers {
|
if bootstrapPathDiffers {
|
||||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
repository, err := git.NewRepository(githubArgs.repository, githubArgs.owner, githubArgs.hostname, ghToken, "flux", githubArgs.owner+"@users.noreply.github.com")
|
repository, err := git.NewRepository(
|
||||||
|
githubArgs.repository,
|
||||||
|
githubArgs.owner,
|
||||||
|
githubArgs.hostname,
|
||||||
|
ghToken,
|
||||||
|
"flux",
|
||||||
|
githubArgs.owner+"@users.noreply.github.com",
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -190,13 +202,22 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logger.Generatef("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
installManifest, err := generateInstallManifests(githubArgs.path.String(), rootArgs.namespace, tmpDir, bootstrapArgs.manifestsPath)
|
installManifest, err := generateInstallManifests(
|
||||||
|
githubArgs.path.String(),
|
||||||
|
rootArgs.namespace,
|
||||||
|
tmpDir,
|
||||||
|
bootstrapArgs.manifestsPath,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage install manifests
|
// stage install manifests
|
||||||
changed, err = repository.Commit(ctx, path.Join(githubArgs.path.String(), rootArgs.namespace), "Add manifests")
|
changed, err = repository.Commit(
|
||||||
|
ctx,
|
||||||
|
path.Join(githubArgs.path.String(), rootArgs.namespace),
|
||||||
|
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -270,13 +291,25 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// configure repo synchronization
|
// configure repo synchronization
|
||||||
logger.Actionf("generating sync manifests")
|
logger.Actionf("generating sync manifests")
|
||||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapArgs.branch, rootArgs.namespace, rootArgs.namespace, filepath.ToSlash(githubArgs.path.String()), tmpDir, githubArgs.interval)
|
syncManifests, err := generateSyncManifests(
|
||||||
|
repoURL,
|
||||||
|
bootstrapArgs.branch,
|
||||||
|
rootArgs.namespace,
|
||||||
|
rootArgs.namespace,
|
||||||
|
filepath.ToSlash(githubArgs.path.String()),
|
||||||
|
tmpDir,
|
||||||
|
githubArgs.interval,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// commit and push manifests
|
||||||
if changed, err = repository.Commit(ctx, path.Join(githubArgs.path.String(), rootArgs.namespace), "Add manifests"); err != nil {
|
if changed, err = repository.Commit(
|
||||||
|
ctx,
|
||||||
|
path.Join(githubArgs.path.String(), rootArgs.namespace),
|
||||||
|
fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version),
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ var gitlabArgs gitlabFlags
|
|||||||
func init() {
|
func init() {
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.owner, "owner", "", "GitLab user or group name")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.repository, "repository", "", "GitLab repository name")
|
||||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.personal, "personal", false, "is personal repository")
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.personal, "personal", false, "if true, the owner is assumed to be a GitLab user; otherwise a group")
|
||||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.private, "private", true, "is private repository")
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.private, "private", true, "if true, the repository is assumed to be private")
|
||||||
bootstrapGitLabCmd.Flags().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval")
|
bootstrapGitLabCmd.Flags().DurationVar(&gitlabArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.hostname, "hostname", git.GitLabDefaultHostname, "GitLab hostname")
|
||||||
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.sshHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
bootstrapGitLabCmd.Flags().StringVar(&gitlabArgs.sshHostname, "ssh-hostname", "", "GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one")
|
||||||
@@ -131,7 +131,14 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
return fmt.Errorf("cluster already bootstrapped to %v path", usedPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
repository, err := git.NewRepository(gitlabArgs.repository, gitlabArgs.owner, gitlabArgs.hostname, glToken, "flux", gitlabArgs.owner+"@users.noreply.gitlab.com")
|
repository, err := git.NewRepository(
|
||||||
|
gitlabArgs.repository,
|
||||||
|
gitlabArgs.owner,
|
||||||
|
gitlabArgs.hostname,
|
||||||
|
glToken,
|
||||||
|
"flux",
|
||||||
|
gitlabArgs.owner+"@users.noreply.gitlab.com",
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -169,13 +176,22 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// generate install manifests
|
// generate install manifests
|
||||||
logger.Generatef("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
installManifest, err := generateInstallManifests(gitlabArgs.path.String(), rootArgs.namespace, tmpDir, bootstrapArgs.manifestsPath)
|
installManifest, err := generateInstallManifests(
|
||||||
|
gitlabArgs.path.String(),
|
||||||
|
rootArgs.namespace,
|
||||||
|
tmpDir,
|
||||||
|
bootstrapArgs.manifestsPath,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage install manifests
|
// stage install manifests
|
||||||
changed, err = repository.Commit(ctx, path.Join(gitlabArgs.path.String(), rootArgs.namespace), "Add manifests")
|
changed, err = repository.Commit(
|
||||||
|
ctx,
|
||||||
|
path.Join(gitlabArgs.path.String(), rootArgs.namespace),
|
||||||
|
fmt.Sprintf("Add flux %s components manifests", bootstrapArgs.version),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -249,13 +265,25 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// configure repo synchronization
|
// configure repo synchronization
|
||||||
logger.Actionf("generating sync manifests")
|
logger.Actionf("generating sync manifests")
|
||||||
syncManifests, err := generateSyncManifests(repoURL, bootstrapArgs.branch, rootArgs.namespace, rootArgs.namespace, filepath.ToSlash(gitlabArgs.path.String()), tmpDir, gitlabArgs.interval)
|
syncManifests, err := generateSyncManifests(
|
||||||
|
repoURL,
|
||||||
|
bootstrapArgs.branch,
|
||||||
|
rootArgs.namespace,
|
||||||
|
rootArgs.namespace,
|
||||||
|
filepath.ToSlash(gitlabArgs.path.String()),
|
||||||
|
tmpDir,
|
||||||
|
gitlabArgs.interval,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit and push manifests
|
// commit and push manifests
|
||||||
if changed, err = repository.Commit(ctx, path.Join(gitlabArgs.path.String(), rootArgs.namespace), "Add manifests"); err != nil {
|
if changed, err = repository.Commit(
|
||||||
|
ctx,
|
||||||
|
path.Join(gitlabArgs.path.String(), rootArgs.namespace),
|
||||||
|
fmt.Sprintf("Add flux %s sync manifests", bootstrapArgs.version),
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
if err := repository.Push(ctx); err != nil {
|
if err := repository.Push(ctx); err != nil {
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp/syntax"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -39,9 +43,13 @@ the status of the object.`,
|
|||||||
RunE: createImagePolicyRun}
|
RunE: createImagePolicyRun}
|
||||||
|
|
||||||
type imagePolicyFlags struct {
|
type imagePolicyFlags struct {
|
||||||
imageRef string
|
imageRef string
|
||||||
semver string
|
semver string
|
||||||
filterRegex string
|
alpha string
|
||||||
|
numeric string
|
||||||
|
filterRegex string
|
||||||
|
filterExtract string
|
||||||
|
filterNumerical string
|
||||||
}
|
}
|
||||||
|
|
||||||
var imagePolicyArgs = imagePolicyFlags{}
|
var imagePolicyArgs = imagePolicyFlags{}
|
||||||
@@ -49,8 +57,11 @@ var imagePolicyArgs = imagePolicyFlags{}
|
|||||||
func init() {
|
func init() {
|
||||||
flags := createImagePolicyCmd.Flags()
|
flags := createImagePolicyCmd.Flags()
|
||||||
flags.StringVar(&imagePolicyArgs.imageRef, "image-ref", "", "the name of an image repository object")
|
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'")
|
flags.StringVar(&imagePolicyArgs.semver, "select-semver", "", "a semver range to apply to tags; e.g., '1.x'")
|
||||||
flags.StringVar(&imagePolicyArgs.filterRegex, "filter-regex", "", " regular expression pattern used to filter the image tags")
|
flags.StringVar(&imagePolicyArgs.alpha, "select-alpha", "", "use alphabetical sorting to select image; either \"asc\" meaning select the last, or \"desc\" meaning select the first")
|
||||||
|
flags.StringVar(&imagePolicyArgs.numeric, "select-numeric", "", "use numeric sorting to select image; either \"asc\" meaning select the last, or \"desc\" meaning select the first")
|
||||||
|
flags.StringVar(&imagePolicyArgs.filterRegex, "filter-regex", "", "regular expression pattern used to filter the image tags")
|
||||||
|
flags.StringVar(&imagePolicyArgs.filterExtract, "filter-extract", "", "replacement pattern (using capture groups from --filter-regex) to use for sorting")
|
||||||
|
|
||||||
createImageCmd.AddCommand(createImagePolicyCmd)
|
createImageCmd.AddCommand(createImagePolicyCmd)
|
||||||
}
|
}
|
||||||
@@ -90,18 +101,49 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
case imagePolicyArgs.semver != "" && imagePolicyArgs.alpha != "":
|
||||||
|
case imagePolicyArgs.semver != "" && imagePolicyArgs.numeric != "":
|
||||||
|
case imagePolicyArgs.alpha != "" && imagePolicyArgs.numeric != "":
|
||||||
|
return fmt.Errorf("only one of --select-semver, --select-alpha or --select-numeric can be specified")
|
||||||
case imagePolicyArgs.semver != "":
|
case imagePolicyArgs.semver != "":
|
||||||
policy.Spec.Policy.SemVer = &imagev1.SemVerPolicy{
|
policy.Spec.Policy.SemVer = &imagev1.SemVerPolicy{
|
||||||
Range: imagePolicyArgs.semver,
|
Range: imagePolicyArgs.semver,
|
||||||
}
|
}
|
||||||
|
case imagePolicyArgs.alpha != "":
|
||||||
|
if imagePolicyArgs.alpha != "desc" && imagePolicyArgs.alpha != "asc" {
|
||||||
|
return fmt.Errorf("--select-alpha must be one of [\"asc\", \"desc\"]")
|
||||||
|
}
|
||||||
|
policy.Spec.Policy.Alphabetical = &imagev1.AlphabeticalPolicy{
|
||||||
|
Order: imagePolicyArgs.alpha,
|
||||||
|
}
|
||||||
|
case imagePolicyArgs.numeric != "":
|
||||||
|
if imagePolicyArgs.numeric != "desc" && imagePolicyArgs.numeric != "asc" {
|
||||||
|
return fmt.Errorf("--select-numeric must be one of [\"asc\", \"desc\"]")
|
||||||
|
}
|
||||||
|
policy.Spec.Policy.Numerical = &imagev1.NumericalPolicy{
|
||||||
|
Order: imagePolicyArgs.numeric,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("a policy must be provided with --semver")
|
return fmt.Errorf("a policy must be provided with either --select-semver or --select-alpha")
|
||||||
}
|
}
|
||||||
|
|
||||||
if imagePolicyArgs.filterRegex != "" {
|
if imagePolicyArgs.filterRegex != "" {
|
||||||
|
exp, err := syntax.Parse(imagePolicyArgs.filterRegex, syntax.Perl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("--filter-regex is an invalid regex pattern")
|
||||||
|
}
|
||||||
policy.Spec.FilterTags = &imagev1.TagFilter{
|
policy.Spec.FilterTags = &imagev1.TagFilter{
|
||||||
Pattern: imagePolicyArgs.filterRegex,
|
Pattern: imagePolicyArgs.filterRegex,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if imagePolicyArgs.filterExtract != "" {
|
||||||
|
if err := validateExtractStr(imagePolicyArgs.filterExtract, exp.CapNames()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
policy.Spec.FilterTags.Extract = imagePolicyArgs.filterExtract
|
||||||
|
}
|
||||||
|
} else if imagePolicyArgs.filterExtract != "" {
|
||||||
|
return fmt.Errorf("cannot specify --filter-extract without specifying --filter-regex")
|
||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
@@ -117,3 +159,94 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
|
|||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Performs a dry-run of the extract function in Regexp to validate the template
|
||||||
|
func validateExtractStr(template string, capNames []string) error {
|
||||||
|
for len(template) > 0 {
|
||||||
|
i := strings.Index(template, "$")
|
||||||
|
if i < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
template = template[i:]
|
||||||
|
if len(template) > 1 && template[1] == '$' {
|
||||||
|
template = template[2:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, num, rest, ok := extract(template)
|
||||||
|
if !ok {
|
||||||
|
// Malformed extract string, assume user didn't want this
|
||||||
|
template = template[1:]
|
||||||
|
return fmt.Errorf("--filter-extract is malformed")
|
||||||
|
}
|
||||||
|
template = rest
|
||||||
|
if num >= 0 {
|
||||||
|
// we won't worry about numbers as we can't validate these
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
found := false
|
||||||
|
for _, capName := range capNames {
|
||||||
|
if name == capName {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("capture group $%s used in --filter-extract not found in --filter-regex", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract method from the regexp package
|
||||||
|
// returns the name or number of the value prepended by $
|
||||||
|
func extract(str string) (name string, num int, rest string, ok bool) {
|
||||||
|
if len(str) < 2 || str[0] != '$' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
brace := false
|
||||||
|
if str[1] == '{' {
|
||||||
|
brace = true
|
||||||
|
str = str[2:]
|
||||||
|
} else {
|
||||||
|
str = str[1:]
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for i < len(str) {
|
||||||
|
rune, size := utf8.DecodeRuneInString(str[i:])
|
||||||
|
if !unicode.IsLetter(rune) && !unicode.IsDigit(rune) && rune != '_' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += size
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
// empty name is not okay
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name = str[:i]
|
||||||
|
if brace {
|
||||||
|
if i >= len(str) || str[i] != '}' {
|
||||||
|
// missing closing brace
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse number.
|
||||||
|
num = 0
|
||||||
|
for i := 0; i < len(name); i++ {
|
||||||
|
if name[i] < '0' || '9' < name[i] || num >= 1e8 {
|
||||||
|
num = -1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
num = num*10 + int(name[i]) - '0'
|
||||||
|
}
|
||||||
|
// Disallow leading zeros.
|
||||||
|
if name[0] == '0' && len(name) > 1 {
|
||||||
|
num = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
rest = str[i:]
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -76,6 +77,7 @@ type secretGitFlags struct {
|
|||||||
keyAlgorithm flags.PublicKeyAlgorithm
|
keyAlgorithm flags.PublicKeyAlgorithm
|
||||||
rsaBits flags.RSAKeyBits
|
rsaBits flags.RSAKeyBits
|
||||||
ecdsaCurve flags.ECDSACurve
|
ecdsaCurve flags.ECDSACurve
|
||||||
|
caFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
var secretGitArgs = NewSecretGitFlags()
|
var secretGitArgs = NewSecretGitFlags()
|
||||||
@@ -87,6 +89,7 @@ func init() {
|
|||||||
createSecretGitCmd.Flags().Var(&secretGitArgs.keyAlgorithm, "ssh-key-algorithm", secretGitArgs.keyAlgorithm.Description())
|
createSecretGitCmd.Flags().Var(&secretGitArgs.keyAlgorithm, "ssh-key-algorithm", secretGitArgs.keyAlgorithm.Description())
|
||||||
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
|
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
|
||||||
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
|
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
|
||||||
|
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
||||||
|
|
||||||
createSecretCmd.AddCommand(createSecretGitCmd)
|
createSecretCmd.AddCommand(createSecretGitCmd)
|
||||||
}
|
}
|
||||||
@@ -147,11 +150,19 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add cert data when it's implemented in source-controller
|
|
||||||
secret.StringData = map[string]string{
|
secret.StringData = map[string]string{
|
||||||
"username": secretGitArgs.username,
|
"username": secretGitArgs.username,
|
||||||
"password": secretGitArgs.password,
|
"password": secretGitArgs.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if secretGitArgs.caFile != "" {
|
||||||
|
ca, err := ioutil.ReadFile(secretGitArgs.caFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read CA file '%s': %w", secretGitArgs.caFile, err)
|
||||||
|
}
|
||||||
|
secret.StringData["caFile"] = string(ca)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,19 +41,19 @@ import (
|
|||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SourceGitFlags struct {
|
type sourceGitFlags struct {
|
||||||
GitURL string
|
url string
|
||||||
GitBranch string
|
branch string
|
||||||
GitTag string
|
tag string
|
||||||
GitSemver string
|
semver string
|
||||||
GitUsername string
|
username string
|
||||||
GitPassword string
|
password string
|
||||||
|
caFile string
|
||||||
GitKeyAlgorithm flags.PublicKeyAlgorithm
|
keyAlgorithm flags.PublicKeyAlgorithm
|
||||||
GitRSABits flags.RSAKeyBits
|
keyRSABits flags.RSAKeyBits
|
||||||
GitECDSACurve flags.ECDSACurve
|
keyECDSACurve flags.ECDSACurve
|
||||||
GitSecretRef string
|
secretRef string
|
||||||
GitImplementation flags.GitImplementation
|
gitImplementation flags.GitImplementation
|
||||||
}
|
}
|
||||||
|
|
||||||
var createSourceGitCmd = &cobra.Command{
|
var createSourceGitCmd = &cobra.Command{
|
||||||
@@ -100,29 +100,30 @@ For private Git repositories, the basic authentication credentials are stored in
|
|||||||
RunE: createSourceGitCmdRun,
|
RunE: createSourceGitCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceArgs = NewSourceGitFlags()
|
var sourceGitArgs = newSourceGitFlags()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitURL, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.url, "url", "", "git address, e.g. ssh://git@host/org/repository")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitBranch, "branch", "master", "git branch")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.branch, "branch", "master", "git branch")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitTag, "tag", "", "git tag")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.tag, "tag", "", "git tag")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceArgs.GitSemver, "tag-semver", "", "git tag semver range")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.semver, "tag-semver", "", "git tag semver range")
|
||||||
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitUsername, "username", "u", "", "basic authentication username")
|
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.username, "username", "u", "", "basic authentication username")
|
||||||
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitPassword, "password", "p", "", "basic authentication password")
|
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.password, "password", "p", "", "basic authentication password")
|
||||||
createSourceGitCmd.Flags().Var(&sourceArgs.GitKeyAlgorithm, "ssh-key-algorithm", sourceArgs.GitKeyAlgorithm.Description())
|
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyAlgorithm, "ssh-key-algorithm", sourceGitArgs.keyAlgorithm.Description())
|
||||||
createSourceGitCmd.Flags().Var(&sourceArgs.GitRSABits, "ssh-rsa-bits", sourceArgs.GitRSABits.Description())
|
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyRSABits, "ssh-rsa-bits", sourceGitArgs.keyRSABits.Description())
|
||||||
createSourceGitCmd.Flags().Var(&sourceArgs.GitECDSACurve, "ssh-ecdsa-curve", sourceArgs.GitECDSACurve.Description())
|
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyECDSACurve, "ssh-ecdsa-curve", sourceGitArgs.keyECDSACurve.Description())
|
||||||
createSourceGitCmd.Flags().StringVarP(&sourceArgs.GitSecretRef, "secret-ref", "", "", "the name of an existing secret containing SSH or basic credentials")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials")
|
||||||
createSourceGitCmd.Flags().Var(&sourceArgs.GitImplementation, "git-implementation", sourceArgs.GitImplementation.Description())
|
createSourceGitCmd.Flags().Var(&sourceGitArgs.gitImplementation, "git-implementation", sourceGitArgs.gitImplementation.Description())
|
||||||
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates, requires libgit2")
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceGitCmd)
|
createSourceCmd.AddCommand(createSourceGitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSourceGitFlags() SourceGitFlags {
|
func newSourceGitFlags() sourceGitFlags {
|
||||||
return SourceGitFlags{
|
return sourceGitFlags{
|
||||||
GitKeyAlgorithm: "rsa",
|
keyAlgorithm: "rsa",
|
||||||
GitRSABits: 2048,
|
keyRSABits: 2048,
|
||||||
GitECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
keyECDSACurve: flags.ECDSACurve{Curve: elliptic.P384()},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,17 +133,21 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
if sourceArgs.GitURL == "" {
|
if sourceGitArgs.url == "" {
|
||||||
return fmt.Errorf("url is required")
|
return fmt.Errorf("url is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sourceGitArgs.gitImplementation.String() != sourcev1.LibGit2Implementation && sourceGitArgs.caFile != "" {
|
||||||
|
return fmt.Errorf("specifing a CA file requires --git-implementation=%s", sourcev1.LibGit2Implementation)
|
||||||
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", name)
|
tmpDir, err := ioutil.TempDir("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
u, err := url.Parse(sourceArgs.GitURL)
|
u, err := url.Parse(sourceGitArgs.url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git URL parse failed: %w", err)
|
return fmt.Errorf("git URL parse failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -159,7 +164,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: sourcev1.GitRepositorySpec{
|
Spec: sourcev1.GitRepositorySpec{
|
||||||
URL: sourceArgs.GitURL,
|
URL: sourceGitArgs.url,
|
||||||
Interval: metav1.Duration{
|
Interval: metav1.Duration{
|
||||||
Duration: createArgs.interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
@@ -167,22 +172,22 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceArgs.GitImplementation != "" {
|
if sourceGitArgs.gitImplementation != "" {
|
||||||
gitRepository.Spec.GitImplementation = sourceArgs.GitImplementation.String()
|
gitRepository.Spec.GitImplementation = sourceGitArgs.gitImplementation.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceArgs.GitSemver != "" {
|
if sourceGitArgs.semver != "" {
|
||||||
gitRepository.Spec.Reference.SemVer = sourceArgs.GitSemver
|
gitRepository.Spec.Reference.SemVer = sourceGitArgs.semver
|
||||||
} else if sourceArgs.GitTag != "" {
|
} else if sourceGitArgs.tag != "" {
|
||||||
gitRepository.Spec.Reference.Tag = sourceArgs.GitTag
|
gitRepository.Spec.Reference.Tag = sourceGitArgs.tag
|
||||||
} else {
|
} else {
|
||||||
gitRepository.Spec.Reference.Branch = sourceArgs.GitBranch
|
gitRepository.Spec.Reference.Branch = sourceGitArgs.branch
|
||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
if sourceArgs.GitSecretRef != "" {
|
if sourceGitArgs.secretRef != "" {
|
||||||
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: sourceArgs.GitSecretRef,
|
Name: sourceGitArgs.secretRef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return exportGit(gitRepository)
|
return exportGit(gitRepository)
|
||||||
@@ -198,11 +203,11 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
withAuth := false
|
withAuth := false
|
||||||
// TODO(hidde): move all auth prep to separate func?
|
// TODO(hidde): move all auth prep to separate func?
|
||||||
if sourceArgs.GitSecretRef != "" {
|
if sourceGitArgs.secretRef != "" {
|
||||||
withAuth = true
|
withAuth = true
|
||||||
} else if u.Scheme == "ssh" {
|
} else if u.Scheme == "ssh" {
|
||||||
logger.Generatef("generating deploy key pair")
|
logger.Generatef("generating deploy key pair")
|
||||||
pair, err := generateKeyPair(ctx, sourceArgs.GitKeyAlgorithm, sourceArgs.GitRSABits, sourceArgs.GitECDSACurve)
|
pair, err := generateKeyPair(ctx, sourceGitArgs.keyAlgorithm, sourceGitArgs.keyRSABits, sourceGitArgs.keyECDSACurve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -240,7 +245,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
withAuth = true
|
withAuth = true
|
||||||
} else if sourceArgs.GitUsername != "" && sourceArgs.GitPassword != "" {
|
} else if sourceGitArgs.username != "" && sourceGitArgs.password != "" {
|
||||||
logger.Actionf("applying secret with basic auth credentials")
|
logger.Actionf("applying secret with basic auth credentials")
|
||||||
secret := corev1.Secret{
|
secret := corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -249,10 +254,19 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{
|
StringData: map[string]string{
|
||||||
"username": sourceArgs.GitUsername,
|
"username": sourceGitArgs.username,
|
||||||
"password": sourceArgs.GitPassword,
|
"password": sourceGitArgs.password,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sourceGitArgs.caFile != "" {
|
||||||
|
ca, err := ioutil.ReadFile(sourceGitArgs.caFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read CA file '%s': %w", sourceGitArgs.caFile, err)
|
||||||
|
}
|
||||||
|
secret.StringData["caFile"] = string(ca)
|
||||||
|
}
|
||||||
|
|
||||||
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
if err := upsertSecret(ctx, kubeClient, secret); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -267,8 +281,8 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
if withAuth {
|
if withAuth {
|
||||||
secretName := name
|
secretName := name
|
||||||
if sourceArgs.GitSecretRef != "" {
|
if sourceGitArgs.secretRef != "" {
|
||||||
secretName = sourceArgs.GitSecretRef
|
secretName = sourceGitArgs.secretRef
|
||||||
}
|
}
|
||||||
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
gitRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
|
|||||||
@@ -34,15 +34,18 @@ import (
|
|||||||
|
|
||||||
var installCmd = &cobra.Command{
|
var installCmd = &cobra.Command{
|
||||||
Use: "install",
|
Use: "install",
|
||||||
Short: "Install the toolkit components",
|
Short: "Install or upgrade Flux",
|
||||||
Long: `The install command deploys the toolkit components in the specified namespace.
|
Long: `The install command deploys Flux in the specified namespace.
|
||||||
If a previous version is installed, then an in-place upgrade will be performed.`,
|
If a previous version is installed, then an in-place upgrade will be performed.`,
|
||||||
Example: ` # Install the latest version in the flux-system namespace
|
Example: ` # Install the latest version in the flux-system namespace
|
||||||
flux install --version=latest --namespace=flux-system
|
flux install --version=latest --namespace=flux-system
|
||||||
|
|
||||||
# Dry-run install for a specific version and a series of components
|
# Install a specific version and a series of components
|
||||||
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
|
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
|
||||||
|
|
||||||
|
# Install Flux onto tainted Kubernetes nodes
|
||||||
|
flux install --toleration-keys=node.kubernetes.io/dedicated-to-flux
|
||||||
|
|
||||||
# Dry-run install with manifests preview
|
# Dry-run install with manifests preview
|
||||||
flux install --dry-run --verbose
|
flux install --dry-run --verbose
|
||||||
|
|
||||||
@@ -66,6 +69,7 @@ var (
|
|||||||
installArch flags.Arch
|
installArch flags.Arch
|
||||||
installLogLevel = flags.LogLevel(rootArgs.defaults.LogLevel)
|
installLogLevel = flags.LogLevel(rootArgs.defaults.LogLevel)
|
||||||
installClusterDomain string
|
installClusterDomain string
|
||||||
|
installTolerationKeys []string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -91,6 +95,8 @@ func init() {
|
|||||||
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy,
|
installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", rootArgs.defaults.NetworkPolicy,
|
||||||
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
||||||
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
installCmd.Flags().StringVar(&installClusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||||
|
installCmd.Flags().StringSliceVar(&installTolerationKeys, "toleration-keys", nil,
|
||||||
|
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
|
||||||
installCmd.Flags().MarkHidden("manifests")
|
installCmd.Flags().MarkHidden("manifests")
|
||||||
installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
installCmd.Flags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
||||||
rootCmd.AddCommand(installCmd)
|
rootCmd.AddCommand(installCmd)
|
||||||
@@ -116,6 +122,13 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if installVersion == install.MakeDefaultOptions().Version {
|
||||||
|
installVersion, err = install.GetLatestVersion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opts := install.Options{
|
opts := install.Options{
|
||||||
BaseURL: installManifestsPath,
|
BaseURL: installManifestsPath,
|
||||||
Version: installVersion,
|
Version: installVersion,
|
||||||
@@ -130,6 +143,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace),
|
ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace),
|
||||||
Timeout: rootArgs.timeout,
|
Timeout: rootArgs.timeout,
|
||||||
ClusterDomain: installClusterDomain,
|
ClusterDomain: installClusterDomain,
|
||||||
|
TolerationKeys: installTolerationKeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
if installManifestsPath == "" {
|
if installManifestsPath == "" {
|
||||||
@@ -149,7 +163,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Print(manifest.Content)
|
fmt.Print(manifest.Content)
|
||||||
} else if installExport {
|
} else if installExport {
|
||||||
fmt.Println("---")
|
fmt.Println("---")
|
||||||
fmt.Println("# GitOps Toolkit revision", installVersion)
|
fmt.Println("# Flux version:", installVersion)
|
||||||
fmt.Println("# Components:", strings.Join(components, ","))
|
fmt.Println("# Components:", strings.Join(components, ","))
|
||||||
fmt.Print(manifest.Content)
|
fmt.Print(manifest.Content)
|
||||||
fmt.Println("---")
|
fmt.Println("---")
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ var rootCmd = &cobra.Command{
|
|||||||
Example: ` # Check prerequisites
|
Example: ` # Check prerequisites
|
||||||
flux check --pre
|
flux check --pre
|
||||||
|
|
||||||
# Install the latest version of the toolkit
|
# Install the latest version of Flux
|
||||||
flux install --version=master
|
flux install --version=master
|
||||||
|
|
||||||
# Create a source from a public Git repository
|
# Create a source from a public Git repository
|
||||||
@@ -88,8 +88,8 @@ var rootCmd = &cobra.Command{
|
|||||||
# Delete a GitRepository source
|
# Delete a GitRepository source
|
||||||
flux delete source git webapp-latest
|
flux delete source git webapp-latest
|
||||||
|
|
||||||
# Uninstall the toolkit and delete CRDs
|
# Uninstall Flux and delete CRDs
|
||||||
flux uninstall --crds
|
flux uninstall
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,12 @@ import (
|
|||||||
|
|
||||||
"github.com/manifoldco/promptui"
|
"github.com/manifoldco/promptui"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
@@ -34,33 +38,30 @@ import (
|
|||||||
|
|
||||||
var uninstallCmd = &cobra.Command{
|
var uninstallCmd = &cobra.Command{
|
||||||
Use: "uninstall",
|
Use: "uninstall",
|
||||||
Short: "Uninstall the toolkit components",
|
Short: "Uninstall Flux and its custom resource definitions",
|
||||||
Long: "The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.",
|
Long: "The uninstall command removes the Flux components and the toolkit.fluxcd.io resources from the cluster.",
|
||||||
Example: ` # Dry-run uninstall of all components
|
Example: ` # Uninstall Flux components, its custom resources and namespace
|
||||||
flux uninstall --dry-run --namespace=flux-system
|
flux uninstall --namespace=flux-system
|
||||||
|
|
||||||
# Uninstall all components and delete custom resource definitions
|
# Uninstall Flux but keep the namespace
|
||||||
flux uninstall --resources --crds --namespace=flux-system
|
flux uninstall --namespace=infra --keep-namespace=true
|
||||||
`,
|
`,
|
||||||
RunE: uninstallCmdRun,
|
RunE: uninstallCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
type uninstallFlags struct {
|
type uninstallFlags struct {
|
||||||
crds bool
|
keepNamespace bool
|
||||||
resources bool
|
dryRun bool
|
||||||
dryRun bool
|
silent bool
|
||||||
silent bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var uninstallArgs uninstallFlags
|
var uninstallArgs uninstallFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
uninstallCmd.Flags().BoolVar(&uninstallArgs.resources, "resources", true,
|
uninstallCmd.Flags().BoolVar(&uninstallArgs.keepNamespace, "keep-namespace", false,
|
||||||
"removes custom resources such as Kustomizations, GitRepositories and HelmRepositories")
|
"skip namespace deletion")
|
||||||
uninstallCmd.Flags().BoolVar(&uninstallArgs.crds, "crds", false,
|
|
||||||
"removes all CRDs previously installed")
|
|
||||||
uninstallCmd.Flags().BoolVar(&uninstallArgs.dryRun, "dry-run", false,
|
uninstallCmd.Flags().BoolVar(&uninstallArgs.dryRun, "dry-run", false,
|
||||||
"only print the object that would be deleted")
|
"only print the objects that would be deleted")
|
||||||
uninstallCmd.Flags().BoolVarP(&uninstallArgs.silent, "silent", "s", false,
|
uninstallCmd.Flags().BoolVarP(&uninstallArgs.silent, "silent", "s", false,
|
||||||
"delete components without asking for confirmation")
|
"delete components without asking for confirmation")
|
||||||
|
|
||||||
@@ -68,6 +69,16 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if !uninstallArgs.dryRun && !uninstallArgs.silent {
|
||||||
|
prompt := promptui.Prompt{
|
||||||
|
Label: "Are you sure you want to delete Flux and its custom resource definitions",
|
||||||
|
IsConfirm: true,
|
||||||
|
}
|
||||||
|
if _, err := prompt.Run(); err != nil {
|
||||||
|
return fmt.Errorf("aborting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -76,96 +87,227 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !uninstallArgs.dryRun && !uninstallArgs.silent {
|
logger.Actionf("deleting components in %s namespace", rootArgs.namespace)
|
||||||
prompt := promptui.Prompt{
|
uninstallComponents(ctx, kubeClient, rootArgs.namespace, uninstallArgs.dryRun)
|
||||||
Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", rootArgs.namespace),
|
|
||||||
IsConfirm: true,
|
|
||||||
}
|
|
||||||
if _, err := prompt.Run(); err != nil {
|
|
||||||
return fmt.Errorf("aborting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dryRun := "--dry-run=server"
|
logger.Actionf("deleting toolkit.fluxcd.io finalizers in all namespaces")
|
||||||
deleteResources := uninstallArgs.resources || uninstallArgs.crds
|
uninstallFinalizers(ctx, kubeClient, uninstallArgs.dryRun)
|
||||||
|
|
||||||
// known kinds with finalizers
|
logger.Actionf("deleting toolkit.fluxcd.io custom resource definitions")
|
||||||
namespacedKinds := []string{
|
uninstallCustomResourceDefinitions(ctx, kubeClient, rootArgs.namespace, uninstallArgs.dryRun)
|
||||||
sourcev1.GitRepositoryKind,
|
|
||||||
sourcev1.HelmRepositoryKind,
|
|
||||||
sourcev1.BucketKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
// suspend bootstrap kustomization to avoid finalizers deadlock
|
if !uninstallArgs.keepNamespace {
|
||||||
kustomizationName := types.NamespacedName{
|
uninstallNamespace(ctx, kubeClient, rootArgs.namespace, uninstallArgs.dryRun)
|
||||||
Namespace: rootArgs.namespace,
|
|
||||||
Name: rootArgs.namespace,
|
|
||||||
}
|
|
||||||
var kustomization kustomizev1.Kustomization
|
|
||||||
err = kubeClient.Get(ctx, kustomizationName, &kustomization)
|
|
||||||
if err == nil {
|
|
||||||
kustomization.Spec.Suspend = true
|
|
||||||
if err := kubeClient.Update(ctx, &kustomization); err != nil {
|
|
||||||
return fmt.Errorf("unable to suspend kustomization '%s': %w", kustomizationName.String(), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil || apierrors.IsNotFound(err) {
|
|
||||||
namespacedKinds = append(namespacedKinds, kustomizev1.KustomizationKind)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add HelmRelease kind to deletion list if exists
|
|
||||||
var list helmv2.HelmReleaseList
|
|
||||||
if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace)); err == nil {
|
|
||||||
namespacedKinds = append(namespacedKinds, helmv2.HelmReleaseKind)
|
|
||||||
}
|
|
||||||
|
|
||||||
if deleteResources {
|
|
||||||
logger.Actionf("uninstalling custom resources")
|
|
||||||
for _, kind := range namespacedKinds {
|
|
||||||
if err := deleteAll(ctx, kind, uninstallArgs.dryRun); err != nil {
|
|
||||||
logger.Failuref("kubectl: %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var kinds []string
|
|
||||||
if uninstallArgs.crds {
|
|
||||||
kinds = append(kinds, "crds")
|
|
||||||
}
|
|
||||||
|
|
||||||
kinds = append(kinds, "clusterroles,clusterrolebindings", "namespace")
|
|
||||||
|
|
||||||
logger.Actionf("uninstalling components")
|
|
||||||
|
|
||||||
for _, kind := range kinds {
|
|
||||||
kubectlArgs := []string{
|
|
||||||
"delete", kind,
|
|
||||||
"-l", fmt.Sprintf("app.kubernetes.io/instance=%s", rootArgs.namespace),
|
|
||||||
"--ignore-not-found", "--timeout", rootArgs.timeout.String(),
|
|
||||||
}
|
|
||||||
if uninstallArgs.dryRun {
|
|
||||||
kubectlArgs = append(kubectlArgs, dryRun)
|
|
||||||
}
|
|
||||||
if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err != nil {
|
|
||||||
return fmt.Errorf("uninstall failed: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Successf("uninstall finished")
|
logger.Successf("uninstall finished")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteAll(ctx context.Context, kind string, dryRun bool) error {
|
func uninstallComponents(ctx context.Context, kubeClient client.Client, namespace string, dryRun bool) {
|
||||||
kubectlArgs := []string{
|
opts, dryRunStr := getDeleteOptions(dryRun)
|
||||||
"delete", kind, "--ignore-not-found",
|
selector := client.MatchingLabels{"app.kubernetes.io/instance": namespace}
|
||||||
"--all", "--all-namespaces",
|
{
|
||||||
"--timeout", rootArgs.timeout.String(),
|
var list appsv1.DeploymentList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace), selector); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("Deployment/%s/%s deletion failed: %s", r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("Deployment/%s/%s deleted %s", r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
if dryRun {
|
var list corev1.ServiceList
|
||||||
kubectlArgs = append(kubectlArgs, "--dry-run=server")
|
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace), selector); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("Service/%s/%s deletion failed: %s", r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("Service/%s/%s deleted %s", r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list networkingv1.NetworkPolicyList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace), selector); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("NetworkPolicy/%s/%s deletion failed: %s", r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("NetworkPolicy/%s/%s deleted %s", r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list corev1.ServiceAccountList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace(namespace), selector); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("ServiceAccount/%s/%s deletion failed: %s", r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("ServiceAccount/%s/%s deleted %s", r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list rbacv1.ClusterRoleList
|
||||||
|
if err := kubeClient.List(ctx, &list, selector); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("ClusterRole/%s deletion failed: %s", r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("ClusterRole/%s deleted %s", r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list rbacv1.ClusterRoleBindingList
|
||||||
|
if err := kubeClient.List(ctx, &list, selector); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("ClusterRoleBinding/%s deletion failed: %s", r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("ClusterRoleBinding/%s deleted %s", r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
|
|
||||||
return err
|
func uninstallFinalizers(ctx context.Context, kubeClient client.Client, dryRun bool) {
|
||||||
|
opts, dryRunStr := getUpdateOptions(dryRun)
|
||||||
|
{
|
||||||
|
var list sourcev1.GitRepositoryList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
r.Finalizers = []string{}
|
||||||
|
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list sourcev1.HelmRepositoryList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
r.Finalizers = []string{}
|
||||||
|
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list sourcev1.HelmChartList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
r.Finalizers = []string{}
|
||||||
|
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list sourcev1.BucketList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
r.Finalizers = []string{}
|
||||||
|
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list kustomizev1.KustomizationList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
r.Finalizers = []string{}
|
||||||
|
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var list helmv2.HelmReleaseList
|
||||||
|
if err := kubeClient.List(ctx, &list, client.InNamespace("")); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
r.Finalizers = []string{}
|
||||||
|
if err := kubeClient.Update(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("%s/%s/%s removing finalizers failed: %s", r.Kind, r.Namespace, r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("%s/%s/%s finalizers deleted %s", r.Kind, r.Namespace, r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func uninstallCustomResourceDefinitions(ctx context.Context, kubeClient client.Client, namespace string, dryRun bool) {
|
||||||
|
opts, dryRunStr := getDeleteOptions(dryRun)
|
||||||
|
selector := client.MatchingLabels{"app.kubernetes.io/instance": namespace}
|
||||||
|
{
|
||||||
|
var list apiextensionsv1.CustomResourceDefinitionList
|
||||||
|
if err := kubeClient.List(ctx, &list, selector); err == nil {
|
||||||
|
for _, r := range list.Items {
|
||||||
|
if err := kubeClient.Delete(ctx, &r, opts); err != nil {
|
||||||
|
logger.Failuref("CustomResourceDefinition/%s deletion failed: %s", r.Name, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("CustomResourceDefinition/%s deleted %s", r.Name, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func uninstallNamespace(ctx context.Context, kubeClient client.Client, namespace string, dryRun bool) {
|
||||||
|
opts, dryRunStr := getDeleteOptions(dryRun)
|
||||||
|
ns := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
|
||||||
|
if err := kubeClient.Delete(ctx, &ns, opts); err != nil {
|
||||||
|
logger.Failuref("Namespace/%s deletion failed: %s", namespace, err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Successf("Namespace/%s deleted %s", namespace, dryRunStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDeleteOptions(dryRun bool) (*client.DeleteOptions, string) {
|
||||||
|
opts := &client.DeleteOptions{}
|
||||||
|
var dryRunStr string
|
||||||
|
if dryRun {
|
||||||
|
client.DryRunAll.ApplyToDelete(opts)
|
||||||
|
dryRunStr = "(dry run)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts, dryRunStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUpdateOptions(dryRun bool) (*client.UpdateOptions, string) {
|
||||||
|
opts := &client.UpdateOptions{}
|
||||||
|
var dryRunStr string
|
||||||
|
if dryRun {
|
||||||
|
client.DryRunAll.ApplyToUpdate(opts)
|
||||||
|
dryRunStr = "(dry run)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts, dryRunStr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
|||||||
# Check prerequisites
|
# Check prerequisites
|
||||||
flux check --pre
|
flux check --pre
|
||||||
|
|
||||||
# Install the latest version of the toolkit
|
# Install the latest version of Flux
|
||||||
flux install --version=master
|
flux install --version=master
|
||||||
|
|
||||||
# Create a source from a public Git repository
|
# Create a source from a public Git repository
|
||||||
@@ -59,8 +59,8 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
|||||||
# Delete a GitRepository source
|
# Delete a GitRepository source
|
||||||
flux delete source git webapp-latest
|
flux delete source git webapp-latest
|
||||||
|
|
||||||
# Uninstall the toolkit and delete CRDs
|
# Uninstall Flux and delete CRDs
|
||||||
flux uninstall --crds
|
flux uninstall
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -84,9 +84,9 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.
|
|||||||
* [flux delete](flux_delete.md) - Delete sources and resources
|
* [flux delete](flux_delete.md) - Delete sources and resources
|
||||||
* [flux export](flux_export.md) - Export resources in YAML format
|
* [flux export](flux_export.md) - Export resources in YAML format
|
||||||
* [flux get](flux_get.md) - Get sources and resources
|
* [flux get](flux_get.md) - Get sources and resources
|
||||||
* [flux install](flux_install.md) - Install the toolkit components
|
* [flux install](flux_install.md) - Install or upgrade Flux
|
||||||
* [flux reconcile](flux_reconcile.md) - Reconcile sources and resources
|
* [flux reconcile](flux_reconcile.md) - Reconcile sources and resources
|
||||||
* [flux resume](flux_resume.md) - Resume suspended resources
|
* [flux resume](flux_resume.md) - Resume suspended resources
|
||||||
* [flux suspend](flux_suspend.md) - Suspend resources
|
* [flux suspend](flux_suspend.md) - Suspend resources
|
||||||
* [flux uninstall](flux_uninstall.md) - Uninstall the toolkit components
|
* [flux uninstall](flux_uninstall.md) - Uninstall Flux and its custom resource definitions
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ The bootstrap sub-commands bootstrap the toolkit components on the targeted Git
|
|||||||
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
|
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
|
||||||
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
||||||
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
||||||
|
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints
|
||||||
-v, --version string toolkit version (default "latest")
|
-v, --version string toolkit version (default "latest")
|
||||||
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ flux bootstrap github [flags]
|
|||||||
--interval duration sync interval (default 1m0s)
|
--interval duration sync interval (default 1m0s)
|
||||||
--owner string GitHub user or organization name
|
--owner string GitHub user or organization name
|
||||||
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||||
--personal is personal repository
|
--personal if true, the owner is assumed to be a GitHub user; otherwise an org
|
||||||
--private is private repository (default true)
|
--private if true, the repository is assumed to be private (default true)
|
||||||
--repository string GitHub repository name
|
--repository string GitHub repository name
|
||||||
--ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one
|
--ssh-hostname string GitHub SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||||
--team stringArray GitHub team to be given maintainer access
|
--team stringArray GitHub team to be given maintainer access
|
||||||
@@ -74,6 +74,7 @@ flux bootstrap github [flags]
|
|||||||
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
||||||
|
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints
|
||||||
--verbose print generated objects
|
--verbose print generated objects
|
||||||
-v, --version string toolkit version (default "latest")
|
-v, --version string toolkit version (default "latest")
|
||||||
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ flux bootstrap gitlab [flags]
|
|||||||
--interval duration sync interval (default 1m0s)
|
--interval duration sync interval (default 1m0s)
|
||||||
--owner string GitLab user or group name
|
--owner string GitLab user or group name
|
||||||
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
--path safeRelativePath path relative to the repository root, when specified the cluster sync will be scoped to this path
|
||||||
--personal is personal repository
|
--personal if true, the owner is assumed to be a GitLab user; otherwise a group
|
||||||
--private is private repository (default true)
|
--private if true, the repository is assumed to be private (default true)
|
||||||
--repository string GitLab repository name
|
--repository string GitLab repository name
|
||||||
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
--ssh-hostname string GitLab SSH hostname, to be used when the SSH host differs from the HTTPS one
|
||||||
```
|
```
|
||||||
@@ -70,6 +70,7 @@ flux bootstrap gitlab [flags]
|
|||||||
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
||||||
--timeout duration timeout for this operation (default 5m0s)
|
--timeout duration timeout for this operation (default 5m0s)
|
||||||
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
--token-auth when enabled, the personal access token will be used instead of SSH deploy key
|
||||||
|
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints
|
||||||
--verbose print generated objects
|
--verbose print generated objects
|
||||||
-v, --version string toolkit version (default "latest")
|
-v, --version string toolkit version (default "latest")
|
||||||
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
||||||
|
|||||||
@@ -18,10 +18,13 @@ flux create image policy <name> [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
--filter-regex string regular expression pattern used to filter the image tags
|
--filter-extract string replacement pattern (using capture groups from --filter-regex) to use for sorting
|
||||||
-h, --help help for policy
|
--filter-regex string regular expression pattern used to filter the image tags
|
||||||
--image-ref string the name of an image repository object
|
-h, --help help for policy
|
||||||
--semver string a semver range to apply to tags; e.g., '1.x'
|
--image-ref string the name of an image repository object
|
||||||
|
--select-alpha string use alphabetical sorting to select image; either "asc" meaning select the last, or "desc" meaning select the first
|
||||||
|
--select-numeric string use numeric sorting to select image; either "asc" meaning select the last, or "desc" meaning select the first
|
||||||
|
--select-semver string a semver range to apply to tags; e.g., '1.x'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ flux create secret git [name] [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
|
--ca-file string path to TLS CA file used for validating self-signed certificates
|
||||||
-h, --help help for git
|
-h, --help help for git
|
||||||
-p, --password string basic authentication password
|
-p, --password string basic authentication password
|
||||||
--ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p256, p384, p521) (default p384)
|
--ssh-ecdsa-curve ecdsaCurve SSH ECDSA public key curve (p256, p384, p521) (default p384)
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ flux create source git [name] [flags]
|
|||||||
|
|
||||||
```
|
```
|
||||||
--branch string git branch (default "master")
|
--branch string git branch (default "master")
|
||||||
|
--ca-file string path to TLS CA file used for validating self-signed certificates, requires libgit2
|
||||||
--git-implementation gitImplementation the Git implementation to use, available options are: (go-git, libgit2)
|
--git-implementation gitImplementation the Git implementation to use, available options are: (go-git, libgit2)
|
||||||
-h, --help help for git
|
-h, --help help for git
|
||||||
-p, --password string basic authentication password
|
-p, --password string basic authentication password
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## flux install
|
## flux install
|
||||||
|
|
||||||
Install the toolkit components
|
Install or upgrade Flux
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
The install command deploys the toolkit components in the specified namespace.
|
The install command deploys Flux in the specified namespace.
|
||||||
If a previous version is installed, then an in-place upgrade will be performed.
|
If a previous version is installed, then an in-place upgrade will be performed.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -17,9 +17,12 @@ flux install [flags]
|
|||||||
# Install the latest version in the flux-system namespace
|
# Install the latest version in the flux-system namespace
|
||||||
flux install --version=latest --namespace=flux-system
|
flux install --version=latest --namespace=flux-system
|
||||||
|
|
||||||
# Dry-run install for a specific version and a series of components
|
# Install a specific version and a series of components
|
||||||
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
|
flux install --dry-run --version=v0.0.7 --components="source-controller,kustomize-controller"
|
||||||
|
|
||||||
|
# Install Flux onto tainted Kubernetes nodes
|
||||||
|
flux install --toleration-keys=node.kubernetes.io/dedicated-to-flux
|
||||||
|
|
||||||
# Dry-run install with manifests preview
|
# Dry-run install with manifests preview
|
||||||
flux install --dry-run --verbose
|
flux install --dry-run --verbose
|
||||||
|
|
||||||
@@ -41,6 +44,7 @@ flux install [flags]
|
|||||||
--log-level logLevel log level, available options are: (debug, info, error) (default info)
|
--log-level logLevel log level, available options are: (debug, info, error) (default info)
|
||||||
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
|
--network-policy deny ingress access to the toolkit controllers from other namespaces using network policies (default true)
|
||||||
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
--registry string container registry where the toolkit images are published (default "ghcr.io/fluxcd")
|
||||||
|
--toleration-keys strings list of toleration keys used to schedule the components pods onto nodes with matching taints
|
||||||
-v, --version string toolkit version (default "latest")
|
-v, --version string toolkit version (default "latest")
|
||||||
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
--watch-all-namespaces watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed (default true)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## flux uninstall
|
## flux uninstall
|
||||||
|
|
||||||
Uninstall the toolkit components
|
Uninstall Flux and its custom resource definitions
|
||||||
|
|
||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
The uninstall command removes the namespace, cluster roles, cluster role bindings and CRDs from the cluster.
|
The uninstall command removes the Flux components and the toolkit.fluxcd.io resources from the cluster.
|
||||||
|
|
||||||
```
|
```
|
||||||
flux uninstall [flags]
|
flux uninstall [flags]
|
||||||
@@ -13,22 +13,21 @@ flux uninstall [flags]
|
|||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```
|
```
|
||||||
# Dry-run uninstall of all components
|
# Uninstall Flux components, its custom resources and namespace
|
||||||
flux uninstall --dry-run --namespace=flux-system
|
flux uninstall --namespace=flux-system
|
||||||
|
|
||||||
# Uninstall all components and delete custom resource definitions
|
# Uninstall Flux but keep the namespace
|
||||||
flux uninstall --resources --crds --namespace=flux-system
|
flux uninstall --namespace=infra --keep-namespace=true
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
--crds removes all CRDs previously installed
|
--dry-run only print the objects that would be deleted
|
||||||
--dry-run only print the object that would be deleted
|
-h, --help help for uninstall
|
||||||
-h, --help help for uninstall
|
--keep-namespace skip namespace deletion
|
||||||
--resources removes custom resources such as Kustomizations, GitRepositories and HelmRepositories (default true)
|
-s, --silent delete components without asking for confirmation
|
||||||
-s, --silent delete components without asking for confirmation
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|||||||
42
docs/dev-guides/debugging.md
Normal file
42
docs/dev-guides/debugging.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Advanced debugging
|
||||||
|
|
||||||
|
This guide covers more advanced debugging topics such as collecting
|
||||||
|
runtime profiling data from GitOps Toolkit components.
|
||||||
|
|
||||||
|
As a user, this page normally should be a last resort, but you may
|
||||||
|
be asked by a maintainer to share a [collected profile](#collecting-a-profile)
|
||||||
|
to debug e.g. performance issues.
|
||||||
|
|
||||||
|
## Pprof
|
||||||
|
|
||||||
|
The [GitOps Toolkit components](../components/index.md) serve [`pprof`](https://golang.org/pkg/net/http/pprof/)
|
||||||
|
runtime profiling data on their metrics HTTP server (default `:8080`).
|
||||||
|
|
||||||
|
### Endpoints
|
||||||
|
|
||||||
|
| Endpoint | Path |
|
||||||
|
|-------------|------------------------|
|
||||||
|
| Index | `/debug/pprof/` |
|
||||||
|
| CPU profile | `/debug/pprof/profile` |
|
||||||
|
| Symbol | `/debug/pprof/symbol` |
|
||||||
|
| Trace | `/debug/pprof/trace` |
|
||||||
|
|
||||||
|
### Collecting a profile
|
||||||
|
|
||||||
|
To collect a profile, port-forward to the component's metrics endpoint
|
||||||
|
and collect the data from the [endpoint](#endpoints) of choice:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ kubectl port-forward -n <namespace> deploy/<component> 8080
|
||||||
|
$ curl -Sk -v http://localhost:8080/debug/pprof/heap > heap.out
|
||||||
|
```
|
||||||
|
|
||||||
|
The collected profile [can be analyzed using `go`](https://blog.golang.org/pprof),
|
||||||
|
or shared with one of the maintainers.
|
||||||
|
|
||||||
|
## Resource usage
|
||||||
|
|
||||||
|
As `kubectl top` gives a limited (and at times inaccurate) overview of
|
||||||
|
resource usage, it is often better to make use of the Grafana metrics
|
||||||
|
to gather insights. See [monitoring](../guides/monitoring.md) for a
|
||||||
|
guide on how to visualize this data with a Grafana dashboard.
|
||||||
@@ -150,6 +150,22 @@ The kustomize-controller creates `kustomization.yaml` files similar to:
|
|||||||
cd ./deploy/prod && kustomize create --autodetect --recursive
|
cd ./deploy/prod && kustomize create --autodetect --recursive
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### What is the behavior of Kustomize used by Flux
|
||||||
|
|
||||||
|
We referred to the Kustomization CLI flags here, so that you can replicate the same behavior using the CLI. The behavior of Kustomize used by the controller is currently configured as following:
|
||||||
|
|
||||||
|
- `--allow_id_changes` is set to false, so it does not change any resource IDs.
|
||||||
|
- `--enable_kyaml` is disabled by default, so it currently used `k8sdeps` to process YAMLs.
|
||||||
|
- `--enable_alpha_plugins` is disabled by default, so it uses only the built-in plugins.
|
||||||
|
- `--load_restrictor` is set to `LoadRestrictionsNone`, so it allows loading files outside the dir containing `kustomization.yaml`.
|
||||||
|
- `--reorder` resources is done in the `legacy` mode, so the output will have namespaces and cluster roles/role bindings first, CRDs before CRs, and webhooks last.
|
||||||
|
|
||||||
|
!!! hint "`kustomization.yaml` validation"
|
||||||
|
To validate changes before committing and/or merging, [a validation
|
||||||
|
utility script is available](https://github.com/fluxcd/flux2-kustomize-helm-example/blob/main/scripts/validate.sh),
|
||||||
|
it runs `kustomize` locally or in CI with the same set of flags as
|
||||||
|
the controller and validates the output using `kubeval`.
|
||||||
|
|
||||||
## Helm questions
|
## Helm questions
|
||||||
|
|
||||||
### How to debug "not ready" errors?
|
### How to debug "not ready" errors?
|
||||||
|
|||||||
771
docs/guides/flux-v1-automation-migration.md
Normal file
771
docs/guides/flux-v1-automation-migration.md
Normal file
@@ -0,0 +1,771 @@
|
|||||||
|
<!-- -*- fill-column: 100 -*- -->
|
||||||
|
# Migrating image update automation to Flux v2
|
||||||
|
|
||||||
|
"Image Update Automation" is a process in which Flux makes commits to your Git repository when it
|
||||||
|
detects that there is a new image to be used in a workload (e.g., a Deployment). In Flux v2 this
|
||||||
|
works quite differently to how it worked in Flux v1. This guide explains the differences and how to
|
||||||
|
port your cluster configuration from v1 to v2. There is also a [tutorial for using image update
|
||||||
|
automation with a new cluster][image-update-tute].
|
||||||
|
|
||||||
|
## Overview of changes between v1 and v2
|
||||||
|
|
||||||
|
In Flux v1, image update automation (from here, just "automation") was built into the Flux daemon,
|
||||||
|
which scanned everything it found in the cluster and updated the Git repository it was syncing.
|
||||||
|
|
||||||
|
In Flux v2,
|
||||||
|
|
||||||
|
- automation is controlled with custom resources, not annotations
|
||||||
|
- ordering images by build time is not supported (there is [a section
|
||||||
|
below](#how-to-migrate-annotations-to-image-policies) explaining what to do instead)
|
||||||
|
- the fields to update in files are marked explicitly, rather than inferred from annotations.
|
||||||
|
|
||||||
|
#### Automation is now controlled by custom resources
|
||||||
|
|
||||||
|
Flux v2 breaks down the functions in Flux v1's daemon into controllers, with each having a specific
|
||||||
|
area of concern. Automation is now done by two controllers: one which scans image repositories to
|
||||||
|
find the latest images, and one which uses that information to commit changes to git
|
||||||
|
repositories. These are in turn separate to the syncing controllers.
|
||||||
|
|
||||||
|
This means that automation in Flux v2 is governed by custom resources. In Flux v1 the daemon scanned
|
||||||
|
everything, and looked at annotations on the resources to determine what to update. Automation in v2
|
||||||
|
is more explicit than in v1 -- you have to mention exactly which images you want to be scanned, and
|
||||||
|
which fields you want to be updated.
|
||||||
|
|
||||||
|
A consequence of using custom resources is that with Flux v2 you can have an arbitrary number of
|
||||||
|
automations, targeting different Git repositories if you wish, and updating different sets of
|
||||||
|
images. If you run a multitenant cluster, the tenants can define automation in their own namespaces,
|
||||||
|
for their own Git repositories.
|
||||||
|
|
||||||
|
#### Selecting an image is more flexible
|
||||||
|
|
||||||
|
The ways in which you choose to select an image have changed. In Flux v1, you generally supply a
|
||||||
|
filter pattern, and the latest image is the image with the most recent build time out of those
|
||||||
|
filtered. In Flux v2, you choose an ordering, and separately specify a filter for the tags to
|
||||||
|
consider. These are dealt with in detail below.
|
||||||
|
|
||||||
|
Selecting an image by build time is no longer supported. This is the implicit default in Flux v1. In
|
||||||
|
Flux v2, you will need to tag images so that they sort in the order you would like -- [see
|
||||||
|
below](#how-to-use-sortable-image-tags) for how to do this conveniently.
|
||||||
|
|
||||||
|
#### Fields to update are explicitly marked
|
||||||
|
|
||||||
|
Lastly, in Flux v2 the fields to update in files are marked explicitly. In Flux v1 they are inferred
|
||||||
|
from the type of the resource, along with the annotations given. The approach in Flux v1 was limited
|
||||||
|
to the types that had been programmed in, whereas Flux v2 can update any Kubernetes object (and some
|
||||||
|
files that aren't Kubernetes objects, like `kustomization.yaml`).
|
||||||
|
|
||||||
|
## Preparing for migration
|
||||||
|
|
||||||
|
It is best to complete migration of your system to _Flux v2 syncing_ first, using the [Flux v1
|
||||||
|
migration guide][flux-v1-migration]. This will remove Flux v1 from the system, along with its image
|
||||||
|
automation. You can then reintroduce automation with Flux v2 by following the instructions in this
|
||||||
|
guide.
|
||||||
|
|
||||||
|
It is safe to leave the annotations for Flux v1 in files while you reintroduce automation, because
|
||||||
|
Flux v2 will ignore them.
|
||||||
|
|
||||||
|
To migrate to Flux v2 automation, you will need to do three things:
|
||||||
|
|
||||||
|
- make sure you are running the automation controllers; then,
|
||||||
|
- declare the automation with an `ImageUpdateAutomation` object; and,
|
||||||
|
- migrate each manifest by translate Flux v1 annotations to Flux v2 `ImageRepository` and
|
||||||
|
`ImagePolicy` objects, and putting update markers in the manifest file.
|
||||||
|
|
||||||
|
### Where to keep `ImageRepository`, `ImagePolicy` and `ImageUpdateAutomation` manifests
|
||||||
|
|
||||||
|
This guide assumes you want to manage automation itself via Flux. In the following sections,
|
||||||
|
manifests for the objects controlling automation are saved in files, committed to Git, and applied
|
||||||
|
in the cluster with Flux.
|
||||||
|
|
||||||
|
A Flux v2 installation will typically have a Git repository structured like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
<...>/flux-system/
|
||||||
|
gotk-components.yaml
|
||||||
|
gotk-sync.yaml
|
||||||
|
<...>/app/
|
||||||
|
# deployments etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
The `<...>` is the path to a particular cluster's definitions -- this may be simply `.`, or
|
||||||
|
something like `clusters/my-cluster`. To get the files in the right place, set a variable for this
|
||||||
|
path:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ CLUSTER_PATH=<...> # e.g., "." or "clusters/my-cluster", or ...
|
||||||
|
$ AUTO_PATH=$CLUSTER_PATH/automation
|
||||||
|
$ mkdir ./$AUTO_PATH
|
||||||
|
```
|
||||||
|
|
||||||
|
The file `$CLUSTER_PATH/flux-system/gotk-components.yaml` has definitions of all the Flux v2
|
||||||
|
controllers and custom resource definitions. The file `gotk-sync.yaml` defines a `GitRepository` and
|
||||||
|
a `Kustomization` which will sync manifests under `$CLUSTER_PATH/`.
|
||||||
|
|
||||||
|
To these will be added definitions for automation objects. This guide puts manifest files for
|
||||||
|
automation in `$CLUSTER_PATH/automation/`, but there is no particular structure required
|
||||||
|
by Flux. The automation objects do not have to be in the same namespace as the objects to be
|
||||||
|
updated.
|
||||||
|
|
||||||
|
#### Migration on a branch
|
||||||
|
|
||||||
|
This guide assumes you will commit changes to the branch that is synced by Flux, as this is the
|
||||||
|
simplest way to understand.
|
||||||
|
|
||||||
|
It may be less disruptive to put migration changes on a branch, then merging when you have completed
|
||||||
|
the migration. You would need to either change the `GitRepository` to point at the migration branch,
|
||||||
|
or have separate `GitRepository` and `Kustomization` objects for the migrated parts of your Git
|
||||||
|
repository. The main thing to avoid is syncing the same objects in two different places; e.g., avoid
|
||||||
|
having Kustomizations that sync both the unmigrated and migrated application configuration.
|
||||||
|
|
||||||
|
### Installing the command-line tool `flux`
|
||||||
|
|
||||||
|
The command-line tool `flux` will be used below; see [these instructions][install-cli] for how to
|
||||||
|
install it.
|
||||||
|
|
||||||
|
## Running the automation controllers
|
||||||
|
|
||||||
|
The first thing to do is to deploy the automation controllers to your cluster. The best way to
|
||||||
|
proceed will depend on the approach you took when following the [Flux read-only migration
|
||||||
|
guide][flux-v1-migration].
|
||||||
|
|
||||||
|
- If you used `flux bootstrap` to create a new Git repository, then ported your cluster
|
||||||
|
configuration to that repository, use [After `flux bootstrap`](#after-flux-bootstrap);
|
||||||
|
- If you used `flux install` to install the controllers directly, use [After migrating Flux v1 in
|
||||||
|
place](#after-migrating-flux-v1-in-place);
|
||||||
|
- If you used `flux install` and exported the configuration to a file, use [After committing Flux
|
||||||
|
v2 configuration to Git](#after-committing-a-flux-v2-configuration-to-git).
|
||||||
|
|
||||||
|
### After `flux bootstrap`
|
||||||
|
|
||||||
|
When starting from scratch, you are likely to have used `flux bootstrap`. Rerun the command, and
|
||||||
|
include the image automation controllers in your starting configuration with the flag
|
||||||
|
`--components-extra`, [as shown in the installation guide][flux-bootstrap].
|
||||||
|
|
||||||
|
This will commit changes to your Git repository and sync them in the cluster.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flux check --components-extra=image-reflector-controller,image-automation-controller
|
||||||
|
```
|
||||||
|
|
||||||
|
Now jump to the section [Migrating each manifest to Flux v2](#migrating-each-manifest-to-flux-v2).
|
||||||
|
|
||||||
|
### After migrating Flux v1 in place
|
||||||
|
|
||||||
|
If you followed the [Flux v1 migration guide][flux-v1-migration], you will already be running some
|
||||||
|
Flux v2 controllers. The automation controllers are currently considered an optional extra to those,
|
||||||
|
but are installed and run in much the same way. You may or may not have committed the Flux v2
|
||||||
|
configuration to your Git repository. If you did, go to the section [After committing Flux v2
|
||||||
|
configuration to Git](#after-committing-flux-v2-configuration-to-git).
|
||||||
|
|
||||||
|
If _not_, you will be installing directly to the cluster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux install --components-extra=image-reflector-controller,image-automation-controller
|
||||||
|
```
|
||||||
|
|
||||||
|
It is safe to repeat the installation command, or to run it after using `flux bootstrap`, so long as
|
||||||
|
you repeat any arguments you supplied the first time.
|
||||||
|
|
||||||
|
Now jump ahead to [Migrating each manifest to Flux v2](#migrating-each-manifest-to-flux-v2).
|
||||||
|
|
||||||
|
#### After committing a Flux v2 configuration to Git
|
||||||
|
|
||||||
|
If you added the Flux v2 configuration to your git repository, assuming it's in the file
|
||||||
|
`$CLUSTER_PATH/flux-system/gotk-components.yaml` as used in the guide, use `flux install` and write
|
||||||
|
it back to that file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux install \
|
||||||
|
--components-extra=image-reflector-controller,image-automation-controller \
|
||||||
|
--export > "$CLUSTER_PATH/flux-system/gotk-components.yaml"
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit changes to the `$CLUSTER_PATH/flux-system/gotk-components.yaml` file and sync the cluster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git add $CLUSTER_PATH/flux-system/gotk-components.yaml
|
||||||
|
$ git commit -s -m "Add image automation controllers to Flux config"
|
||||||
|
$ git push
|
||||||
|
$ flux reconcile kustomization --with-source flux-system
|
||||||
|
```
|
||||||
|
|
||||||
|
## Controlling automation with an `ImageUpdateAutomation` object
|
||||||
|
|
||||||
|
In Flux v1, automation was run by default. With Flux v2, you have to explicitly tell the controller
|
||||||
|
which Git repository to update and how to do so. These are defined in an `ImageUpdateAutomation`
|
||||||
|
object; but first, you need a `GitRepository` with write access, for the automation to use.
|
||||||
|
|
||||||
|
If you followed the [Flux v1 read-only migration guide][flux-v1-migration], you will have a
|
||||||
|
`GitRepository` defined in the namespace `flux-system`, for syncing to use. This `GitRepository`
|
||||||
|
will have _read_ access to the Git repository by default, and automation needs _write_ access to
|
||||||
|
push commits.
|
||||||
|
|
||||||
|
To give it write access, you can replace the secret it refers to. How to do this will depend on what
|
||||||
|
kind of authentication you used to install Flux v2.
|
||||||
|
|
||||||
|
### Replacing the Git credentials secret
|
||||||
|
|
||||||
|
The secret with Git credentials will be named in the `.spec.secretRef.name` field of the
|
||||||
|
`GitRepository` object. Say your `GitRepository` is in the _namespace_ `flux-system` and _named_
|
||||||
|
`flux-system` (these are the defaults if you used `flux bootstrap`); you can retrieve the secret
|
||||||
|
name and Git URL with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ FLUX_NS=flux-system
|
||||||
|
$ GIT_NAME=flux-system
|
||||||
|
$ SECRET_NAME=$(kubectl -n $FLUX_NS get gitrepository $GIT_NAME -o jsonpath={.spec.secretRef.name})
|
||||||
|
$ GIT_URL=$(kubectl -n $FLUX_NS get gitrepository $GIT_NAME -o jsonpath='{.spec.url}')
|
||||||
|
$ echo $SECRET_NAME $GIT_URL # make sure they have values
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're not sure which kind of credentials you're using, look at the secret:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ kubectl -n $FLUX_NS describe secret $SECRET_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
An entry at `.data.identity` indicates that you are using an SSH key (the [first
|
||||||
|
section](#replacing-an-ssh-key-secret) below); an entry at `.data.username` indicates you are using
|
||||||
|
a username and password or token (the [second section](#replacing-a-usernamepassword-secret)
|
||||||
|
below).
|
||||||
|
|
||||||
|
#### Replacing an SSH key secret
|
||||||
|
|
||||||
|
When using an SSH (deploy) key, create a new key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux create secret git -n $FLUX_NS $SECRET_NAME --url=$GIT_URL
|
||||||
|
```
|
||||||
|
|
||||||
|
You will need to copy the public key that's printed out, and install that as a deploy key for your
|
||||||
|
Git repo **making sure to check the 'All write access' box** (or otherwise give the key write
|
||||||
|
permissions). Remove the old deploy key.
|
||||||
|
|
||||||
|
#### Replacing a username/password secret
|
||||||
|
|
||||||
|
When you're using a username and password to authenticate, you may be able to change the permissions
|
||||||
|
associated with that account.
|
||||||
|
|
||||||
|
If not, you will need to create a new access token (e.g., ["Personal Access Token"][github-pat] in
|
||||||
|
GitHub). In this case, once you have the new token you can replace the secret with the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux create secret git -n $FLUX_NS $SECRET_NAME \
|
||||||
|
--username <username> --password <token> --url $GIT_URL
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Checking the new credentials
|
||||||
|
|
||||||
|
To check if your replaced credentials still work, try syncing the `GitRepository` object:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux reconcile source git -n $FLUX_NS $GIT_NAME
|
||||||
|
► annotating GitRepository flux-system in flux-system namespace
|
||||||
|
✔ GitRepository annotated
|
||||||
|
◎ waiting for GitRepository reconciliation
|
||||||
|
✔ GitRepository reconciliation completed
|
||||||
|
✔ fetched revision main/d537304e8f5f41f1584ca1e807df5b5752b2577e
|
||||||
|
```
|
||||||
|
|
||||||
|
When this is successful, it tells you the new credentials have at least read access.
|
||||||
|
|
||||||
|
### Making an automation object
|
||||||
|
|
||||||
|
To set automation running, you create an [`ImageUpdateAutomation`][auto-ref] object. Each object
|
||||||
|
will update a Git repository, according to the image policies in the namespace.
|
||||||
|
|
||||||
|
Here is an `ImageUpdateAutomation` manifest for the example (note: you will have to supply your own
|
||||||
|
value for at least the host part of the email address):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
$ # the environment variables $AUTO_PATH and $GIT_NAME are set above
|
||||||
|
$ FLUXBOT_EMAIL=fluxbot@example.com # supply your own host or address here
|
||||||
|
$ flux create image update my-app-auto \
|
||||||
|
--author-name FluxBot --author-email "$FLUXBOT_EMAIL" \
|
||||||
|
--git-repo-ref $GIT_NAME --branch main \
|
||||||
|
--interval 5m \
|
||||||
|
--export > ./$AUTO_PATH/my-app-auto.yaml
|
||||||
|
$ cat my-app-auto.yaml
|
||||||
|
---
|
||||||
|
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||||
|
kind: ImageUpdateAutomation
|
||||||
|
metadata:
|
||||||
|
name: my-app-auto
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
checkout:
|
||||||
|
branch: main
|
||||||
|
gitRepositoryRef:
|
||||||
|
name: flux-system
|
||||||
|
commit:
|
||||||
|
authorEmail: fluxbot@example.com
|
||||||
|
authorName: FluxBot
|
||||||
|
interval: 5m0s
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Commit and check that the automation object works
|
||||||
|
|
||||||
|
Commit the manifeat file and push:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git add ./$AUTO_PATH/my-app-auto.yaml
|
||||||
|
$ git commit -s -m "Add image update automation"
|
||||||
|
$ git push
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Then sync and check the object status:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux reconcile kustomization --with-source flux-system
|
||||||
|
► annotating GitRepository flux-system in flux-system namespace
|
||||||
|
✔ GitRepository annotated
|
||||||
|
◎ waiting for GitRepository reconciliation
|
||||||
|
✔ GitRepository reconciliation completed
|
||||||
|
✔ fetched revision main/401dd3b550f82581c7d12bb79ade389089c6422f
|
||||||
|
► annotating Kustomization flux-system in flux-system namespace
|
||||||
|
✔ Kustomization annotated
|
||||||
|
◎ waiting for Kustomization reconciliation
|
||||||
|
✔ Kustomization reconciliation completed
|
||||||
|
✔ reconciled revision main/401dd3b550f82581c7d12bb79ade389089c6422f
|
||||||
|
$ flux get image update
|
||||||
|
NAME READY MESSAGE LAST RUN SUSPENDED
|
||||||
|
my-app-auto True no updates made 2021-02-08T14:53:43Z False
|
||||||
|
```
|
||||||
|
|
||||||
|
Read on to the next section to see how to change each manifest file to work with Flux v2.
|
||||||
|
|
||||||
|
## Migrating each manifest to Flux v2
|
||||||
|
|
||||||
|
In Flux v1, the annotation
|
||||||
|
|
||||||
|
fluxcd.io/automated: "true"
|
||||||
|
|
||||||
|
switches automation on for a manifest (a description of a Kubernetes object). For each manifest that
|
||||||
|
has that annotation, you will need to create custom resources to scan for the latest image, and to
|
||||||
|
replace the annotations with field markers.
|
||||||
|
|
||||||
|
The following sections explain these steps, using this example Deployment manifest which is
|
||||||
|
initially annotated to work with Flux v1:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-app
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
fluxcd.io/automated: "true"
|
||||||
|
fluxcd.io/tag.app: semver:^5.0
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: podinfo
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: podinfo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: app
|
||||||
|
image: ghcr.io/stefanprodan/podinfo:5.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
A YAML file may have more than one manifest in it, separated with
|
||||||
|
`---`. Be careful to account for each manifest in a file.
|
||||||
|
|
||||||
|
You may wish to try migrating the automation of just one file or manifest and follow it through to
|
||||||
|
the end of the guide, before returning here to do the remainder.
|
||||||
|
|
||||||
|
### How to migrate annotations to image policies
|
||||||
|
|
||||||
|
For each image repository that is the subject of automation you will need to create an
|
||||||
|
`ImageRepository` object, so that the image repository is scanned for tags. The image repository in
|
||||||
|
the example deployment is `ghcr.io/stefanprodan/podinfo`, which is the image reference minus its
|
||||||
|
tag:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
$ cat $CLUSTER_PATH/app/my-app.yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-app
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
fluxcd.io/automated: "true"
|
||||||
|
fluxcd.io/tag.app: semver:^5.0
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: podinfo
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: podinfo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: app
|
||||||
|
image: ghcr.io/stefanprodan/podinfo:5.0.0 # <-- image reference
|
||||||
|
```
|
||||||
|
|
||||||
|
The command-line tool `flux` will help create a manifest for you. Note that the output is redirected
|
||||||
|
to a file under `$AUTO_PATH`, so it can be added to the Git repository and synced to the cluster.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ # the environment variable $AUTO_PATH was set earlier
|
||||||
|
$ flux create image repository podinfo-image \
|
||||||
|
--image ghcr.io/stefanprodan/podinfo \
|
||||||
|
--interval 5m \
|
||||||
|
--export > ./$AUTO_PATH/podinfo-image.yaml
|
||||||
|
$ cat ./$AUTO_PATH/podinfo-image.yaml
|
||||||
|
---
|
||||||
|
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||||
|
kind: ImageRepository
|
||||||
|
metadata:
|
||||||
|
name: podinfo-image
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
image: ghcr.io/stefanprodan/podinfo
|
||||||
|
interval: 5m0s
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! hint
|
||||||
|
If you are using the same image repository in several manifests, you only need one
|
||||||
|
`ImageRepository` object for it.
|
||||||
|
|
||||||
|
##### Using image registry credentials for scanning
|
||||||
|
|
||||||
|
When your image repositories are private, you supply Kubernetes with "image pull secrets" with
|
||||||
|
credentials for accessing the image registry (e.g., DockerHub). The image reflector controller needs
|
||||||
|
the same kind of credentials to scan image repositories.
|
||||||
|
|
||||||
|
There are several ways that image pull secrets can be made available for the image reflector
|
||||||
|
controller. The [image update tutorial][image-update-tute-creds] describes how to create or arrange
|
||||||
|
secrets for scanning to use. Also see later in the tutorial for [instructions specific to some cloud
|
||||||
|
platforms][image-update-tute-clouds].
|
||||||
|
|
||||||
|
##### Committing and checking the ImageRepository
|
||||||
|
|
||||||
|
Add the `ImageRepository` manifest to the Git index and commit it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git add ./$AUTO_PATH/podinfo-image.yaml
|
||||||
|
$ git commit -s -m "Add image repository object for podinfo"
|
||||||
|
$ git push
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can sync the new commit, and check that the object is working:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux reocncile kustomization --with-source flux-system
|
||||||
|
► annotating GitRepository flux-system in flux-system namespace
|
||||||
|
✔ GitRepository annotated
|
||||||
|
◎ waiting for GitRepository reconciliation
|
||||||
|
✔ GitRepository reconciliation completed
|
||||||
|
✔ fetched revision main/fd2fe8a61d4537bcfa349e4d1dbc480ea699ba8a
|
||||||
|
► annotating Kustomization flux-system in flux-system namespace
|
||||||
|
✔ Kustomization annotated
|
||||||
|
◎ waiting for Kustomization reconciliation
|
||||||
|
✔ Kustomization reconciliation completed
|
||||||
|
✔ reconciled revision main/fd2fe8a61d4537bcfa349e4d1dbc480ea699ba8a
|
||||||
|
$ flux get image repository podinfo-image
|
||||||
|
NAME READY MESSAGE LAST SCAN SUSPENDED
|
||||||
|
podinfo-image True successful scan, found 16 tags 2021-02-08T14:31:38Z False
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Replacing automation annotations
|
||||||
|
|
||||||
|
For each _field_ that's being updated by automation, you'll need an `ImagePolicy` object to describe
|
||||||
|
how to select an image for the field value. In the example, the field `.image` in the container
|
||||||
|
named `"app"` is the field being updated.
|
||||||
|
|
||||||
|
In Flux v1, annotations describe how to select the image to update to, using a prefix. In the
|
||||||
|
example, the prefix is `semver:`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
annotations:
|
||||||
|
fluxcd.io/automated: "true"
|
||||||
|
fluxcd.io/tag.app: semver:^5.0
|
||||||
|
```
|
||||||
|
|
||||||
|
These are the prefixes supported in Flux v1, and what to use in Flux v2:
|
||||||
|
|
||||||
|
| Flux v1 prefix | Meaning | Flux v2 equivalent |
|
||||||
|
|----------------|---------|--------------------|
|
||||||
|
| `glob:` | Filter for tags matching the glob pattern, then select the newest by build time | [Use sortable tags](#how-to-use-sortable-image-tags) |
|
||||||
|
| `regex:` | Filter for tags matching the regular expression, then select the newest by build time |[Use sortable tags](#how-to-use-sortable-image-tags) |
|
||||||
|
| `semver:` | Filter for tags that represent versions, and select the highest version in the given range | [Use semver ordering](#how-to-use-semver-image-tags) |
|
||||||
|
|
||||||
|
#### How to use sortable image tags
|
||||||
|
|
||||||
|
To give image tags a useful ordering, you can use a timestamp or serial number as part of each
|
||||||
|
image's tag, then sort either alphabetically or numerically.
|
||||||
|
|
||||||
|
This is a change from Flux v1, in which the build time was fetched from each image's config, and
|
||||||
|
didn't need to be included in the image tag. Therefore, this is likely to require a change to your
|
||||||
|
build process.
|
||||||
|
|
||||||
|
The guide [How to make sortable image tags][image-tags-guide] explains how to change your build
|
||||||
|
process to tag images with a timestamp. This will mean Flux v2 can sort the tags to find the most
|
||||||
|
recently built image.
|
||||||
|
|
||||||
|
##### Filtering the tags in an `ImagePolicy`
|
||||||
|
|
||||||
|
The recommended format for image tags using a timestamp is:
|
||||||
|
|
||||||
|
<branch>-<sha1>-<timestamp>
|
||||||
|
|
||||||
|
The timestamp (or serial number) is the part of the tag that you want to order on. The SHA1 is there
|
||||||
|
so you can trace an image back to the commit from which it was built. You don't need the branch for
|
||||||
|
sorting, but you may want to include only builds from a specific branch.
|
||||||
|
|
||||||
|
Say you want to filter for only images that are from `main` branch, and pick the most recent. Your
|
||||||
|
`ImagePolicy` would look like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||||
|
kind: ImagePolicy
|
||||||
|
metadata:
|
||||||
|
name: my-app-policy
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
imageRepositoryRef:
|
||||||
|
name: podinfo-image
|
||||||
|
filterTags:
|
||||||
|
pattern: '^main-[a-f0-9]+-(?P<ts>[0-9]+)'
|
||||||
|
extract: '$ts'
|
||||||
|
policy:
|
||||||
|
numerical:
|
||||||
|
order: asc
|
||||||
|
```
|
||||||
|
|
||||||
|
The `.spec.pattern` field gives a regular expression that a tag must match to be included. The
|
||||||
|
`.spec.extract` field gives a replacement pattern that can refer back to capture groups in the
|
||||||
|
filter pattern. The extracted values are sorted to find the selected image tag. In this case, the
|
||||||
|
timestamp part of the tag will be extracted and sorted numerically in ascending order. See [the
|
||||||
|
reference docs][imagepolicy-ref] for more examples.
|
||||||
|
|
||||||
|
Once you have made sure you have image tags and an `ImagePolicy` that works, jump ahead to [Checking
|
||||||
|
the ImagePolicy works](#checking-that-the-image-policy-works).
|
||||||
|
|
||||||
|
### How to use SemVer image tags
|
||||||
|
|
||||||
|
The other kind of sorting is by [SemVer][semver], picking the highest version from among those
|
||||||
|
included by the filter. A semver range will also filter for tags that fit in the range. For example,
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
semver:
|
||||||
|
range: ^5.0
|
||||||
|
```
|
||||||
|
|
||||||
|
includes only tags that have a major version of `5`, and selects whichever is the highest.
|
||||||
|
|
||||||
|
This can be combined with a regular expression pattern, to filter on other parts of the tags. For
|
||||||
|
example, you might put a target environment as well as the version in your image tags, like
|
||||||
|
`dev-v1.0.3`.
|
||||||
|
|
||||||
|
Then you would use an `ImagePolicy` similar to this one:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||||
|
kind: ImagePolicy
|
||||||
|
metadata:
|
||||||
|
name: my-app-policy
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
imageRepositoryRef:
|
||||||
|
name: podinfo-image
|
||||||
|
filterTags:
|
||||||
|
pattern: '^dev-v(?P<version>.*)'
|
||||||
|
extract: '$version'
|
||||||
|
policy:
|
||||||
|
semver:
|
||||||
|
range: '^1.0'
|
||||||
|
```
|
||||||
|
|
||||||
|
Continue on to the next sections to see an example, and how to check that your `ImagePolicy` works.
|
||||||
|
|
||||||
|
#### An `ImagePolicy` for the example
|
||||||
|
|
||||||
|
The example Deployment has annotations using `semver:` as a prefix, so the policy object also uses
|
||||||
|
semver:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ # the environment variable $AUTO_PATH was set earlier
|
||||||
|
$ flux create image policy my-app-policy \
|
||||||
|
--image-ref podinfo-image \
|
||||||
|
--semver '^5.0' \
|
||||||
|
--export > ./$AUTO_PATH/my-app-policy.yaml
|
||||||
|
$ cat ./$AUTO_PATH/my-app-policy.yaml
|
||||||
|
---
|
||||||
|
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||||
|
kind: ImagePolicy
|
||||||
|
metadata:
|
||||||
|
name: my-app-policy
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
imageRepositoryRef:
|
||||||
|
name: podinfo-image
|
||||||
|
policy:
|
||||||
|
semver:
|
||||||
|
range: ^5.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Checking that the `ImagePolicy` works
|
||||||
|
|
||||||
|
Commit the manifest file, and push:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git add ./$AUTO_PATH/my-app-policy.yaml
|
||||||
|
$ git commit -s -m "Add image policy for my-app"
|
||||||
|
$ git push
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can reconcile and check that the image policy works:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux reconcile kustomization --with-source flux-system
|
||||||
|
► annotating GitRepository flux-system in flux-system namespace
|
||||||
|
✔ GitRepository annotated
|
||||||
|
◎ waiting for GitRepository reconciliation
|
||||||
|
✔ GitRepository reconciliation completed
|
||||||
|
✔ fetched revision main/7dcf50222499be8c97e22cd37e26bbcda8f70b95
|
||||||
|
► annotating Kustomization flux-system in flux-system namespace
|
||||||
|
✔ Kustomization annotated
|
||||||
|
◎ waiting for Kustomization reconciliation
|
||||||
|
✔ Kustomization reconciliation completed
|
||||||
|
✔ reconciled revision main/7dcf50222499be8c97e22cd37e26bbcda8f70b95
|
||||||
|
$ flux get image policy flux-system
|
||||||
|
NAME READY MESSAGE LATEST IMAGE
|
||||||
|
my-app-policy True Latest image tag for 'ghcr.io/stefanprodan/podinfo' resolved to: 5.1.4 ghcr.io/stefanprodan/podinfo:5.1.4
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to mark up files for update
|
||||||
|
|
||||||
|
The last thing to do in each manifest is to mark the fields that you want to be updated.
|
||||||
|
|
||||||
|
In Flux v1, the annotations in a manifest determines the fields to be updated. In the example, the
|
||||||
|
annotations target the image used by the container `app`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-app
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
fluxcd.io/automated: "true"
|
||||||
|
fluxcd.io/tag.app: semver:^5.0 # <-- `.app` here
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: podinfo
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: podinfo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: app # <-- targets `app` here
|
||||||
|
image: ghcr.io/stefanprodan/podinfo:5.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
This works straight-forwardly for Deployment manifests, but when it comes to `HelmRelease`
|
||||||
|
manifests, it [gets complicated][helm-auto], and it doesn't work at all for many kinds of resources.
|
||||||
|
|
||||||
|
For Flux v2, you mark the field you want to be updated directly, with the namespaced name of the
|
||||||
|
image policy to apply. This is the example Deployment, marked up for Flux v2:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: default
|
||||||
|
name: my-app
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: podinfo
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: podinfo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: app
|
||||||
|
image: ghcr.io/stefanprodan/podinfo:5.0.0 # {"$imagepolicy": "flux-system:my-app-policy"}
|
||||||
|
```
|
||||||
|
|
||||||
|
The value `flux-system:my-app-policy` names the policy that selects the desired image.
|
||||||
|
|
||||||
|
This works in the same way for `DaemonSet` and `CronJob` manifests. For `HelmRelease` manifests, put
|
||||||
|
the marker alongside the part of the `values` that has the image tag. If the image tag is a separate
|
||||||
|
field, you can put `:tag` on the end of the name, to replace the value with just the selected
|
||||||
|
image's tag. The [image automation guide][image-update-tute-custom] has examples for `HelmRelease`
|
||||||
|
and other custom resources.
|
||||||
|
|
||||||
|
### Committing the marker change and checking that automation works
|
||||||
|
|
||||||
|
Referring to the image policy created earlier, you can see the example Deployment does not use the
|
||||||
|
most recent image. When you commit the manifest file with the update marker added, you would expect
|
||||||
|
automation to update the file.
|
||||||
|
|
||||||
|
Commit the change that adds an update marker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git add app/my-app.yaml # the filename of the example
|
||||||
|
$ git commit -s -m "Add update marker to my-app manifest"
|
||||||
|
$ git push
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Now to check that the automation makes a change:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ flux reconcile image update my-app-auto
|
||||||
|
► annotating ImageUpdateAutomation my-app-auto in flux-system namespace
|
||||||
|
✔ ImageUpdateAutomation annotated
|
||||||
|
◎ waiting for ImageUpdateAutomation reconciliation
|
||||||
|
✔ ImageUpdateAutomation reconciliation completed
|
||||||
|
✔ committed and pushed a92a4b654f520c00cb6c46b2d5e4fb4861aa58fc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If a change was not pushed by the image automation, there's several things you can check:
|
||||||
|
|
||||||
|
- it's possible it made a change that is not reported in the latest status -- pull from the origin
|
||||||
|
and check the commit log
|
||||||
|
- check that the name used in the marker corresponds to the namespace and name of an `ImagePolicy`
|
||||||
|
- check that the `ImageUpdateAutomation` is in the same namespace as the `ImagePolicy` objects
|
||||||
|
named in markers
|
||||||
|
- check that the image policy and the image repository are both reported as `Ready`
|
||||||
|
- check that the credentials referenced by the `GitRepository` object have write permission, and
|
||||||
|
create new credentials if necessary.
|
||||||
|
|
||||||
|
As a fallback, you can scan the logs of the automation controller to see if it logged errors:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ kubectl logs -n flux-system deploy/image-automation-controller
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you are satisfied that it is working, you can migrate the rest of the manifests using the steps
|
||||||
|
from ["Migrating each manifest to Flux v2"](#migrating-each-manifest-to-flux-v2) above.
|
||||||
|
|
||||||
|
[image-update-tute]: https://toolkit.fluxcd.io/guides/image-update/
|
||||||
|
[imagepolicy-ref]: https://toolkit.fluxcd.io/components/image/imagepolicies/
|
||||||
|
[helm-auto]: https://docs.fluxcd.io/en/1.21.1/references/helm-operator-integration/#automated-image-detection
|
||||||
|
[image-update-tute-custom]: https://toolkit.fluxcd.io/guides/image-update/#configure-image-update-for-custom-resources
|
||||||
|
[flux-v1-migration]: https://toolkit.fluxcd.io/guides/flux-v1-migration/
|
||||||
|
[install-cli]: https://toolkit.fluxcd.io/get-started/#install-the-flux-cli
|
||||||
|
[flux-bootstrap]: https://toolkit.fluxcd.io/guides/installation/#bootstrap
|
||||||
|
[github-pat]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token
|
||||||
|
[auto-object-ref]: https://toolkit.fluxcd.io/components/image/imageupdateautomations/
|
||||||
|
[image-update-tute-creds]: https://toolkit.fluxcd.io/guides/image-update/#configure-image-scanning
|
||||||
|
[image-update-tute-clouds]: https://toolkit.fluxcd.io/guides/image-update/#imagerepository-cloud-providers-authentication
|
||||||
|
[image-tags-guide]: https://toolkit.fluxcd.io/guides/sortable-image-tags/
|
||||||
|
[auto-ref]: https://toolkit.fluxcd.io/components/image/imageupdateautomations/
|
||||||
|
[semver]: https://semver.org
|
||||||
@@ -170,8 +170,7 @@ Create an `ImagePolicy` to tell Flux which semver range to use when filtering ta
|
|||||||
```sh
|
```sh
|
||||||
flux create image policy podinfo \
|
flux create image policy podinfo \
|
||||||
--image-ref=podinfo \
|
--image-ref=podinfo \
|
||||||
--interval=1m \
|
--select-semver=5.0.x \
|
||||||
--semver=5.0.x \
|
|
||||||
--export > ./clusters/my-cluster/podinfo-policy.yaml
|
--export > ./clusters/my-cluster/podinfo-policy.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -292,7 +291,7 @@ Tell Flux to pull and apply changes:
|
|||||||
flux reconcile kustomization flux-system --with-source
|
flux reconcile kustomization flux-system --with-source
|
||||||
```
|
```
|
||||||
|
|
||||||
In a couple of seconds Flux will push a commit to your repository with
|
In a couple of seconds, Flux will push a commit to your repository with
|
||||||
the latest image tag that matches the podinfo policy:
|
the latest image tag that matches the podinfo policy:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
@@ -429,6 +428,86 @@ LB and the generated URL `http://<LoadBalancerAddress>/<ReceiverURL>`.
|
|||||||
and any other system that supports webhooks e.g. GitHub Actions, Jenkins, CircleCI, etc.
|
and any other system that supports webhooks e.g. GitHub Actions, Jenkins, CircleCI, etc.
|
||||||
See the [Receiver CRD docs](../components/notification/receiver.md) for more details.
|
See the [Receiver CRD docs](../components/notification/receiver.md) for more details.
|
||||||
|
|
||||||
|
## Incident management
|
||||||
|
|
||||||
|
### Suspend automation
|
||||||
|
|
||||||
|
During an incident you may wish to stop Flux from pushing image updates to Git.
|
||||||
|
|
||||||
|
You can suspend the image automation directly in-cluster:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flux suspend image update flux-system
|
||||||
|
```
|
||||||
|
|
||||||
|
Or by editing the `ImageUpdateAutomation` manifest in Git:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
kind: ImageUpdateAutomation
|
||||||
|
metadata:
|
||||||
|
name: flux-system
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
suspend: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the incident is resolved, you can resume automation with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flux resume image update flux-system
|
||||||
|
```
|
||||||
|
|
||||||
|
If you wish to pause the automation for a particular image only,
|
||||||
|
you can suspend/resume the image scanning:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flux suspend image repository podinfo
|
||||||
|
```
|
||||||
|
|
||||||
|
### Revert image updates
|
||||||
|
|
||||||
|
Assuming you've configured Flux to update an app to its latest stable version:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flux create image policy podinfo \
|
||||||
|
--image-ref=podinfo \
|
||||||
|
--select-semver=">=5.0.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
If the latest version e.g. `5.0.1` causes an incident in production, you can tell Flux to
|
||||||
|
revert the image tag to a previous version e.g. `5.0.0` with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flux create image policy podinfo \
|
||||||
|
--image-ref=podinfo \
|
||||||
|
--select-semver=5.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Or by changing the semver range in Git:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
kind: ImagePolicy
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
policy:
|
||||||
|
semver:
|
||||||
|
range: 5.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Based on the above configuration, Flux will patch the podinfo deployment manifest in Git
|
||||||
|
and roll out `5.0.0` in-cluster.
|
||||||
|
|
||||||
|
When a new version is available e.g. `5.0.2`, you can update the policy once more
|
||||||
|
and tell Flux to consider only versions greater than `5.0.1`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flux create image policy podinfo \
|
||||||
|
--image-ref=podinfo \
|
||||||
|
--select-semver=">5.0.1"
|
||||||
|
```
|
||||||
|
|
||||||
## ImageRepository cloud providers authentication
|
## ImageRepository cloud providers authentication
|
||||||
|
|
||||||
If relying on a cloud provider image repository, you might need to do some extra
|
If relying on a cloud provider image repository, you might need to do some extra
|
||||||
@@ -578,7 +657,7 @@ spec:
|
|||||||
|
|
||||||
#### Using access token [short-lived]
|
#### Using access token [short-lived]
|
||||||
|
|
||||||
!!!note "Workload Identity"
|
!!! note "Workload Identity"
|
||||||
Please ensure that you enable workload identity for your cluster, create a GCP service account that has
|
Please ensure that you enable workload identity for your cluster, create a GCP service account that has
|
||||||
access to the container registry and create an IAM policy binding between the GCP service account and
|
access to the container registry and create an IAM policy binding between the GCP service account and
|
||||||
the Kubernetes service account so that the pods created by the cronjob can access GCP APIs and get the token.
|
the Kubernetes service account so that the pods created by the cronjob can access GCP APIs and get the token.
|
||||||
@@ -713,4 +792,87 @@ or [Sealed Secrets](sealed-secrets.md) , commit and push the encypted file to gi
|
|||||||
|
|
||||||
### Azure Container Registry
|
### Azure Container Registry
|
||||||
|
|
||||||
TODO
|
AKS clusters are not able to pull and run images from ACR by default.
|
||||||
|
Read [Integrating AKS /w ACR](https://docs.microsoft.com/en-us/azure/aks/cluster-container-registry-integration) as a potential pre-requisite
|
||||||
|
before integrating Flux `ImageRepositories` with ACR.
|
||||||
|
|
||||||
|
Note that the resulting ImagePullSecret for Flux could also be specified by Pods within the same Namespace to pull and run ACR images as well.
|
||||||
|
|
||||||
|
#### Generating Tokens for Managed Identities [short-lived]
|
||||||
|
|
||||||
|
With [AAD Pod-Identity](https://azure.github.io/aad-pod-identity/docs/), we can create Pods that have their own
|
||||||
|
cloud credentials for accessing Azure services like ACR.
|
||||||
|
|
||||||
|
Your cluster should have `--enable-managed-identity` configured.
|
||||||
|
This software can be [installed via Helm](https://azure.github.io/aad-pod-identity/docs/getting-started/installation/) not managed by Azure.
|
||||||
|
Use Flux's `HelmRepository` and `HelmRelease` object to manage the aad-pod-identity installation from a bootstrap repository.
|
||||||
|
|
||||||
|
!!! As an alternative to Helm, the `--enable-aad-pod-identity` flag for the `az aks create` is currently in Preview.
|
||||||
|
Follow the Azure guide for [Creating an AKS cluster with AAD Pod Identity](https://docs.microsoft.com/en-us/azure/aks/use-azure-ad-pod-identity) if you would like to enable this feature with the Azure CLI.
|
||||||
|
|
||||||
|
Once we have AAD Pod Identity installed, we can create a Deployment that frequently refreshes an image pull secret into
|
||||||
|
our desired Namespace.
|
||||||
|
|
||||||
|
Create a directory in your control repository and save this `kustomization.yaml`:
|
||||||
|
```yaml
|
||||||
|
# kustomization.yaml
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- https://github.com/fluxcd/flux2/archive/main.zip//manifests/integrations/registry-credentials-sync/azure
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- config-patches.yaml
|
||||||
|
```
|
||||||
|
Save and configure the following patch -- note the instructional comments for configuring matching Azure resources:
|
||||||
|
```yaml
|
||||||
|
# config-patches.yaml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
ACR_NAME: my-registry
|
||||||
|
KUBE_SECRET: my-registry # does not yet exist -- will be created in the same Namespace
|
||||||
|
SYNC_PERIOD: "3600" # ACR tokens expire every 3 hours; refresh faster than that
|
||||||
|
|
||||||
|
# Create an identity in Azure and assign it a role to pull from ACR (note: the identity's resourceGroup should match the desired ACR):
|
||||||
|
# az identity create -n acr-sync
|
||||||
|
# az role assignment create --role AcrPull --assignee-object-id "$(az identity show -n acr-sync -o tsv --query principalId)"
|
||||||
|
# Fetch the clientID and resourceID to configure the AzureIdentity spec below:
|
||||||
|
# az identity show -n acr-sync -otsv --query clientId
|
||||||
|
# az identity show -n acr-sync -otsv --query resourceId
|
||||||
|
---
|
||||||
|
apiVersion: aadpodidentity.k8s.io/v1
|
||||||
|
kind: AzureIdentity
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync # name must match the stub-resource in az-identity.yaml
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
clientID: 4ceaa448-d7b9-4a80-8f32-497eaf3d3287
|
||||||
|
resourceID: /subscriptions/8c69185e-55f9-4d00-8e71-a1b1bb1386a1/resourcegroups/stealthybox/providers/Microsoft.ManagedIdentity/userAssignedIdentities/acr-sync
|
||||||
|
type: 0 # user-managed identity
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify that `kustomize build .` works, then commit the directory to you control repo.
|
||||||
|
Flux will apply the Deployment and it will use the AAD managed identity for that Pod to regularly fetch ACR tokens into your configured `KUBE_SECRET` name.
|
||||||
|
Reference the `KUBE_SECRET` value from any `ImageRepository` objects for that ACR registry.
|
||||||
|
|
||||||
|
This example uses the `fluxcd/flux2` github archive as a remote base, but you may copy the [./manifests/integrations/registry-credentials-sync/azure](github.com/fluxcd/flux2/tree/main/manifests/integrations/registry-credentials-sync/azure)
|
||||||
|
folder into your own repository or use a git submodule to vendor it if preferred.
|
||||||
|
|
||||||
|
#### Using Static Credentials [long-lived]
|
||||||
|
|
||||||
|
!!! Using a static credential requires a Secrets management solution compatible with your GitOps workflow.
|
||||||
|
|
||||||
|
Follow the official Azure documentation for [Creating an Image Pull Secret for ACR](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-auth-kubernetes).
|
||||||
|
|
||||||
|
Instead of creating the Secret directly into your Kubernetes cluster, encrypt it using [Mozilla SOPS](mozilla-sops.md)
|
||||||
|
or [Sealed Secrets](sealed-secrets.md), then commit and push the encypted file to git.
|
||||||
|
|
||||||
|
This Secret should be in the same Namespace as your flux `ImageRepository` object.
|
||||||
|
Update the `ImageRepository.spec.secretRef` to point to it.
|
||||||
|
|
||||||
|
It is also possible to create [Repository Scoped Tokens](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-repository-scoped-permissions).
|
||||||
|
|
||||||
|
!!! Note that this feature is in preview and does have limitations.
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ flux bootstrap <GIT-PROVIDER> \
|
|||||||
If you wish to install a specific version, use the Flux
|
If you wish to install a specific version, use the Flux
|
||||||
[release tag](https://github.com/fluxcd/flux2/releases) e.g. `--version=v0.2.0`.
|
[release tag](https://github.com/fluxcd/flux2/releases) e.g. `--version=v0.2.0`.
|
||||||
|
|
||||||
|
If you wish to deploy the Flux components onto
|
||||||
|
[tainted Kubernetes nodes](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/),
|
||||||
|
you can specify the toleration keys with `--toleration-keys=node.kubernetes.io/dedicated-to-flux`.
|
||||||
|
|
||||||
With `--path` you can configure the directory which will be used to reconcile the target cluster.
|
With `--path` you can configure the directory which will be used to reconcile the target cluster.
|
||||||
To control multiple clusters from the same Git repository, you have to set a unique path per
|
To control multiple clusters from the same Git repository, you have to set a unique path per
|
||||||
cluster e.g. `clusters/staging` and `clusters/production`:
|
cluster e.g. `clusters/staging` and `clusters/production`:
|
||||||
@@ -310,15 +314,13 @@ If you don't specify the SSH algorithm, then `flux` will generate an RSA 2048 bi
|
|||||||
Azure DevOps requires a non-default Git implementation (`libgit2`) to be enabled, so that the Git v2 protocol is supported.
|
Azure DevOps requires a non-default Git implementation (`libgit2`) to be enabled, so that the Git v2 protocol is supported.
|
||||||
Note that this implementation does not support shallow cloning, and it is therefore advised to only resort to this option if a
|
Note that this implementation does not support shallow cloning, and it is therefore advised to only resort to this option if a
|
||||||
connection fails with the default configuration.
|
connection fails with the default configuration.
|
||||||
Additionally, the current implementation of image automation does not support Azure DevOps as has no Git implementation with
|
|
||||||
this protocol. This limitation will likely change in the future.
|
|
||||||
|
|
||||||
If you are using Azure DevOps you need to specify a different Git implementation than the default:
|
If you are using Azure DevOps you need to specify a different Git implementation than the default:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
flux create source git flux-system \
|
flux create source git flux-system \
|
||||||
--git-implementation=libgit2 \
|
--git-implementation=libgit2 \
|
||||||
--url=ssh://git@ssh.dev.azure.com/v3/org/project/repository \
|
--url=ssh://git@ssh.dev.azure.com/v3/<org>/<project>/<repository> \
|
||||||
--branch=master \
|
--branch=master \
|
||||||
--interval=1m
|
--interval=1m
|
||||||
```
|
```
|
||||||
@@ -328,6 +330,21 @@ If you don't specify the SSH algorithm, then `flux` will generate an RSA 2048 bi
|
|||||||
(e.g. `ssh.dev.azure.com:v3`).
|
(e.g. `ssh.dev.azure.com:v3`).
|
||||||
Use the [RFC 3986 compatible syntax](https://tools.ietf.org/html/rfc3986#section-3) instead: `ssh.dev.azure.com/v3`.
|
Use the [RFC 3986 compatible syntax](https://tools.ietf.org/html/rfc3986#section-3) instead: `ssh.dev.azure.com/v3`.
|
||||||
|
|
||||||
|
If you wish to use Git over HTTPS, then generated a personal access token and supply it as the password:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flux create source git flux-system \
|
||||||
|
--git-implementation=libgit2 \
|
||||||
|
--url=https://dev.azure.com/<org>/<project>/_git/<repository> \
|
||||||
|
--branch=master \
|
||||||
|
--username=git \
|
||||||
|
--password=token \
|
||||||
|
--interval=1m
|
||||||
|
```
|
||||||
|
|
||||||
|
Please consult the [Azure DevOps documentation](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page)
|
||||||
|
on how to generate personal access tokens for Git repositories.
|
||||||
|
|
||||||
If your Git server supports basic auth, you can set the URL to HTTPS and specify the credentials with:
|
If your Git server supports basic auth, you can set the URL to HTTPS and specify the credentials with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -591,11 +608,28 @@ kustomize build https://github.com/fluxcd/flux2/manifests/install?ref=main | kub
|
|||||||
|
|
||||||
## Uninstall
|
## Uninstall
|
||||||
|
|
||||||
You can uninstall the Flux components with:
|
You can uninstall Flux with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
flux uninstall --crds
|
flux uninstall --namespace=flux-system
|
||||||
```
|
```
|
||||||
|
|
||||||
The above command will delete the custom resources definitions, the
|
The above command performs the following operations:
|
||||||
controllers, and the namespace where they were installed.
|
|
||||||
|
- deletes Flux components (deployments and services)
|
||||||
|
- deletes Flux network policies
|
||||||
|
- deletes Flux RBAC (service accounts, cluster roles and cluster role bindings)
|
||||||
|
- removes the Kubernetes finalizers from Flux custom resources
|
||||||
|
- deletes Flux custom resource definitions and custom resources
|
||||||
|
- deletes the namespace where Flux was installed
|
||||||
|
|
||||||
|
If you've installed Flux in a namespace that you wish to preserve, you
|
||||||
|
can skip the namespace deletion with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
flux uninstall --namespace=infra --keep-namespace
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! hint
|
||||||
|
Note that the `uninstall` command will not remove any Kubernetes objects
|
||||||
|
or Helm releases that were reconciled on the cluster by Flux.
|
||||||
|
|||||||
157
docs/guides/sortable-image-tags.md
Normal file
157
docs/guides/sortable-image-tags.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<!-- -*- fill-column: 100 -*- -->
|
||||||
|
# How to make sortable image tags to use with automation
|
||||||
|
|
||||||
|
Flux v2 does not support selecting the lastest image by build time. Obtaining the build time needs
|
||||||
|
the container config for each image, and fetching that is subject to strict rate limiting by image
|
||||||
|
registries (e.g., by [DockerHub][dockerhub-rates]).
|
||||||
|
|
||||||
|
This guide explains how to construct image tags so that the most recent image has the tag that comes
|
||||||
|
last in alphabetical or numerical order. The technique suggested is to put a timestamp or serial
|
||||||
|
number in each image tag.
|
||||||
|
|
||||||
|
## Formats and alternatives
|
||||||
|
|
||||||
|
The important properties for sorting are that the parts of the timestamp go from most significant to
|
||||||
|
least (e.g., the year down to the second). For numbers it is best to use numerical order, since this
|
||||||
|
will work with values of different width (e.g., '12' sorts after '2').
|
||||||
|
|
||||||
|
Image tags are often shown in user interfaces, so readability matters. Here is an example of a
|
||||||
|
readable timestamp that will sort well:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ # date and time (remember ':' is not allowed in a tag)
|
||||||
|
$ date +%F.%H%M%S
|
||||||
|
2021-01-28.133158
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use a timestamp that sorts as a number, like [Unix
|
||||||
|
time](https://en.wikipedia.org/wiki/Unix_time):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ # seconds since Jan 1 1970
|
||||||
|
$ date +%s
|
||||||
|
1611840548
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can use a serial number as part of the tag. Some CI platforms will provide a
|
||||||
|
build number in an environment variable, but that may not be reliable to use as a serial number --
|
||||||
|
check the platform documentation.
|
||||||
|
|
||||||
|
A commit count can be a reasonable stand-in for a serial number, if you build an image per commit
|
||||||
|
and you don't rewrite the branch in question:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ # commits in branch
|
||||||
|
$ git --rev-list --count HEAD
|
||||||
|
1504
|
||||||
|
```
|
||||||
|
|
||||||
|
Beware: this will not give a useful number if you have a shallow clone.
|
||||||
|
|
||||||
|
### Other things to include in the image tag
|
||||||
|
|
||||||
|
It is also handy to quickly trace an image to the branch and commit of its source code. Including
|
||||||
|
the branch also means you can filter for images from a particular branch.
|
||||||
|
|
||||||
|
A useful tag format is
|
||||||
|
|
||||||
|
<branch>-<sha1>-<timestamp>
|
||||||
|
|
||||||
|
The branch and tag will usually be made available in a CI platform as environment variables. See
|
||||||
|
|
||||||
|
- [CircleCI's built-in variables `CIRCLE_BRANCH` and `CIRCLE_SHA1`][circle-ci-env]
|
||||||
|
- [GitHub Actions' `GITHUB_REF` and `GITHUB_SHA`][github-actions-env]
|
||||||
|
- [Travis CI's `TRAVIS_BRANCH` and `TRAVIS_COMMIT`][travis-env].
|
||||||
|
|
||||||
|
## Example of a build process with timestamp tagging
|
||||||
|
|
||||||
|
Here is an example of a [GitHub Actions job][gha-syntax] that creates a "build ID" with the git
|
||||||
|
branch, SHA1, and a timestamp, and uses it as a tag when building an image:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build-push:
|
||||||
|
env:
|
||||||
|
IMAGE: org/my-app
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Generate build ID
|
||||||
|
id: prep
|
||||||
|
run: |
|
||||||
|
branch=${GITHUB_REF##*/}
|
||||||
|
sha=${GITHUB_SHA::8}
|
||||||
|
ts=$(date +%s)
|
||||||
|
echo "::set-output name=BUILD_ID::${branch}-${sha}-${ts}"
|
||||||
|
|
||||||
|
# These are prerequisites for the docker build step
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and publish container image with tag
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE }}:${{ steps.prep.outputs.BUILD_ID }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using in an `ImagePolicy` object
|
||||||
|
|
||||||
|
When creating an `ImagePolicy` object, you will need to extract just the timestamp part of the tag,
|
||||||
|
using the `tagFilter` field. You can filter for a particular branch to restrict images to only those
|
||||||
|
built from that branch.
|
||||||
|
|
||||||
|
Here is an example that filters for only images built from `main` branch, and selects the most
|
||||||
|
recent according the timestamp (created with `date +%s`):
|
||||||
|
|
||||||
|
```
|
||||||
|
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||||
|
kind: ImagePolicy
|
||||||
|
metadata:
|
||||||
|
name: image-repo-policy
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
imageRepositoryRef:
|
||||||
|
name: image-repo
|
||||||
|
filterTags:
|
||||||
|
pattern: '^main-[a-f0-9]+-(?P<ts>[0-9]+)'
|
||||||
|
extract: '$ts'
|
||||||
|
policy:
|
||||||
|
numerical:
|
||||||
|
order: asc
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't care about the branch, that part can be a wildcard in the pattern:
|
||||||
|
|
||||||
|
```
|
||||||
|
apiVersion: image.toolkit.fluxcd.io/v1alpha1
|
||||||
|
kind: ImagePolicy
|
||||||
|
metadata:
|
||||||
|
name: image-repo-policy
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
imageRepositoryRef:
|
||||||
|
name: image-repo
|
||||||
|
filterTags:
|
||||||
|
pattern: '^.+-[a-f0-9]+-(?P<ts>[0-9]+)'
|
||||||
|
extract: '$ts'
|
||||||
|
policy:
|
||||||
|
numerical:
|
||||||
|
order: asc
|
||||||
|
```
|
||||||
|
|
||||||
|
[circle-ci-env]: https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables
|
||||||
|
[github-actions-env]: https://docs.github.com/en/actions/reference/environment-variables#default-environment-variables
|
||||||
|
[travis-env]: https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
|
||||||
|
[dockerhub-rates]: https://docs.docker.com/docker-hub/billing/faq/#pull-rate-limiting-faqs
|
||||||
|
[gha-syntax]: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
|
||||||
@@ -68,26 +68,34 @@ Features:
|
|||||||
- Dependency management (infrastructure and workloads)
|
- Dependency management (infrastructure and workloads)
|
||||||
- Alerting to external systems (webhook senders)
|
- Alerting to external systems (webhook senders)
|
||||||
- External events handling (webhook receivers)
|
- External events handling (webhook receivers)
|
||||||
- Configuration update automation (automated patching)
|
- Automated container image updates to Git (image scanning and patching)
|
||||||
- Policy-driven validation (OPA, admission controllers)
|
- Policy-driven validation (OPA, Kyverno, admission controllers)
|
||||||
- Seamless integration with Git providers (GitHub, GitLab, Bitbucket)
|
- Seamless integration with Git providers (GitHub, GitLab, Bitbucket)
|
||||||
- Interoperability with workflow providers (GitHub Actions, Tekton, Argo)
|
- Interoperability with workflow providers (GitHub Actions, Tekton, Argo)
|
||||||
- Interoperability with Cluster API (CAPI) providers
|
- Interoperability with Cluster API (CAPI) providers
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
The Flux project is always looking for new contributors and there are a multitude of ways to get involved.
|
Need help or want to contribute? Please see the links below. The Flux project is always looking for
|
||||||
Depending on what you want to do, some of the following bits might be your first steps:
|
new contributors and there are a multitude of ways to get involved.
|
||||||
|
|
||||||
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
|
- Getting Started?
|
||||||
- Ask questions and add suggestions in our [GitOps Toolkit Discussions](https://github.com/fluxcd/toolkit/discussions)
|
- Look at our [Get Started guide](https://toolkit.fluxcd.io/get-started/) and give us feedback
|
||||||
- Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
- Need help?
|
||||||
- Join the [planning discussions](https://github.com/fluxcd/flux2/discussions)
|
- First: Ask questions on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||||
- And if you are completely new to Flux v2 and the GitOps Toolkit, take a look at our [Get Started guide](get-started/index.md) and give us feedback
|
- Second: Talk to us in the #flux channel on [CNCF Slack](https://slack.cncf.io/)
|
||||||
- Check out [how to contribute](contributing/index.md) to the project
|
- Please follow our [Support Guidelines](https://fluxcd.io/support/)
|
||||||
|
(in short: be nice, be respectful of volunteers' time, understand that maintainers and
|
||||||
|
contributors cannot respond to all DMs, and keep discussions in the public #flux channel as much as possible).
|
||||||
|
- Have feature proposals or want to contribute?
|
||||||
|
- Propose features on our [GH Discussions page](https://github.com/fluxcd/flux2/discussions)
|
||||||
|
- Join our upcoming dev meetings ([meeting access and agenda](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARDeh6b70B0/view))
|
||||||
|
- [Join the flux-dev mailing list](https://lists.cncf.io/g/cncf-flux-dev).
|
||||||
|
- Check out [how to contribute](contributing/index.md) to the project
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
Check out our **[events calendar](https://fluxcd.io/community/#talks)**, both with upcoming talks you can attend or past events videos you can watch.
|
Check out our **[events calendar](https://fluxcd.io/community/#talks)**,
|
||||||
|
both with upcoming talks you can attend or past events videos you can watch.
|
||||||
|
|
||||||
We look forward to seeing you with us!
|
We look forward to seeing you with us!
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ Tasks
|
|||||||
|
|
||||||
### Flux image update feature parity
|
### Flux image update feature parity
|
||||||
|
|
||||||
[= 80% "80%"]
|
[= 100% "100%"]
|
||||||
|
|
||||||
Image automation is available as a prerelease. See [this
|
Image automation is available as a prerelease. See [this
|
||||||
guide](https://toolkit.fluxcd.io/guides/image-update/) for how to
|
guide](https://toolkit.fluxcd.io/guides/image-update/) for how to
|
||||||
@@ -68,14 +68,12 @@ Tasks
|
|||||||
- [x] <span style="color:grey">Implement an image scanning controller</span>
|
- [x] <span style="color:grey">Implement an image scanning controller</span>
|
||||||
- [x] <span style="color:grey">Public image repo support</span>
|
- [x] <span style="color:grey">Public image repo support</span>
|
||||||
- [x] <span style="color:grey">Credentials from Secret [fluxcd/image-reflector-controller#35](https://github.com/fluxcd/image-reflector-controller/pull/35)</span>
|
- [x] <span style="color:grey">Credentials from Secret [fluxcd/image-reflector-controller#35](https://github.com/fluxcd/image-reflector-controller/pull/35)</span>
|
||||||
- [ ] ECR-specific support [fluxcd/image-reflector-controller#11](https://github.com/fluxcd/image-reflector-controller/issues/11)
|
|
||||||
- [ ] GCR-specific support [fluxcd/image-reflector-controller#11](https://github.com/fluxcd/image-reflector-controller/issues/11)
|
|
||||||
- [ ] Azure-specific support [fluxcd/image-reflector-controller#11](https://github.com/fluxcd/image-reflector-controller/issues/11)
|
|
||||||
- [x] <span style="color:grey">Design the automation component</span>
|
- [x] <span style="color:grey">Design the automation component</span>
|
||||||
- [x] <span style="color:grey">Implement the image scan/patch/push workflow</span>
|
- [x] <span style="color:grey">Implement the image scan/patch/push workflow</span>
|
||||||
- [x] <span style="color:grey">Integrate the new components in the Flux CLI [fluxcd/flux2#538](https://github.com/fluxcd/flux2/pull/538)</span>
|
- [x] <span style="color:grey">Integrate the new components in the Flux CLI [fluxcd/flux2#538](https://github.com/fluxcd/flux2/pull/538)</span>
|
||||||
- [x] <span style="color:grey">Write a guide for how to use image automation ([guide here](https://toolkit.fluxcd.io/guides/image-update/))</span>
|
- [x] <span style="color:grey">Write a guide for how to use image automation ([guide here](https://toolkit.fluxcd.io/guides/image-update/))</span>
|
||||||
- [ ] Write a migration guide from Flux annotations
|
- [x] <span style="color:grey">ACR/ECR/GCR integration ([guide here](https://toolkit.fluxcd.io/guides/image-update/#imagerepository-cloud-providers-authentication))</span>
|
||||||
|
- [x] <span style="color:grey">Write a migration guide from Flux v1 annotations ([guide here](https://toolkit.fluxcd.io/guides/flux-v1-automation-migration/))</span>
|
||||||
|
|
||||||
## The road to Helm Operator v2
|
## The road to Helm Operator v2
|
||||||
|
|
||||||
|
|||||||
16
go.mod
16
go.mod
@@ -5,17 +5,17 @@ go 1.15
|
|||||||
require (
|
require (
|
||||||
github.com/blang/semver/v4 v4.0.0
|
github.com/blang/semver/v4 v4.0.0
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2
|
github.com/cyphar/filepath-securejoin v0.2.2
|
||||||
github.com/fluxcd/helm-controller/api v0.6.1
|
github.com/fluxcd/helm-controller/api v0.7.0
|
||||||
github.com/fluxcd/image-automation-controller/api v0.4.0
|
github.com/fluxcd/image-automation-controller/api v0.5.0
|
||||||
github.com/fluxcd/image-reflector-controller/api v0.5.0
|
github.com/fluxcd/image-reflector-controller/api v0.6.0
|
||||||
github.com/fluxcd/kustomize-controller/api v0.7.4
|
github.com/fluxcd/kustomize-controller/api v0.8.0
|
||||||
github.com/fluxcd/notification-controller/api v0.7.1
|
github.com/fluxcd/notification-controller/api v0.8.0
|
||||||
github.com/fluxcd/pkg/apis/meta v0.7.0
|
github.com/fluxcd/pkg/apis/meta v0.8.0
|
||||||
github.com/fluxcd/pkg/git v0.3.0
|
github.com/fluxcd/pkg/git v0.3.0
|
||||||
github.com/fluxcd/pkg/runtime v0.8.0
|
github.com/fluxcd/pkg/runtime v0.8.0
|
||||||
github.com/fluxcd/pkg/ssh v0.0.5
|
github.com/fluxcd/pkg/ssh v0.0.5
|
||||||
github.com/fluxcd/pkg/untar v0.0.5
|
github.com/fluxcd/pkg/untar v0.0.5
|
||||||
github.com/fluxcd/source-controller/api v0.7.4
|
github.com/fluxcd/source-controller/api v0.8.0
|
||||||
github.com/google/go-containerregistry v0.2.0
|
github.com/google/go-containerregistry v0.2.0
|
||||||
github.com/manifoldco/promptui v0.7.0
|
github.com/manifoldco/promptui v0.7.0
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
@@ -28,6 +28,6 @@ require (
|
|||||||
k8s.io/client-go v0.20.2
|
k8s.io/client-go v0.20.2
|
||||||
sigs.k8s.io/cli-utils v0.20.2
|
sigs.k8s.io/cli-utils v0.20.2
|
||||||
sigs.k8s.io/controller-runtime v0.8.0
|
sigs.k8s.io/controller-runtime v0.8.0
|
||||||
sigs.k8s.io/kustomize/api v0.7.0
|
sigs.k8s.io/kustomize/api v0.7.4
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|||||||
74
go.sum
74
go.sum
@@ -35,7 +35,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
|
|||||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||||
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
||||||
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||||
github.com/Azure/go-autorest/autorest v0.10.2 h1:NuSF3gXetiHyUbVdneJMEVyPUYAe5wh+aN08JYAf1tI=
|
|
||||||
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||||
github.com/Azure/go-autorest/autorest v0.11.1 h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ=
|
github.com/Azure/go-autorest/autorest v0.11.1 h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ=
|
||||||
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
||||||
@@ -59,7 +58,6 @@ github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocm
|
|||||||
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||||
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
||||||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
|
||||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||||
github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
|
github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
|
||||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||||
@@ -113,7 +111,6 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1U
|
|||||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs=
|
|
||||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
@@ -198,23 +195,24 @@ github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
|
|||||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fluxcd/helm-controller/api v0.6.1 h1:vraSNIXdSiqrjpKs53hnr2NMpL0YKGQcEpuILUZ3SlE=
|
github.com/fluxcd/helm-controller/api v0.7.0 h1:9/Ii9rW/PN2zxe4//uJlfleUYMuc4Ha/breMn4vTlUw=
|
||||||
github.com/fluxcd/helm-controller/api v0.6.1/go.mod h1:vd4Nt6ZZlDkXwJLkmHRgvNc1bvBPMEIZ0o1qFyEWglo=
|
github.com/fluxcd/helm-controller/api v0.7.0/go.mod h1:QIoGA1SCBqQgHRF2BjdxJX6DRBjLGWC86ZDj7d2P5bI=
|
||||||
github.com/fluxcd/image-automation-controller/api v0.4.0 h1:WMPVYfPQTLMvoINdoBDp5u26PJLO1EUZLyeQ95yHKgA=
|
github.com/fluxcd/image-automation-controller/api v0.5.0 h1:A8hXCPAbvfvEI+Z7E9+v+ty3Wehmq2bjM6/lIzXyvWs=
|
||||||
github.com/fluxcd/image-automation-controller/api v0.4.0/go.mod h1:lHGxbFJNIwgK32YWx9uNf4WR/Z92Hdrl8cn6TzixfUI=
|
github.com/fluxcd/image-automation-controller/api v0.5.0/go.mod h1:t1rcueSECYj/77cXWsji06uEQgzdTV+2Hdd+ryCqKhg=
|
||||||
github.com/fluxcd/image-reflector-controller/api v0.5.0 h1:xzE+Vi5NevR2TkD9OrEYmjBLTYxeQ28VY5mo0x1Q93Q=
|
github.com/fluxcd/image-reflector-controller/api v0.6.0 h1:M62HPqw2UvFRVNKy0EMtNUJC9BJ6HC3VULnV0gnqlpg=
|
||||||
github.com/fluxcd/image-reflector-controller/api v0.5.0/go.mod h1:MS3mGjZLnzZsfSqVLGbp0WNJr/k8XRFpw4G6ApLFTbc=
|
github.com/fluxcd/image-reflector-controller/api v0.6.0/go.mod h1:MS3mGjZLnzZsfSqVLGbp0WNJr/k8XRFpw4G6ApLFTbc=
|
||||||
github.com/fluxcd/kustomize-controller/api v0.7.4 h1:SbcwYh0041yVmODnHtUFNpqtD8HYPoDm1AhL40WkgB8=
|
github.com/fluxcd/kustomize-controller/api v0.8.0 h1:i8xFqIQweqvSFiYBaoAnYnaKw+M5n50n+8yb5LgjIss=
|
||||||
github.com/fluxcd/kustomize-controller/api v0.7.4/go.mod h1:GMdry0llCMT50hDcQ70ujmtd3vOXUWCJMTggPpGNM+I=
|
github.com/fluxcd/kustomize-controller/api v0.8.0/go.mod h1:RIaE0c/tgHr75OP9f1CAQzm0n7yGFqWf2jZcNb1ix28=
|
||||||
github.com/fluxcd/notification-controller/api v0.7.1 h1:BDeJ172oJN0LKC6VJlu42NkeBCvTKn9cDPVaUo8M1Xs=
|
github.com/fluxcd/notification-controller/api v0.8.0 h1:lOLYX2H/owlL8I9ws1lS6uN9dmaJk3KtT+/MgQhPKIw=
|
||||||
github.com/fluxcd/notification-controller/api v0.7.1/go.mod h1:n1ow7Mxdcedrio5pf/HhBbaVp/4yGTFACPgfCtiyWoU=
|
github.com/fluxcd/notification-controller/api v0.8.0/go.mod h1:nWQZb8DeTM/tdgTxCts6QRxfXTtTPQWuQGeoffwYUbw=
|
||||||
github.com/fluxcd/pkg/apis/meta v0.5.0 h1:FaU++mQY0g4sVVl+hG+vk0CXBLbb4EVfRuzs3IjLXvo=
|
github.com/fluxcd/pkg/apis/kustomize v0.0.1 h1:TkA80R0GopRY27VJqzKyS6ifiKIAfwBd7OHXtV3t2CI=
|
||||||
|
github.com/fluxcd/pkg/apis/kustomize v0.0.1/go.mod h1:JAFPfnRmcrAoG1gNiA8kmEXsnOBuDyZ/F5X4DAQcVV0=
|
||||||
github.com/fluxcd/pkg/apis/meta v0.5.0/go.mod h1:aEUuZIawboAAFLlYz/juVJ7KNmlWbBtJFYkOWWmGUR4=
|
github.com/fluxcd/pkg/apis/meta v0.5.0/go.mod h1:aEUuZIawboAAFLlYz/juVJ7KNmlWbBtJFYkOWWmGUR4=
|
||||||
github.com/fluxcd/pkg/apis/meta v0.7.0 h1:5e8gm4OLqjuKWdrOIY5DEEsjcwzyJFK8rCDesJ+V8IY=
|
|
||||||
github.com/fluxcd/pkg/apis/meta v0.7.0/go.mod h1:yHuY8kyGHYz22I0jQzqMMGCcHViuzC/WPdo9Gisk8Po=
|
github.com/fluxcd/pkg/apis/meta v0.7.0/go.mod h1:yHuY8kyGHYz22I0jQzqMMGCcHViuzC/WPdo9Gisk8Po=
|
||||||
|
github.com/fluxcd/pkg/apis/meta v0.8.0 h1:wqWpUsxhKHB1ZztcvOz+vnyhdKW9cWmjFp8Vci/XOdk=
|
||||||
|
github.com/fluxcd/pkg/apis/meta v0.8.0/go.mod h1:yHuY8kyGHYz22I0jQzqMMGCcHViuzC/WPdo9Gisk8Po=
|
||||||
github.com/fluxcd/pkg/git v0.3.0 h1:nrKZWZ/ymDevud3Wf1LEieO/QcNPnqz1/MrkZBFcg9o=
|
github.com/fluxcd/pkg/git v0.3.0 h1:nrKZWZ/ymDevud3Wf1LEieO/QcNPnqz1/MrkZBFcg9o=
|
||||||
github.com/fluxcd/pkg/git v0.3.0/go.mod h1:ZwG0iLOqNSyNw6lsPIAO+v6+BqqCXyV+r1Oq6Lm+slg=
|
github.com/fluxcd/pkg/git v0.3.0/go.mod h1:ZwG0iLOqNSyNw6lsPIAO+v6+BqqCXyV+r1Oq6Lm+slg=
|
||||||
github.com/fluxcd/pkg/runtime v0.6.2 h1:sWnSv6AhMY30fexRQ37lv2Q9Rvdu05zbiqMSldw+MjQ=
|
|
||||||
github.com/fluxcd/pkg/runtime v0.6.2/go.mod h1:RuqYOYCvBJwo4rg83d28WOt2vfSaemuZCVpUagAjWQc=
|
github.com/fluxcd/pkg/runtime v0.6.2/go.mod h1:RuqYOYCvBJwo4rg83d28WOt2vfSaemuZCVpUagAjWQc=
|
||||||
github.com/fluxcd/pkg/runtime v0.8.0 h1:cnSBZJLcXlKgjXpFFFExu+4ZncIxmPgNIx+ErLcCLnA=
|
github.com/fluxcd/pkg/runtime v0.8.0 h1:cnSBZJLcXlKgjXpFFFExu+4ZncIxmPgNIx+ErLcCLnA=
|
||||||
github.com/fluxcd/pkg/runtime v0.8.0/go.mod h1:tQwEN+RESjJmtwSSv7I+6bkNM9raIXpGsCjruaIVX6A=
|
github.com/fluxcd/pkg/runtime v0.8.0/go.mod h1:tQwEN+RESjJmtwSSv7I+6bkNM9raIXpGsCjruaIVX6A=
|
||||||
@@ -222,8 +220,8 @@ github.com/fluxcd/pkg/ssh v0.0.5 h1:rnbFZ7voy2JBlUfMbfyqArX2FYaLNpDhccGFC3qW83A=
|
|||||||
github.com/fluxcd/pkg/ssh v0.0.5/go.mod h1:7jXPdXZpc0ttMNz2kD9QuMi3RNn/e0DOFbj0Tij/+Hs=
|
github.com/fluxcd/pkg/ssh v0.0.5/go.mod h1:7jXPdXZpc0ttMNz2kD9QuMi3RNn/e0DOFbj0Tij/+Hs=
|
||||||
github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
|
github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
|
||||||
github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw=
|
github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw=
|
||||||
github.com/fluxcd/source-controller/api v0.7.4 h1:Hy29SUyanKNHbb4AQZo7PqzmTx4y8TfkeO0GqBcb5PE=
|
github.com/fluxcd/source-controller/api v0.8.0 h1:jOgeOwCLXzmjinRiDT7e/IuSB7WNZMgrUwMLJm09K/o=
|
||||||
github.com/fluxcd/source-controller/api v0.7.4/go.mod h1:u2sdc/QDm0tzXHL7mZVj928hc3MMU+4mKCuAQg+94Bk=
|
github.com/fluxcd/source-controller/api v0.8.0/go.mod h1:u2sdc/QDm0tzXHL7mZVj928hc3MMU+4mKCuAQg+94Bk=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
||||||
@@ -365,7 +363,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
|||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
@@ -393,7 +390,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
|||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
|
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||||
@@ -419,7 +415,6 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
|||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@@ -429,7 +424,6 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC
|
|||||||
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||||
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||||
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||||
github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
|
|
||||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||||
github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=
|
github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=
|
||||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||||
@@ -464,7 +458,6 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh
|
|||||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
|
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
@@ -492,7 +485,6 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
|||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
|
||||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
|
github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
|
||||||
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
@@ -617,7 +609,6 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||||
github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
|
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
|
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
|
||||||
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
@@ -627,7 +618,6 @@ github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
|||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
|
github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
|
||||||
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
@@ -668,7 +658,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
|||||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
@@ -713,7 +702,6 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd
|
|||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
|
||||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
@@ -736,7 +724,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@@ -792,14 +779,12 @@ go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee33
|
|||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||||
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
@@ -808,9 +793,7 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
|
|||||||
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
|
||||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||||
@@ -909,7 +892,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
|||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
@@ -976,7 +958,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
|
|
||||||
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
|
||||||
@@ -987,7 +968,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|||||||
golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -995,7 +975,6 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
|
|||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
|
||||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@@ -1131,7 +1110,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
|||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
@@ -1167,7 +1145,6 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||||
@@ -1188,7 +1165,6 @@ k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
|
|||||||
k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
|
k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
|
||||||
k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY=
|
k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY=
|
||||||
k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
|
k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
|
||||||
k8s.io/api v0.19.4 h1:I+1I4cgJYuCDgiLNjKx7SLmIbwgj9w7N7Zr5vSIdwpo=
|
|
||||||
k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk=
|
k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk=
|
||||||
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
|
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
|
||||||
k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw=
|
k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw=
|
||||||
@@ -1206,7 +1182,6 @@ k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZ
|
|||||||
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||||
k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig=
|
k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig=
|
||||||
k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
|
k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
|
||||||
k8s.io/apimachinery v0.19.4 h1:+ZoddM7nbzrDCp0T3SWnyxqf8cbWPT2fkZImoyvHUG0=
|
|
||||||
k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
|
k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
|
||||||
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||||
k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg=
|
k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg=
|
||||||
@@ -1218,7 +1193,6 @@ k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
|
|||||||
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
|
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
|
||||||
k8s.io/apiserver v0.20.2/go.mod h1:2nKd93WyMhZx4Hp3RfgH2K5PhwyTrprrkWYnI7id7jA=
|
k8s.io/apiserver v0.20.2/go.mod h1:2nKd93WyMhZx4Hp3RfgH2K5PhwyTrprrkWYnI7id7jA=
|
||||||
k8s.io/cli-runtime v0.0.0-20191214191754-e6dc6d5c8724/go.mod h1:wzlq80lvjgHW9if6MlE4OIGC86MDKsy5jtl9nxz/IYY=
|
k8s.io/cli-runtime v0.0.0-20191214191754-e6dc6d5c8724/go.mod h1:wzlq80lvjgHW9if6MlE4OIGC86MDKsy5jtl9nxz/IYY=
|
||||||
k8s.io/cli-runtime v0.17.2 h1:YH4txSplyGudvxjhAJeHEtXc7Tr/16clKGfN076ydGk=
|
|
||||||
k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI=
|
k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI=
|
||||||
k8s.io/cli-runtime v0.20.2 h1:W0/FHdbApnl9oB7xdG643c/Zaf7TZT+43I+zKxwqvhU=
|
k8s.io/cli-runtime v0.20.2 h1:W0/FHdbApnl9oB7xdG643c/Zaf7TZT+43I+zKxwqvhU=
|
||||||
k8s.io/cli-runtime v0.20.2/go.mod h1:FjH6uIZZZP3XmwrXWeeYCbgxcrD6YXxoAykBaWH0VdM=
|
k8s.io/cli-runtime v0.20.2/go.mod h1:FjH6uIZZZP3XmwrXWeeYCbgxcrD6YXxoAykBaWH0VdM=
|
||||||
@@ -1229,7 +1203,6 @@ k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
|
|||||||
k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
|
k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
|
||||||
k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU=
|
k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU=
|
||||||
k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
|
k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
|
||||||
k8s.io/client-go v0.19.4 h1:85D3mDNoLF+xqpyE9Dh/OtrJDyJrSRKkHmDXIbEzer8=
|
|
||||||
k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA=
|
k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA=
|
||||||
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
|
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
|
||||||
k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ=
|
k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ=
|
||||||
@@ -1261,14 +1234,12 @@ k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
|||||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
|
|
||||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||||
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
|
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
|
||||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||||
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
|
|
||||||
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
|
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
|
||||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
|
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
|
||||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
||||||
@@ -1278,9 +1249,7 @@ k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU
|
|||||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||||
k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg=
|
|
||||||
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g=
|
|
||||||
k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
@@ -1301,25 +1270,22 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz
|
|||||||
sigs.k8s.io/cli-utils v0.20.2 h1:jNMu4ExtvFXlmKqZMDJqySqK55vGtVRqoLht6eIMffw=
|
sigs.k8s.io/cli-utils v0.20.2 h1:jNMu4ExtvFXlmKqZMDJqySqK55vGtVRqoLht6eIMffw=
|
||||||
sigs.k8s.io/cli-utils v0.20.2/go.mod h1:uT5cVxMrOoRplL8umtrJx5R51ZWsIKD7lQfPtot80uA=
|
sigs.k8s.io/cli-utils v0.20.2/go.mod h1:uT5cVxMrOoRplL8umtrJx5R51ZWsIKD7lQfPtot80uA=
|
||||||
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
|
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
|
||||||
sigs.k8s.io/controller-runtime v0.7.0 h1:bU20IBBEPccWz5+zXpLnpVsgBYxqclaHu1pVDl/gEt8=
|
|
||||||
sigs.k8s.io/controller-runtime v0.7.0/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU=
|
sigs.k8s.io/controller-runtime v0.7.0/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU=
|
||||||
sigs.k8s.io/controller-runtime v0.8.0 h1:s0dYdo7lQgJiAf+alP82PRwbz+oAqL3oSyMQ18XRDOc=
|
sigs.k8s.io/controller-runtime v0.8.0 h1:s0dYdo7lQgJiAf+alP82PRwbz+oAqL3oSyMQ18XRDOc=
|
||||||
sigs.k8s.io/controller-runtime v0.8.0/go.mod h1:v9Lbj5oX443uR7GXYY46E0EE2o7k2YxQ58GxVNeXSW4=
|
sigs.k8s.io/controller-runtime v0.8.0/go.mod h1:v9Lbj5oX443uR7GXYY46E0EE2o7k2YxQ58GxVNeXSW4=
|
||||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||||
sigs.k8s.io/kustomize/api v0.7.0 h1:djxH9k1izeU1BvdP1i23qqKwhmWu2BuKNEKr/Da7Dpw=
|
sigs.k8s.io/kustomize/api v0.7.4 h1:WyuZ7ZI7U978udBWMFdKlxjTMOmF7BjpQA1BuygyArY=
|
||||||
sigs.k8s.io/kustomize/api v0.7.0/go.mod h1:3TxKEyaxwOIfHmRbQF14hDUSRmVQI0iSn8qDA5zaO/0=
|
sigs.k8s.io/kustomize/api v0.7.4/go.mod h1:HkbVkf2FjT4G1iOFnPVsr013Af5RFD6dEJg/cUIWiM0=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.8.1/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
|
sigs.k8s.io/kustomize/kyaml v0.8.1/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.3 h1:ARSJUMN/c3k31DYxRfZ+vp/UepUQjg9zCwny7Oj908I=
|
sigs.k8s.io/kustomize/kyaml v0.10.9 h1:n3WNdvPPReRNDxW+XXd2JlyZ8EII721I21D1DBpBVBE=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.3/go.mod h1:RA+iCHA2wPCOfv6uG6TfXXWhYsHpgErq/AljxWKuxtg=
|
sigs.k8s.io/kustomize/kyaml v0.10.9/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
|
||||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
|
|
||||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
||||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
||||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@@ -32,8 +31,11 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@@ -44,6 +46,7 @@ import (
|
|||||||
kustypes "sigs.k8s.io/kustomize/api/types"
|
kustypes "sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
imageautov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
imageautov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
|
||||||
imagereflectv1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
imagereflectv1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||||
@@ -163,8 +166,11 @@ func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
scheme := apiruntime.NewScheme()
|
scheme := apiruntime.NewScheme()
|
||||||
|
_ = apiextensionsv1.AddToScheme(scheme)
|
||||||
_ = corev1.AddToScheme(scheme)
|
_ = corev1.AddToScheme(scheme)
|
||||||
_ = rbacv1.AddToScheme(scheme)
|
_ = rbacv1.AddToScheme(scheme)
|
||||||
|
_ = appsv1.AddToScheme(scheme)
|
||||||
|
_ = networkingv1.AddToScheme(scheme)
|
||||||
_ = sourcev1.AddToScheme(scheme)
|
_ = sourcev1.AddToScheme(scheme)
|
||||||
_ = kustomizev1.AddToScheme(scheme)
|
_ = kustomizev1.AddToScheme(scheme)
|
||||||
_ = helmv2.AddToScheme(scheme)
|
_ = helmv2.AddToScheme(scheme)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/helm-controller/archive/v0.6.1.zip//helm-controller-0.6.1/config/crd
|
- https://github.com/fluxcd/helm-controller/archive/v0.7.0.zip//helm-controller-0.7.0/config/crd
|
||||||
- https://github.com/fluxcd/helm-controller/archive/v0.6.1.zip//helm-controller-0.6.1/config/manager
|
- https://github.com/fluxcd/helm-controller/archive/v0.7.0.zip//helm-controller-0.7.0/config/manager
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/image-automation-controller/archive/v0.4.0.zip//image-automation-controller-0.4.0/config/crd
|
- https://github.com/fluxcd/image-automation-controller/archive/v0.5.0.zip//image-automation-controller-0.5.0/config/crd
|
||||||
- https://github.com/fluxcd/image-automation-controller/archive/v0.4.0.zip//image-automation-controller-0.4.0/config/manager
|
- https://github.com/fluxcd/image-automation-controller/archive/v0.5.0.zip//image-automation-controller-0.5.0/config/manager
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/image-reflector-controller/archive/v0.5.0.zip//image-reflector-controller-0.5.0/config/crd
|
- https://github.com/fluxcd/image-reflector-controller/archive/v0.6.0.zip//image-reflector-controller-0.6.0/config/crd
|
||||||
- https://github.com/fluxcd/image-reflector-controller/archive/v0.5.0.zip//image-reflector-controller-0.5.0/config/manager
|
- https://github.com/fluxcd/image-reflector-controller/archive/v0.6.0.zip//image-reflector-controller-0.6.0/config/manager
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/kustomize-controller/archive/v0.7.4.zip//kustomize-controller-0.7.4/config/crd
|
- https://github.com/fluxcd/kustomize-controller/archive/v0.8.0.zip//kustomize-controller-0.8.0/config/crd
|
||||||
- https://github.com/fluxcd/kustomize-controller/archive/v0.7.4.zip//kustomize-controller-0.7.4/config/manager
|
- https://github.com/fluxcd/kustomize-controller/archive/v0.8.0.zip//kustomize-controller-0.8.0/config/manager
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/notification-controller/archive/v0.7.1.zip//notification-controller-0.7.1/config/crd
|
- https://github.com/fluxcd/notification-controller/archive/v0.8.0.zip//notification-controller-0.8.0/config/crd
|
||||||
- https://github.com/fluxcd/notification-controller/archive/v0.7.1.zip//notification-controller-0.7.1/config/manager
|
- https://github.com/fluxcd/notification-controller/archive/v0.8.0.zip//notification-controller-0.8.0/config/manager
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/source-controller/archive/v0.7.4.zip//source-controller-0.7.4/config/crd
|
- https://github.com/fluxcd/source-controller/archive/v0.8.0.zip//source-controller-0.8.0/config/crd
|
||||||
- https://github.com/fluxcd/source-controller/archive/v0.7.4.zip//source-controller-0.7.4/config/manager
|
- https://github.com/fluxcd/source-controller/archive/v0.8.0.zip//source-controller-0.8.0/config/manager
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
commonLabels:
|
||||||
|
app: credentials-sync
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- sync.yaml
|
||||||
|
|
||||||
|
vars:
|
||||||
|
- name: KUBE_SECRET
|
||||||
|
objref:
|
||||||
|
kind: ConfigMap
|
||||||
|
name: credentials-sync
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: data.KUBE_SECRET
|
||||||
|
|
||||||
|
configurations:
|
||||||
|
- kustomizeconfig.yaml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
varReference:
|
||||||
|
- path: rules/resourceNames
|
||||||
|
kind: Role
|
||||||
125
manifests/integrations/registry-credentials-sync/_base/sync.yaml
Normal file
125
manifests/integrations/registry-credentials-sync/_base/sync.yaml
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
# Patch this ConfigMap with additional values needed for your cloud
|
||||||
|
KUBE_SECRET: my-registry-token # does not yet exist -- will be created in the same Namespace
|
||||||
|
SYNC_PERIOD: "3600" # tokens expire; refresh faster than that
|
||||||
|
|
||||||
|
---
|
||||||
|
# This Deployment frequently fetches registry tokens and applies them as an imagePullSecret.
|
||||||
|
# It's done as a 1-replica Deployment rather than a CronJob, because CronJob scheduling can
|
||||||
|
# block cluster bootstraps and cold-reboots from obtaining registry tokens for a considerable time.
|
||||||
|
# This deployment will immediately fetch a token, which reduces latency for working image updates.
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
serviceAccount: credentials-sync
|
||||||
|
containers:
|
||||||
|
- image: busybox # override this with a cloud-specific image
|
||||||
|
name: sync
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: credentials-sync
|
||||||
|
env:
|
||||||
|
- name: RECONCILE_SH # override this env var with a shell function in a kustomize patch
|
||||||
|
value: |-
|
||||||
|
reconcile() {
|
||||||
|
echo reconciling...
|
||||||
|
}
|
||||||
|
command:
|
||||||
|
- bash
|
||||||
|
- -ceu
|
||||||
|
- |-
|
||||||
|
# template reconcile() into the script
|
||||||
|
# env var is expanded by k8s before the pod starts
|
||||||
|
$(RECONCILE_SH)
|
||||||
|
|
||||||
|
apply-secret() {
|
||||||
|
/kbin/kubectl create secret docker-registry "${1}" \
|
||||||
|
--docker-server="${2}" \
|
||||||
|
--docker-username="${3}" \
|
||||||
|
--docker-password="${4}" \
|
||||||
|
--dry-run=client -o=yaml \
|
||||||
|
| grep -v "creationTimestamp:" \
|
||||||
|
| /kbin/kubectl apply -f -
|
||||||
|
}
|
||||||
|
|
||||||
|
pause_loop() {
|
||||||
|
sleep "${SYNC_PERIOD:-3600}" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
graceful_exit() {
|
||||||
|
echo "Trapped signal -- $(date)"
|
||||||
|
job_ids="$(
|
||||||
|
jobs \
|
||||||
|
| grep "pause_loop" \
|
||||||
|
| cut -d] -f1 \
|
||||||
|
| tr [ %
|
||||||
|
)"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
if [ "${job_ids}" ]; then
|
||||||
|
kill ${job_ids}
|
||||||
|
fi
|
||||||
|
wait
|
||||||
|
echo "Graceful exit -- $(date)"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap graceful_exit INT TERM
|
||||||
|
|
||||||
|
echo "Loop started (period: ${SYNC_PERIOD} s) -- $(date)"
|
||||||
|
while true; do
|
||||||
|
reconcile & wait $!
|
||||||
|
pause_loop & wait $!
|
||||||
|
done
|
||||||
|
resources: {}
|
||||||
|
|
||||||
|
|
||||||
|
# RBAC necessary for our Deployment to apply our imagePullSecret
|
||||||
|
---
|
||||||
|
kind: Role
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
# # Lock this down to the specific Secret name (Optional)
|
||||||
|
resourceNames:
|
||||||
|
- $(KUBE_SECRET) # templated from kustomize vars referencing ConfigMap, also see kustomizeconfig.yaml
|
||||||
|
---
|
||||||
|
kind: RoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: credentials-sync
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: credentials-sync
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
commonLabels:
|
||||||
|
app: credentials-sync
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- sync.yaml
|
||||||
|
|
||||||
|
vars:
|
||||||
|
- name: KUBE_SECRET
|
||||||
|
objref:
|
||||||
|
kind: ConfigMap
|
||||||
|
name: credentials-sync
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: data.KUBE_SECRET
|
||||||
|
|
||||||
|
configurations:
|
||||||
|
- kustomizeconfig.yaml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
varReference:
|
||||||
|
- path: rules/resourceNames
|
||||||
|
kind: Role
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
# Patch this ConfigMap with additional values needed for your cloud
|
||||||
|
KUBE_SECRET: my-registry-token # does not yet exist -- will be created in the same Namespace
|
||||||
|
|
||||||
|
---
|
||||||
|
# This CronJob frequently fetches registry tokens and applies them as an imagePullSecret.
|
||||||
|
# note: CronJob scheduling can block cluster bootstraps and cold-reboots from obtaining registry tokens for a considerable time.
|
||||||
|
# To run the job immediately, do `kubectl create job --from=cronjob/credentials-sync -n flux-system credentials-sync-init`
|
||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
suspend: false
|
||||||
|
schedule: 0 */6 * * *
|
||||||
|
failedJobsHistoryLimit: 1
|
||||||
|
successfulJobsHistoryLimit: 1
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
serviceAccountName: credentials-sync
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- image: busybox # override this with a cloud-specific image
|
||||||
|
name: sync
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: credentials-sync
|
||||||
|
env:
|
||||||
|
- name: RECONCILE_SH # override this env var with a shell function in a kustomize patch
|
||||||
|
value: |-
|
||||||
|
reconcile() {
|
||||||
|
echo reconciling...
|
||||||
|
}
|
||||||
|
command:
|
||||||
|
- bash
|
||||||
|
- -ceu
|
||||||
|
- |-
|
||||||
|
# template reconcile() into the script
|
||||||
|
# env var is expanded by k8s before the pod starts
|
||||||
|
$(RECONCILE_SH)
|
||||||
|
|
||||||
|
apply-secret() {
|
||||||
|
/kbin/kubectl create secret docker-registry "${1}" \
|
||||||
|
--docker-server="${2}" \
|
||||||
|
--docker-username="${3}" \
|
||||||
|
--docker-password="${4}" \
|
||||||
|
--dry-run=client -o=yaml \
|
||||||
|
| grep -v "creationTimestamp:" \
|
||||||
|
| /kbin/kubectl apply -f -
|
||||||
|
}
|
||||||
|
|
||||||
|
reconcile
|
||||||
|
resources: {}
|
||||||
|
|
||||||
|
|
||||||
|
# RBAC necessary for our Deployment to apply our imagePullSecret
|
||||||
|
---
|
||||||
|
kind: Role
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
# # Lock this down to the specific Secret name (Optional)
|
||||||
|
resourceNames:
|
||||||
|
- $(KUBE_SECRET) # templated from kustomize vars referencing ConfigMap, also see kustomizeconfig.yaml
|
||||||
|
---
|
||||||
|
kind: RoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: credentials-sync
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: credentials-sync
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
ECR_REGION: us-east-1 # set the region
|
||||||
|
ECR_REGISTRY: <account id>.dkr.ecr.<region>.amazonaws.com # fill in the account id and region
|
||||||
|
KUBE_SECRET: ecr-credentials # does not yet exist -- will be created in the same Namespace
|
||||||
|
|
||||||
|
|
||||||
|
# Bind IRSA for the ServiceAccount
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
annotations:
|
||||||
|
eks.amazonaws.com/role-arn: <role arn> # set the ARN for your role
|
||||||
|
|
||||||
|
|
||||||
|
# Set the reconcile period
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
schedule: 0 */6 * * * # every 6hrs -- ECR tokens expire every 12 hours; refresh faster than that
|
||||||
|
|
||||||
|
|
||||||
|
## If not using IRSA, set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables
|
||||||
|
## Store these values in a Secret and load them in the container using envFrom.
|
||||||
|
## For managing this secret via GitOps, consider using SOPS or SealedSecrets and add that manifest in a resource file for this kustomize build.
|
||||||
|
## https://toolkit.fluxcd.io/guides/mozilla-sops/
|
||||||
|
## https://toolkit.fluxcd.io/guides/sealed-secrets/
|
||||||
|
# ---
|
||||||
|
# apiVersion: apps/v1
|
||||||
|
# kind: Deployment
|
||||||
|
# metadata:
|
||||||
|
# name: credentials-sync
|
||||||
|
# namespace: flux-system
|
||||||
|
# spec:
|
||||||
|
# template:
|
||||||
|
# spec:
|
||||||
|
# containers:
|
||||||
|
# - name: sync
|
||||||
|
# envFrom:
|
||||||
|
# secretRef:
|
||||||
|
# name: $(ECR_SECRET_NAME) # uncomment the var for this in kustomization.yaml
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- image: bitnami/kubectl
|
||||||
|
name: copy-kubectl
|
||||||
|
# it's okay to do this because kubectl is a statically linked binary
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -ceu
|
||||||
|
- cp $(which kubectl) /kbin/
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- name: kbin
|
||||||
|
mountPath: /kbin
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
volumeMounts:
|
||||||
|
- name: kbin
|
||||||
|
mountPath: /kbin
|
||||||
|
volumes:
|
||||||
|
- name: kbin
|
||||||
|
emptyDir: {}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namePrefix: ecr-
|
||||||
|
commonLabels:
|
||||||
|
app: ecr-credentials-sync
|
||||||
|
|
||||||
|
namespace: flux-system
|
||||||
|
|
||||||
|
bases:
|
||||||
|
- ../_base
|
||||||
|
## If not using IRSA, consider creating the following file via SOPS or SealedSecrets
|
||||||
|
# - encrypted-secret.yaml
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- config-patches.yaml
|
||||||
|
- kubectl-patch.yaml
|
||||||
|
- reconcile-patch.yaml
|
||||||
|
|
||||||
|
## uncomment if using encrypted-secret.yaml
|
||||||
|
# vars:
|
||||||
|
# - name: ECR_SECRET_NAME
|
||||||
|
# objref:
|
||||||
|
# kind: Secret
|
||||||
|
# name: credentials-sync
|
||||||
|
# apiVersion: v1
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
image: mcr.microsoft.com/azure-cli
|
||||||
|
env:
|
||||||
|
- name: RECONCILE_SH
|
||||||
|
value: |-
|
||||||
|
reconcile() {
|
||||||
|
echo "Starting ECR token sync -- $(date)"
|
||||||
|
echo "Logging into ECR: ${ECR_REGION} -- ${ECR_REGISTRY}"
|
||||||
|
token="$(aws ecr get-login-password --region "${ECR_REGION}")"
|
||||||
|
user="AWS"
|
||||||
|
server="${ECR_REGISTRY}"
|
||||||
|
|
||||||
|
echo "Creating secret: ${KUBE_SECRET}"
|
||||||
|
apply-secret "${KUBE_SECRET}" "${token}" "${user}" "${server}"
|
||||||
|
|
||||||
|
echo "Finished ECR token sync -- $(date)"
|
||||||
|
echo
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# This is a stub resource patched by config-patches.yaml, so that all config is visible in one file
|
||||||
|
---
|
||||||
|
apiVersion: aadpodidentity.k8s.io/v1
|
||||||
|
kind: AzureIdentity
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync # if this is changed, also change in config-patches.yaml
|
||||||
|
namespace: flux-system
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
ACR_NAME: my-registry
|
||||||
|
KUBE_SECRET: acr-my-registry # does not yet exist -- will be created in the same Namespace
|
||||||
|
|
||||||
|
# Create an identity in Azure and assign it a role to pull from ACR (note: the identity's resourceGroup should match the desired ACR):
|
||||||
|
# az identity create -n acr-sync
|
||||||
|
# az role assignment create --role AcrPull --assignee-object-id "$(az identity show -n acr-sync -o tsv --query principalId)"
|
||||||
|
# Fetch the clientID and resourceID to configure the AzureIdentity spec below:
|
||||||
|
# az identity show -n acr-sync -otsv --query clientId
|
||||||
|
# az identity show -n acr-sync -otsv --query resourceId
|
||||||
|
---
|
||||||
|
apiVersion: aadpodidentity.k8s.io/v1
|
||||||
|
kind: AzureIdentity
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync # name must match the stub-resource in az-identity.yaml
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
clientID: 82d01fb0-7799-4d9d-92c7-21e7632c0000
|
||||||
|
resourceID: /subscriptions/873c7e7f-76cd-4805-ae86-b923850b0000/resourcegroups/stealthybox/providers/Microsoft.ManagedIdentity/userAssignedIdentities/acr-sync
|
||||||
|
type: 0 # user-managed identity
|
||||||
|
|
||||||
|
# Set the reconcile period + specify the pod-identity via the aadpodidbinding label
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
schedule: 0 * * * * # ACR tokens expire every 3 hours; refresh faster than that
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
aadpodidbinding: $(AZ_IDENTITY_NAME) # match the AzureIdentity name
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- image: bitnami/kubectl
|
||||||
|
name: copy-kubectl
|
||||||
|
# it's okay to do this because kubectl is a statically linked binary
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -ceu
|
||||||
|
- cp $(which kubectl) /kbin/
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- name: kbin
|
||||||
|
mountPath: /kbin
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
volumeMounts:
|
||||||
|
- name: kbin
|
||||||
|
mountPath: /kbin
|
||||||
|
volumes:
|
||||||
|
- name: kbin
|
||||||
|
emptyDir: {}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namePrefix: acr-
|
||||||
|
commonLabels:
|
||||||
|
app: acr-credentials-sync
|
||||||
|
|
||||||
|
namespace: flux-system
|
||||||
|
|
||||||
|
bases:
|
||||||
|
- ../_base
|
||||||
|
resources:
|
||||||
|
- az-identity.yaml
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- config-patches.yaml
|
||||||
|
- kubectl-patch.yaml
|
||||||
|
- reconcile-patch.yaml
|
||||||
|
|
||||||
|
vars:
|
||||||
|
- name: AZ_IDENTITY_NAME
|
||||||
|
objref:
|
||||||
|
kind: AzureIdentity
|
||||||
|
name: credentials-sync
|
||||||
|
apiVersion: aadpodidentity.k8s.io/v1
|
||||||
|
|
||||||
|
configurations:
|
||||||
|
- kustomizeconfig.yaml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
varReference:
|
||||||
|
- path: spec/jobTemplate/spec/template/metadata/labels
|
||||||
|
kind: Deployment
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
image: mcr.microsoft.com/azure-cli
|
||||||
|
env:
|
||||||
|
- name: RECONCILE_SH
|
||||||
|
value: |-
|
||||||
|
reconcile() {
|
||||||
|
echo "Starting ACR token sync -- $(date)"
|
||||||
|
echo "Logging into Azure"
|
||||||
|
az login --identity
|
||||||
|
echo "Logging into ACR: ${ACR_NAME}"
|
||||||
|
output="$(az acr login --expose-token -o=tsv -n "${ACR_NAME}")"
|
||||||
|
read token server <<< "${output}"
|
||||||
|
user="00000000-0000-0000-0000-000000000000"
|
||||||
|
|
||||||
|
echo "Creating secret: ${KUBE_SECRET}"
|
||||||
|
/kbin/kubectl create secret docker-registry "${KUBE_SECRET}" \
|
||||||
|
--docker-server="${server}" \
|
||||||
|
--docker-username="00000000-0000-0000-0000-000000000000" \
|
||||||
|
--docker-password="${token}" \
|
||||||
|
--dry-run=client -o=yaml \
|
||||||
|
| grep -v "creationTimestamp:" \
|
||||||
|
| /kbin/kubectl apply -f -
|
||||||
|
|
||||||
|
echo "Finished ACR token sync -- $(date)"
|
||||||
|
echo
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
GCR_REGISTRY: gcr.io # set the registry
|
||||||
|
KUBE_SECRET: gcr-credentials # does not yet exist -- will be created in the same Namespace
|
||||||
|
|
||||||
|
# Bind to the GCP service-account
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: <name>@<project-id>.iam.gserviceaccount.com # set the GCP service-account
|
||||||
|
|
||||||
|
# Set the reconcile period
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
schedule: 0,30 * * * * # 30m interval -- GCR tokens expire every hour; refresh faster than that
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namePrefix: gcr-
|
||||||
|
commonLabels:
|
||||||
|
app: gcr-credentials-sync
|
||||||
|
|
||||||
|
namespace: flux-system
|
||||||
|
|
||||||
|
bases:
|
||||||
|
- ../_base
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- config-patches.yaml
|
||||||
|
- reconcile-patch.yaml
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: batch/v1beta1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
image: aws/aws-cli
|
||||||
|
env:
|
||||||
|
- name: RECONCILE_SH
|
||||||
|
value: |-
|
||||||
|
reconcile() {
|
||||||
|
echo "Starting GCR token sync -- $(date)"
|
||||||
|
echo "Logging into ECR: ${ECR_REGION} -- ${ECR_REGISTRY}"
|
||||||
|
token="$(gcloud auth print-access-token)"
|
||||||
|
user="oauth2accesstoken "
|
||||||
|
server="${GCR_REGISTRY}"
|
||||||
|
|
||||||
|
echo "Creating secret: ${KUBE_SECRET}"
|
||||||
|
apply-secret "${KUBE_SECRET}" "${token}" "${user}" "${server}"
|
||||||
|
|
||||||
|
echo "Finished GCR token sync -- $(date)"
|
||||||
|
echo
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
ECR_REGION: us-east-1 # set the region
|
||||||
|
ECR_REGISTRY: <account id>.dkr.ecr.<region>.amazonaws.com # fill in the account id and region
|
||||||
|
KUBE_SECRET: ecr-credentials # does not yet exist -- will be created in the same Namespace
|
||||||
|
SYNC_PERIOD: "21600" # 6hrs -- ECR tokens expire every 12 hours; refresh faster than that
|
||||||
|
|
||||||
|
|
||||||
|
# Bind IRSA for the ServiceAccount
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
annotations:
|
||||||
|
eks.amazonaws.com/role-arn: <role arn> # set the ARN for your role
|
||||||
|
|
||||||
|
|
||||||
|
## If not using IRSA, set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables
|
||||||
|
## Store these values in a Secret and load them in the container using envFrom.
|
||||||
|
## For managing this secret via GitOps, consider using SOPS or SealedSecrets and add that manifest in a resource file for this kustomize build.
|
||||||
|
## https://toolkit.fluxcd.io/guides/mozilla-sops/
|
||||||
|
## https://toolkit.fluxcd.io/guides/sealed-secrets/
|
||||||
|
# ---
|
||||||
|
# apiVersion: apps/v1
|
||||||
|
# kind: Deployment
|
||||||
|
# metadata:
|
||||||
|
# name: credentials-sync
|
||||||
|
# namespace: flux-system
|
||||||
|
# spec:
|
||||||
|
# template:
|
||||||
|
# spec:
|
||||||
|
# containers:
|
||||||
|
# - name: sync
|
||||||
|
# envFrom:
|
||||||
|
# secretRef:
|
||||||
|
# name: $(ECR_SECRET_NAME) # uncomment the var for this in kustomization.yaml
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- image: bitnami/kubectl
|
||||||
|
name: copy-kubectl
|
||||||
|
# it's okay to do this because kubectl is a statically linked binary
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -ceu
|
||||||
|
- cp $(which kubectl) /kbin/
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- name: kbin
|
||||||
|
mountPath: /kbin
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
volumeMounts:
|
||||||
|
- name: kbin
|
||||||
|
mountPath: /kbin
|
||||||
|
volumes:
|
||||||
|
- name: kbin
|
||||||
|
emptyDir: {}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namePrefix: ecr-
|
||||||
|
commonLabels:
|
||||||
|
app: ecr-credentials-sync
|
||||||
|
|
||||||
|
namespace: flux-system
|
||||||
|
|
||||||
|
bases:
|
||||||
|
- ../_base
|
||||||
|
## If not using IRSA, consider creating the following file via SOPS or SealedSecrets
|
||||||
|
# - encrypted-secret.yaml
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- config-patches.yaml
|
||||||
|
- kubectl-patch.yaml
|
||||||
|
- reconcile-patch.yaml
|
||||||
|
|
||||||
|
## uncomment if using encrypted-secret.yaml
|
||||||
|
# vars:
|
||||||
|
# - name: ECR_SECRET_NAME
|
||||||
|
# objref:
|
||||||
|
# kind: Secret
|
||||||
|
# name: credentials-sync
|
||||||
|
# apiVersion: v1
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
image: aws/aws-cli
|
||||||
|
env:
|
||||||
|
- name: RECONCILE_SH
|
||||||
|
value: |-
|
||||||
|
reconcile() {
|
||||||
|
echo "Starting ECR token sync -- $(date)"
|
||||||
|
echo "Logging into ECR: ${ECR_REGION} -- ${ECR_REGISTRY}"
|
||||||
|
token="$(aws ecr get-login-password --region "${ECR_REGION}")"
|
||||||
|
user="AWS"
|
||||||
|
server="${ECR_REGISTRY}"
|
||||||
|
|
||||||
|
echo "Creating secret: ${KUBE_SECRET}"
|
||||||
|
apply-secret "${KUBE_SECRET}" "${token}" "${user}" "${server}"
|
||||||
|
|
||||||
|
echo "Finished ECR token sync -- $(date)"
|
||||||
|
echo
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# This is a stub resource patched by config-patches.yaml, so that all config is visible in one file
|
||||||
|
---
|
||||||
|
apiVersion: aadpodidentity.k8s.io/v1
|
||||||
|
kind: AzureIdentity
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync # if this is changed, also change in config-patches.yaml
|
||||||
|
namespace: flux-system
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
ACR_NAME: my-registry
|
||||||
|
KUBE_SECRET: acr-my-registry # does not yet exist -- will be created in the same Namespace
|
||||||
|
SYNC_PERIOD: "3600" # ACR tokens expire every 3 hours; refresh faster than that
|
||||||
|
|
||||||
|
# Create an identity in Azure and assign it a role to pull from ACR (note: the identity's resourceGroup should match the desired ACR):
|
||||||
|
# az identity create -n acr-sync
|
||||||
|
# az role assignment create --role AcrPull --assignee-object-id "$(az identity show -n acr-sync -o tsv --query principalId)"
|
||||||
|
# Fetch the clientID and resourceID to configure the AzureIdentity spec below:
|
||||||
|
# az identity show -n acr-sync -otsv --query clientId
|
||||||
|
# az identity show -n acr-sync -otsv --query resourceId
|
||||||
|
---
|
||||||
|
apiVersion: aadpodidentity.k8s.io/v1
|
||||||
|
kind: AzureIdentity
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync # name must match the stub-resource in az-identity.yaml
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
clientID: 82d01fb0-7799-4d9d-92c7-21e7632c0000
|
||||||
|
resourceID: /subscriptions/873c7e7f-76cd-4805-ae86-b923850b0000/resourcegroups/stealthybox/providers/Microsoft.ManagedIdentity/userAssignedIdentities/acr-sync
|
||||||
|
type: 0 # user-managed identity
|
||||||
|
|
||||||
|
# Specify the pod-identity via the aadpodidbinding label
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
aadpodidbinding: $(AZ_IDENTITY_NAME) # match the AzureIdentity name
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- image: bitnami/kubectl
|
||||||
|
name: copy-kubectl
|
||||||
|
# it's okay to do this because kubectl is a statically linked binary
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -ceu
|
||||||
|
- cp $(which kubectl) /kbin/
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- name: kbin
|
||||||
|
mountPath: /kbin
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
volumeMounts:
|
||||||
|
- name: kbin
|
||||||
|
mountPath: /kbin
|
||||||
|
volumes:
|
||||||
|
- name: kbin
|
||||||
|
emptyDir: {}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namePrefix: acr-
|
||||||
|
commonLabels:
|
||||||
|
app: acr-credentials-sync
|
||||||
|
|
||||||
|
namespace: flux-system
|
||||||
|
|
||||||
|
bases:
|
||||||
|
- ../_base
|
||||||
|
resources:
|
||||||
|
- az-identity.yaml
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- config-patches.yaml
|
||||||
|
- kubectl-patch.yaml
|
||||||
|
- reconcile-patch.yaml
|
||||||
|
|
||||||
|
vars:
|
||||||
|
- name: AZ_IDENTITY_NAME
|
||||||
|
objref:
|
||||||
|
kind: AzureIdentity
|
||||||
|
name: credentials-sync
|
||||||
|
apiVersion: aadpodidentity.k8s.io/v1
|
||||||
|
|
||||||
|
configurations:
|
||||||
|
- kustomizeconfig.yaml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
varReference:
|
||||||
|
- path: spec/template/metadata/labels
|
||||||
|
kind: Deployment
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
image: mcr.microsoft.com/azure-cli
|
||||||
|
env:
|
||||||
|
- name: RECONCILE_SH
|
||||||
|
value: |-
|
||||||
|
reconcile() {
|
||||||
|
echo "Starting ACR token sync -- $(date)"
|
||||||
|
echo "Logging into Azure"
|
||||||
|
az login --identity
|
||||||
|
echo "Logging into ACR: ${ACR_NAME}"
|
||||||
|
output="$(az acr login --expose-token -o=tsv -n "${ACR_NAME}")"
|
||||||
|
read token server <<< "${output}"
|
||||||
|
user="00000000-0000-0000-0000-000000000000"
|
||||||
|
|
||||||
|
echo "Creating secret: ${KUBE_SECRET}"
|
||||||
|
apply-secret "${KUBE_SECRET}" "${token}" "${user}" "${server}"
|
||||||
|
|
||||||
|
echo "Finished ECR token sync -- $(date)"
|
||||||
|
echo
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
data:
|
||||||
|
GCR_REGISTRY: gcr.io # set the registry
|
||||||
|
KUBE_SECRET: gcr-credentials # does not yet exist -- will be created in the same Namespace
|
||||||
|
SYNC_PERIOD: "1800" # 30m -- GCR tokens expire every hour; refresh faster than that
|
||||||
|
|
||||||
|
|
||||||
|
# Bind to the GCP service-account
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: <name>@<project-id>.iam.gserviceaccount.com # set the GCP service-account
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namePrefix: gcr-
|
||||||
|
commonLabels:
|
||||||
|
app: gcr-credentials-sync
|
||||||
|
|
||||||
|
namespace: flux-system
|
||||||
|
|
||||||
|
bases:
|
||||||
|
- ../_base
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- config-patches.yaml
|
||||||
|
- reconcile-patch.yaml
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: credentials-sync
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: sync
|
||||||
|
image: aws/aws-cli
|
||||||
|
env:
|
||||||
|
- name: RECONCILE_SH
|
||||||
|
value: |-
|
||||||
|
reconcile() {
|
||||||
|
echo "Starting GCR token sync -- $(date)"
|
||||||
|
echo "Logging into ECR: ${ECR_REGION} -- ${ECR_REGISTRY}"
|
||||||
|
token="$(gcloud auth print-access-token)"
|
||||||
|
user="oauth2accesstoken "
|
||||||
|
server="${GCR_REGISTRY}"
|
||||||
|
|
||||||
|
echo "Creating secret: ${KUBE_SECRET}"
|
||||||
|
apply-secret "${KUBE_SECRET}" "${token}" "${user}" "${server}"
|
||||||
|
|
||||||
|
echo "Finished GCR token sync -- $(date)"
|
||||||
|
echo
|
||||||
|
}
|
||||||
@@ -48,6 +48,7 @@ nav:
|
|||||||
- Get Started: get-started/index.md
|
- Get Started: get-started/index.md
|
||||||
- Migration:
|
- Migration:
|
||||||
- Migrate from Flux v1: guides/flux-v1-migration.md
|
- Migrate from Flux v1: guides/flux-v1-migration.md
|
||||||
|
- Migrate from Flux v1 image update automation: guides/flux-v1-automation-migration.md
|
||||||
- Migrate from the Helm Operator: guides/helm-operator-migration.md
|
- Migrate from the Helm Operator: guides/helm-operator-migration.md
|
||||||
- Guides:
|
- Guides:
|
||||||
- Installation: guides/installation.md
|
- Installation: guides/installation.md
|
||||||
@@ -58,6 +59,7 @@ nav:
|
|||||||
- Sealed Secrets: guides/sealed-secrets.md
|
- Sealed Secrets: guides/sealed-secrets.md
|
||||||
- Mozilla SOPS: guides/mozilla-sops.md
|
- Mozilla SOPS: guides/mozilla-sops.md
|
||||||
- Automate image updates to Git: guides/image-update.md
|
- Automate image updates to Git: guides/image-update.md
|
||||||
|
- Sortable image tags to use with automation: guides/sortable-image-tags.md
|
||||||
- Toolkit Components:
|
- Toolkit Components:
|
||||||
- Overview: components/index.md
|
- Overview: components/index.md
|
||||||
- Source Controller:
|
- Source Controller:
|
||||||
@@ -196,6 +198,7 @@ nav:
|
|||||||
- Uninstall: cmd/flux_uninstall.md
|
- Uninstall: cmd/flux_uninstall.md
|
||||||
- Dev Guides:
|
- Dev Guides:
|
||||||
- Watching for source changes: dev-guides/source-watcher.md
|
- Watching for source changes: dev-guides/source-watcher.md
|
||||||
|
- Advanced debugging: dev-guides/debugging.md
|
||||||
- Roadmap: roadmap/index.md
|
- Roadmap: roadmap/index.md
|
||||||
- Contributing: contributing/index.md
|
- Contributing: contributing/index.md
|
||||||
- FAQ: faq/index.md
|
- FAQ: faq/index.md
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ package install
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
securejoin "github.com/cyphar/filepath-securejoin"
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
|
|
||||||
@@ -75,3 +78,29 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
|||||||
Content: string(content),
|
Content: string(content),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLatestVersion calls the GitHub API and returns the latest released version.
|
||||||
|
func GetLatestVersion() (string, error) {
|
||||||
|
ghURL := "https://api.github.com/repos/fluxcd/flux2/releases/latest"
|
||||||
|
c := http.DefaultClient
|
||||||
|
c.Timeout = 15 * time.Second
|
||||||
|
|
||||||
|
res, err := c.Get(ghURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("calling GitHub API failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Body != nil {
|
||||||
|
defer res.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type meta struct {
|
||||||
|
Tag string `json:"tag_name"`
|
||||||
|
}
|
||||||
|
var m meta
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&m); err != nil {
|
||||||
|
return "", fmt.Errorf("decoding GitHub API response failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Tag, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
func TestGenerate(t *testing.T) {
|
func TestGenerate(t *testing.T) {
|
||||||
opts := MakeDefaultOptions()
|
opts := MakeDefaultOptions()
|
||||||
|
opts.TolerationKeys = []string{"node.kubernetes.io/controllers"}
|
||||||
output, err := Generate(opts)
|
output, err := Generate(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -36,5 +37,9 @@ func TestGenerate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.Content, opts.TolerationKeys[0]) {
|
||||||
|
t.Errorf("toleration key '%s' not found", opts.TolerationKeys[0])
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println(output)
|
fmt.Println(output)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type Options struct {
|
|||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
TargetPath string
|
TargetPath string
|
||||||
ClusterDomain string
|
ClusterDomain string
|
||||||
|
TolerationKeys []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeDefaultOptions() Options {
|
func MakeDefaultOptions() Options {
|
||||||
|
|||||||
@@ -137,6 +137,13 @@ spec:
|
|||||||
imagePullSecrets:
|
imagePullSecrets:
|
||||||
- name: {{.ImagePullSecret}}
|
- name: {{.ImagePullSecret}}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{ if gt (len .TolerationKeys) 0 }}
|
||||||
|
tolerations:
|
||||||
|
{{- range $i, $key := .TolerationKeys }}
|
||||||
|
- key: "{{$key}}"
|
||||||
|
operator: "Exists"
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
`
|
`
|
||||||
|
|
||||||
var labelsTmpl = `---
|
var labelsTmpl = `---
|
||||||
|
|||||||
Reference in New Issue
Block a user