Compare commits
14 Commits
rfc-cli-pl
...
v2.8.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfa461ed21 | ||
|
|
f11a921e0c | ||
|
|
b248efab1d | ||
|
|
4d5e044eb9 | ||
|
|
3c8917ca28 | ||
|
|
c1f11bcf3d | ||
|
|
bc6aa4754c | ||
|
|
0e7194ce77 | ||
|
|
97222a775a | ||
|
|
959025e649 | ||
|
|
75f5e33fd1 | ||
|
|
79cae5d033 | ||
|
|
41505fd5f3 | ||
|
|
2042ddf849 |
6
.github/labels.yaml
vendored
6
.github/labels.yaml
vendored
@@ -44,12 +44,12 @@
|
|||||||
description: Feature request proposals in the RFC format
|
description: Feature request proposals in the RFC format
|
||||||
color: '#D621C3'
|
color: '#D621C3'
|
||||||
aliases: ['area/RFC']
|
aliases: ['area/RFC']
|
||||||
|
- name: backport:release/v2.5.x
|
||||||
|
description: To be backported to release/v2.5.x
|
||||||
|
color: '#ffd700'
|
||||||
- name: backport:release/v2.6.x
|
- name: backport:release/v2.6.x
|
||||||
description: To be backported to release/v2.6.x
|
description: To be backported to release/v2.6.x
|
||||||
color: '#ffd700'
|
color: '#ffd700'
|
||||||
- name: backport:release/v2.7.x
|
- name: backport:release/v2.7.x
|
||||||
description: To be backported to release/v2.7.x
|
description: To be backported to release/v2.7.x
|
||||||
color: '#ffd700'
|
color: '#ffd700'
|
||||||
- name: backport:release/v2.8.x
|
|
||||||
description: To be backported to release/v2.8.x
|
|
||||||
color: '#ffd700'
|
|
||||||
|
|||||||
2
.github/workflows/README.md
vendored
2
.github/workflows/README.md
vendored
@@ -23,7 +23,7 @@ amd when it finds a new controller version, the workflow performs the following
|
|||||||
- Updates the controller API package version in `go.mod`.
|
- Updates the controller API package version in `go.mod`.
|
||||||
- Patches the controller CRDs version in the `manifests/crds` overlay.
|
- Patches the controller CRDs version in the `manifests/crds` overlay.
|
||||||
- Patches the controller Deployment version in `manifests/bases` overlay.
|
- Patches the controller Deployment version in `manifests/bases` overlay.
|
||||||
- Opens a Pull Request against the checked out branch.
|
- Opens a Pull Request against the `main` branch.
|
||||||
- Triggers the e2e test suite to run for the opened PR.
|
- Triggers the e2e test suite to run for the opened PR.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/conformance.yaml
vendored
2
.github/workflows/conformance.yaml
vendored
@@ -3,7 +3,7 @@ name: conformance
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ 'main', 'update-components-**', 'release/**', 'conform*' ]
|
branches: [ 'main', 'update-components', 'release/**', 'conform*' ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
2
.github/workflows/update.yaml
vendored
2
.github/workflows/update.yaml
vendored
@@ -106,7 +106,7 @@ jobs:
|
|||||||
committer: GitHub <noreply@github.com>
|
committer: GitHub <noreply@github.com>
|
||||||
author: fluxcdbot <fluxcdbot@users.noreply.github.com>
|
author: fluxcdbot <fluxcdbot@users.noreply.github.com>
|
||||||
signoff: true
|
signoff: true
|
||||||
branch: update-components-${{ github.ref_name }}
|
branch: update-components
|
||||||
title: Update toolkit components
|
title: Update toolkit components
|
||||||
body: |
|
body: |
|
||||||
${{ steps.update.outputs.pr_body }}
|
${{ steps.update.outputs.pr_body }}
|
||||||
|
|||||||
3
.github/workflows/upgrade-fluxcd-pkg.yaml
vendored
3
.github/workflows/upgrade-fluxcd-pkg.yaml
vendored
@@ -3,9 +3,6 @@ name: upgrade-fluxcd-pkg
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upgrade-fluxcd-pkg:
|
upgrade-fluxcd-pkg:
|
||||||
uses: fluxcd/gha-workflows/.github/workflows/upgrade-fluxcd-pkg.yaml@v0.9.0
|
uses: fluxcd/gha-workflows/.github/workflows/upgrade-fluxcd-pkg.yaml@v0.9.0
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -49,10 +48,9 @@ from the given directory or a single manifest file.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
type buildArtifactFlags struct {
|
type buildArtifactFlags struct {
|
||||||
output string
|
output string
|
||||||
path string
|
path string
|
||||||
ignorePaths []string
|
ignorePaths []string
|
||||||
resolveSymlinks bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var excludeOCI = append(strings.Split(sourceignore.ExcludeVCS, ","), strings.Split(sourceignore.ExcludeExt, ",")...)
|
var excludeOCI = append(strings.Split(sourceignore.ExcludeVCS, ","), strings.Split(sourceignore.ExcludeExt, ",")...)
|
||||||
@@ -63,7 +61,6 @@ func init() {
|
|||||||
buildArtifactCmd.Flags().StringVarP(&buildArtifactArgs.path, "path", "p", "", "Path to the directory where the Kubernetes manifests are located.")
|
buildArtifactCmd.Flags().StringVarP(&buildArtifactArgs.path, "path", "p", "", "Path to the directory where the Kubernetes manifests are located.")
|
||||||
buildArtifactCmd.Flags().StringVarP(&buildArtifactArgs.output, "output", "o", "artifact.tgz", "Path to where the artifact tgz file should be written.")
|
buildArtifactCmd.Flags().StringVarP(&buildArtifactArgs.output, "output", "o", "artifact.tgz", "Path to where the artifact tgz file should be written.")
|
||||||
buildArtifactCmd.Flags().StringSliceVar(&buildArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format")
|
buildArtifactCmd.Flags().StringSliceVar(&buildArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format")
|
||||||
buildArtifactCmd.Flags().BoolVar(&buildArtifactArgs.resolveSymlinks, "resolve-symlinks", false, "resolve symlinks by copying their targets into the artifact")
|
|
||||||
|
|
||||||
buildCmd.AddCommand(buildArtifactCmd)
|
buildCmd.AddCommand(buildArtifactCmd)
|
||||||
}
|
}
|
||||||
@@ -88,15 +85,6 @@ func buildArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("invalid path '%s', must point to an existing directory or file", path)
|
return fmt.Errorf("invalid path '%s', must point to an existing directory or file", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if buildArtifactArgs.resolveSymlinks {
|
|
||||||
resolved, cleanupDir, err := resolveSymlinks(path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("resolving symlinks failed: %w", err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(cleanupDir)
|
|
||||||
path = resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Actionf("building artifact from %s", path)
|
logger.Actionf("building artifact from %s", path)
|
||||||
|
|
||||||
ociClient := oci.NewClient(oci.DefaultOptions())
|
ociClient := oci.NewClient(oci.DefaultOptions())
|
||||||
@@ -108,141 +96,6 @@ func buildArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveSymlinks creates a temporary directory with symlinks resolved to their
|
|
||||||
// real file contents. This allows building artifacts from symlink trees (e.g.,
|
|
||||||
// those created by Nix) where the actual files live outside the source directory.
|
|
||||||
// It returns the resolved path and the temporary directory path for cleanup.
|
|
||||||
func resolveSymlinks(srcPath string) (string, string, error) {
|
|
||||||
absPath, err := filepath.Abs(srcPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := os.Stat(absPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// For a single file, resolve the symlink and return the path to the
|
|
||||||
// copied file within the temp dir, preserving file semantics for callers.
|
|
||||||
if !info.IsDir() {
|
|
||||||
resolved, err := filepath.EvalSymlinks(absPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("resolving symlink for %s: %w", absPath, err)
|
|
||||||
}
|
|
||||||
tmpDir, err := os.MkdirTemp("", "flux-artifact-*")
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
dst := filepath.Join(tmpDir, filepath.Base(absPath))
|
|
||||||
if err := copyFile(resolved, dst); err != nil {
|
|
||||||
os.RemoveAll(tmpDir)
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
return dst, tmpDir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir, err := os.MkdirTemp("", "flux-artifact-*")
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
visited := make(map[string]bool)
|
|
||||||
if err := copyDir(absPath, tmpDir, visited); err != nil {
|
|
||||||
os.RemoveAll(tmpDir)
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmpDir, tmpDir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// copyDir recursively copies the contents of srcDir to dstDir, resolving any
|
|
||||||
// symlinks encountered along the way. The visited map tracks resolved real
|
|
||||||
// directory paths to detect and break symlink cycles.
|
|
||||||
func copyDir(srcDir, dstDir string, visited map[string]bool) error {
|
|
||||||
real, err := filepath.EvalSymlinks(srcDir)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("resolving symlink %s: %w", srcDir, err)
|
|
||||||
}
|
|
||||||
abs, err := filepath.Abs(real)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("getting absolute path for %s: %w", real, err)
|
|
||||||
}
|
|
||||||
if visited[abs] {
|
|
||||||
return nil // break the cycle
|
|
||||||
}
|
|
||||||
visited[abs] = true
|
|
||||||
|
|
||||||
entries, err := os.ReadDir(srcDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range entries {
|
|
||||||
srcPath := filepath.Join(srcDir, entry.Name())
|
|
||||||
dstPath := filepath.Join(dstDir, entry.Name())
|
|
||||||
|
|
||||||
// Resolve symlinks to get the real path and info.
|
|
||||||
realPath, err := filepath.EvalSymlinks(srcPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("resolving symlink %s: %w", srcPath, err)
|
|
||||||
}
|
|
||||||
realInfo, err := os.Stat(realPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("stat resolved path %s: %w", realPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if realInfo.IsDir() {
|
|
||||||
if err := os.MkdirAll(dstPath, realInfo.Mode()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Recursively copy the resolved directory contents.
|
|
||||||
if err := copyDir(realPath, dstPath, visited); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !realInfo.Mode().IsRegular() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := copyFile(realPath, dstPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFile(src, dst string) error {
|
|
||||||
srcInfo, err := os.Stat(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
in, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer in.Close()
|
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcInfo.Mode())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
if _, err := io.Copy(out, in); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return out.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveReaderToFile(reader io.Reader) (string, error) {
|
func saveReaderToFile(reader io.Reader) (string, error) {
|
||||||
b, err := io.ReadAll(bufio.NewReader(reader))
|
b, err := io.ReadAll(bufio.NewReader(reader))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -69,113 +68,3 @@ data:
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_resolveSymlinks(t *testing.T) {
|
|
||||||
g := NewWithT(t)
|
|
||||||
|
|
||||||
// Create source directory with a real file
|
|
||||||
srcDir := t.TempDir()
|
|
||||||
realFile := filepath.Join(srcDir, "real.yaml")
|
|
||||||
g.Expect(os.WriteFile(realFile, []byte("apiVersion: v1\nkind: Namespace\nmetadata:\n name: test\n"), 0o644)).To(Succeed())
|
|
||||||
|
|
||||||
// Create a directory with symlinks pointing to files outside it
|
|
||||||
symlinkDir := t.TempDir()
|
|
||||||
symlinkFile := filepath.Join(symlinkDir, "linked.yaml")
|
|
||||||
g.Expect(os.Symlink(realFile, symlinkFile)).To(Succeed())
|
|
||||||
|
|
||||||
// Also add a regular file in the symlink dir
|
|
||||||
regularFile := filepath.Join(symlinkDir, "regular.yaml")
|
|
||||||
g.Expect(os.WriteFile(regularFile, []byte("apiVersion: v1\nkind: ConfigMap\n"), 0o644)).To(Succeed())
|
|
||||||
|
|
||||||
// Create a symlinked subdirectory
|
|
||||||
subDir := filepath.Join(srcDir, "subdir")
|
|
||||||
g.Expect(os.MkdirAll(subDir, 0o755)).To(Succeed())
|
|
||||||
g.Expect(os.WriteFile(filepath.Join(subDir, "nested.yaml"), []byte("nested"), 0o644)).To(Succeed())
|
|
||||||
g.Expect(os.Symlink(subDir, filepath.Join(symlinkDir, "linkeddir"))).To(Succeed())
|
|
||||||
|
|
||||||
// Resolve symlinks
|
|
||||||
resolved, cleanupDir, err := resolveSymlinks(symlinkDir)
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
t.Cleanup(func() { os.RemoveAll(cleanupDir) })
|
|
||||||
|
|
||||||
// Verify the regular file was copied
|
|
||||||
content, err := os.ReadFile(filepath.Join(resolved, "regular.yaml"))
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
g.Expect(string(content)).To(Equal("apiVersion: v1\nkind: ConfigMap\n"))
|
|
||||||
|
|
||||||
// Verify the symlinked file was resolved and copied
|
|
||||||
content, err = os.ReadFile(filepath.Join(resolved, "linked.yaml"))
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
g.Expect(string(content)).To(ContainSubstring("kind: Namespace"))
|
|
||||||
|
|
||||||
// Verify that the resolved file is a regular file, not a symlink
|
|
||||||
info, err := os.Lstat(filepath.Join(resolved, "linked.yaml"))
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
g.Expect(info.Mode().IsRegular()).To(BeTrue())
|
|
||||||
|
|
||||||
// Verify that the symlinked directory was resolved and its contents were copied
|
|
||||||
content, err = os.ReadFile(filepath.Join(resolved, "linkeddir", "nested.yaml"))
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
g.Expect(string(content)).To(Equal("nested"))
|
|
||||||
|
|
||||||
// Verify that the file inside the symlinked directory is a regular file
|
|
||||||
info, err = os.Lstat(filepath.Join(resolved, "linkeddir", "nested.yaml"))
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
g.Expect(info.Mode().IsRegular()).To(BeTrue())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_resolveSymlinks_singleFile(t *testing.T) {
|
|
||||||
g := NewWithT(t)
|
|
||||||
|
|
||||||
// Create a real file
|
|
||||||
srcDir := t.TempDir()
|
|
||||||
realFile := filepath.Join(srcDir, "manifest.yaml")
|
|
||||||
g.Expect(os.WriteFile(realFile, []byte("kind: ConfigMap"), 0o644)).To(Succeed())
|
|
||||||
|
|
||||||
// Create a symlink to the real file
|
|
||||||
linkDir := t.TempDir()
|
|
||||||
linkFile := filepath.Join(linkDir, "link.yaml")
|
|
||||||
g.Expect(os.Symlink(realFile, linkFile)).To(Succeed())
|
|
||||||
|
|
||||||
// Resolve the single symlinked file
|
|
||||||
resolved, cleanupDir, err := resolveSymlinks(linkFile)
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
t.Cleanup(func() { os.RemoveAll(cleanupDir) })
|
|
||||||
|
|
||||||
// The returned path should be a file, not a directory
|
|
||||||
info, err := os.Stat(resolved)
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
g.Expect(info.IsDir()).To(BeFalse())
|
|
||||||
|
|
||||||
// Verify contents
|
|
||||||
content, err := os.ReadFile(resolved)
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
g.Expect(string(content)).To(Equal("kind: ConfigMap"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_resolveSymlinks_cycle(t *testing.T) {
|
|
||||||
g := NewWithT(t)
|
|
||||||
|
|
||||||
// Create a directory with a symlink cycle: dir/link -> dir
|
|
||||||
dir := t.TempDir()
|
|
||||||
g.Expect(os.WriteFile(filepath.Join(dir, "file.yaml"), []byte("data"), 0o644)).To(Succeed())
|
|
||||||
g.Expect(os.Symlink(dir, filepath.Join(dir, "cycle"))).To(Succeed())
|
|
||||||
|
|
||||||
// resolveSymlinks should not infinite-loop
|
|
||||||
resolved, cleanupDir, err := resolveSymlinks(dir)
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
t.Cleanup(func() { os.RemoveAll(cleanupDir) })
|
|
||||||
|
|
||||||
// The file should be copied
|
|
||||||
content, err := os.ReadFile(filepath.Join(resolved, "file.yaml"))
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
g.Expect(string(content)).To(Equal("data"))
|
|
||||||
|
|
||||||
// The cycle directory should exist but not cause infinite nesting
|
|
||||||
_, err = os.Stat(filepath.Join(resolved, "cycle"))
|
|
||||||
g.Expect(err).To(BeNil())
|
|
||||||
|
|
||||||
// There should NOT be deeply nested cycle/cycle/cycle/... paths
|
|
||||||
_, err = os.Stat(filepath.Join(resolved, "cycle", "cycle", "cycle"))
|
|
||||||
g.Expect(os.IsNotExist(err)).To(BeTrue())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -103,18 +103,17 @@ The command can read the credentials from '~/.docker/config.json' but they can a
|
|||||||
}
|
}
|
||||||
|
|
||||||
type pushArtifactFlags struct {
|
type pushArtifactFlags struct {
|
||||||
path string
|
path string
|
||||||
source string
|
source string
|
||||||
revision string
|
revision string
|
||||||
creds string
|
creds string
|
||||||
provider flags.SourceOCIProvider
|
provider flags.SourceOCIProvider
|
||||||
ignorePaths []string
|
ignorePaths []string
|
||||||
annotations []string
|
annotations []string
|
||||||
output string
|
output string
|
||||||
debug bool
|
debug bool
|
||||||
reproducible bool
|
reproducible bool
|
||||||
insecure bool
|
insecure bool
|
||||||
resolveSymlinks bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var pushArtifactArgs = newPushArtifactFlags()
|
var pushArtifactArgs = newPushArtifactFlags()
|
||||||
@@ -138,7 +137,6 @@ func init() {
|
|||||||
pushArtifactCmd.Flags().BoolVarP(&pushArtifactArgs.debug, "debug", "", false, "display logs from underlying library")
|
pushArtifactCmd.Flags().BoolVarP(&pushArtifactArgs.debug, "debug", "", false, "display logs from underlying library")
|
||||||
pushArtifactCmd.Flags().BoolVar(&pushArtifactArgs.reproducible, "reproducible", false, "ensure reproducible image digests by setting the created timestamp to '1970-01-01T00:00:00Z'")
|
pushArtifactCmd.Flags().BoolVar(&pushArtifactArgs.reproducible, "reproducible", false, "ensure reproducible image digests by setting the created timestamp to '1970-01-01T00:00:00Z'")
|
||||||
pushArtifactCmd.Flags().BoolVar(&pushArtifactArgs.insecure, "insecure-registry", false, "allows artifacts to be pushed without TLS")
|
pushArtifactCmd.Flags().BoolVar(&pushArtifactArgs.insecure, "insecure-registry", false, "allows artifacts to be pushed without TLS")
|
||||||
pushArtifactCmd.Flags().BoolVar(&pushArtifactArgs.resolveSymlinks, "resolve-symlinks", false, "resolve symlinks by copying their targets into the artifact")
|
|
||||||
|
|
||||||
pushCmd.AddCommand(pushArtifactCmd)
|
pushCmd.AddCommand(pushArtifactCmd)
|
||||||
}
|
}
|
||||||
@@ -185,15 +183,6 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("invalid path '%s', must point to an existing directory or file: %w", path, err)
|
return fmt.Errorf("invalid path '%s', must point to an existing directory or file: %w", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pushArtifactArgs.resolveSymlinks {
|
|
||||||
resolved, cleanupDir, err := resolveSymlinks(path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("resolving symlinks failed: %w", err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(cleanupDir)
|
|
||||||
path = resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
annotations := map[string]string{}
|
annotations := map[string]string{}
|
||||||
for _, annotation := range pushArtifactArgs.annotations {
|
for _, annotation := range pushArtifactArgs.annotations {
|
||||||
kv := strings.Split(annotation, "=")
|
kv := strings.Split(annotation, "=")
|
||||||
|
|||||||
@@ -152,14 +152,7 @@ func reconciliationHandled(kubeClient client.Client, namespacedName types.Namesp
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch result.Status {
|
return result.Status == kstatus.CurrentStatus, nil
|
||||||
case kstatus.CurrentStatus:
|
|
||||||
return true, nil
|
|
||||||
case kstatus.InProgressStatus:
|
|
||||||
return false, nil
|
|
||||||
default:
|
|
||||||
return false, fmt.Errorf("%s", result.Message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,17 +126,6 @@ func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
resume.printMessage(reconcileResps)
|
resume.printMessage(reconcileResps)
|
||||||
|
|
||||||
// Return an error if any reconciliation failed
|
|
||||||
var failedCount int
|
|
||||||
for _, r := range reconcileResps {
|
|
||||||
if r.resumable != nil && r.err != nil {
|
|
||||||
failedCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if failedCount > 0 {
|
|
||||||
return fmt.Errorf("reconciliation failed for %d %s(s)", failedCount, resume.kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ The following template can be used for the GitHub release page:
|
|||||||
|
|
||||||
<!-- Text describing the most important changes in this release -->
|
<!-- Text describing the most important changes in this release -->
|
||||||
|
|
||||||
ℹ️ Please follow the [Upgrade Procedure for Flux v2.7+](https://github.com/fluxcd/flux2/discussions/5572) for a smooth upgrade from Flux v2.6 to the latest version.
|
|
||||||
|
|
||||||
### Fixes and improvements
|
### Fixes and improvements
|
||||||
|
|
||||||
<!-- List of fixes and improvements to the controllers and CLI -->
|
<!-- List of fixes and improvements to the controllers and CLI -->
|
||||||
@@ -38,7 +36,7 @@ The following template can be used for the GitHub release page:
|
|||||||
|
|
||||||
## Components changelog
|
## Components changelog
|
||||||
|
|
||||||
- <name>-controller [v<version>](https://github.com/fluxcd/<name>-controller/blob/<version>/CHANGELOG.md)
|
- <name>-controller [v<version>](https://github.com/fluxcd/<name>-controller/blob/<version>/CHANGELOG.md
|
||||||
|
|
||||||
## CLI changelog
|
## CLI changelog
|
||||||
|
|
||||||
|
|||||||
24
go.mod
24
go.mod
@@ -12,25 +12,25 @@ require (
|
|||||||
github.com/distribution/distribution/v3 v3.0.0
|
github.com/distribution/distribution/v3 v3.0.0
|
||||||
github.com/fluxcd/cli-utils v0.37.2-flux.1
|
github.com/fluxcd/cli-utils v0.37.2-flux.1
|
||||||
github.com/fluxcd/go-git-providers v0.26.0
|
github.com/fluxcd/go-git-providers v0.26.0
|
||||||
github.com/fluxcd/helm-controller/api v1.5.3
|
github.com/fluxcd/helm-controller/api v1.5.2
|
||||||
github.com/fluxcd/image-automation-controller/api v1.1.1
|
github.com/fluxcd/image-automation-controller/api v1.1.1
|
||||||
github.com/fluxcd/image-reflector-controller/api v1.1.1
|
github.com/fluxcd/image-reflector-controller/api v1.1.1
|
||||||
github.com/fluxcd/kustomize-controller/api v1.8.2
|
github.com/fluxcd/kustomize-controller/api v1.8.2
|
||||||
github.com/fluxcd/notification-controller/api v1.8.2
|
github.com/fluxcd/notification-controller/api v1.8.2
|
||||||
github.com/fluxcd/pkg/apis/event v0.25.0
|
github.com/fluxcd/pkg/apis/event v0.24.1
|
||||||
github.com/fluxcd/pkg/apis/meta v1.26.0
|
github.com/fluxcd/pkg/apis/meta v1.25.1
|
||||||
github.com/fluxcd/pkg/auth v0.40.0
|
github.com/fluxcd/pkg/auth v0.38.4
|
||||||
github.com/fluxcd/pkg/chartutil v1.23.0
|
github.com/fluxcd/pkg/chartutil v1.22.1
|
||||||
github.com/fluxcd/pkg/envsubst v1.5.0
|
github.com/fluxcd/pkg/envsubst v1.5.0
|
||||||
github.com/fluxcd/pkg/git v0.46.0
|
github.com/fluxcd/pkg/git v0.43.1
|
||||||
github.com/fluxcd/pkg/kustomize v1.28.0
|
github.com/fluxcd/pkg/kustomize v1.27.1
|
||||||
github.com/fluxcd/pkg/oci v0.63.0
|
github.com/fluxcd/pkg/oci v0.60.1
|
||||||
github.com/fluxcd/pkg/runtime v0.103.0
|
github.com/fluxcd/pkg/runtime v0.100.4
|
||||||
github.com/fluxcd/pkg/sourceignore v0.17.0
|
github.com/fluxcd/pkg/sourceignore v0.17.0
|
||||||
github.com/fluxcd/pkg/ssa v0.70.0
|
github.com/fluxcd/pkg/ssa v0.67.3
|
||||||
github.com/fluxcd/pkg/ssh v0.24.0
|
github.com/fluxcd/pkg/ssh v0.24.0
|
||||||
github.com/fluxcd/pkg/tar v0.17.0
|
github.com/fluxcd/pkg/tar v0.17.0
|
||||||
github.com/fluxcd/pkg/version v0.14.0
|
github.com/fluxcd/pkg/version v0.12.0
|
||||||
github.com/fluxcd/source-controller/api v1.8.1
|
github.com/fluxcd/source-controller/api v1.8.1
|
||||||
github.com/fluxcd/source-watcher/api/v2 v2.1.1
|
github.com/fluxcd/source-watcher/api/v2 v2.1.1
|
||||||
github.com/go-git/go-git/v5 v5.16.5
|
github.com/go-git/go-git/v5 v5.16.5
|
||||||
@@ -128,7 +128,7 @@ require (
|
|||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fluxcd/pkg/apis/acl v0.9.0 // indirect
|
github.com/fluxcd/pkg/apis/acl v0.9.0 // indirect
|
||||||
github.com/fluxcd/pkg/apis/kustomize v1.16.0 // indirect
|
github.com/fluxcd/pkg/apis/kustomize v1.15.1 // indirect
|
||||||
github.com/fluxcd/pkg/cache v0.13.0 // indirect
|
github.com/fluxcd/pkg/cache v0.13.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||||
|
|||||||
52
go.sum
52
go.sum
@@ -174,8 +174,8 @@ github.com/fluxcd/gitkit v0.6.0 h1:iNg5LTx6ePo+Pl0ZwqHTAkhbUHxGVSY3YCxCdw7VIFg=
|
|||||||
github.com/fluxcd/gitkit v0.6.0/go.mod h1:svOHuKi0fO9HoawdK4HfHAJJseZDHHjk7I3ihnCIqNo=
|
github.com/fluxcd/gitkit v0.6.0/go.mod h1:svOHuKi0fO9HoawdK4HfHAJJseZDHHjk7I3ihnCIqNo=
|
||||||
github.com/fluxcd/go-git-providers v0.26.0 h1:0DUsXc1nS9Fe4n8tXSEUCGemWzHShd66gmotayDPekw=
|
github.com/fluxcd/go-git-providers v0.26.0 h1:0DUsXc1nS9Fe4n8tXSEUCGemWzHShd66gmotayDPekw=
|
||||||
github.com/fluxcd/go-git-providers v0.26.0/go.mod h1:VJDKUOhZwNAIqDF5iPtIpTr/annsDbKMkPpWiDMBdpo=
|
github.com/fluxcd/go-git-providers v0.26.0/go.mod h1:VJDKUOhZwNAIqDF5iPtIpTr/annsDbKMkPpWiDMBdpo=
|
||||||
github.com/fluxcd/helm-controller/api v1.5.3 h1:ruLzuyTHjjE9A5B/U+Id2q7yHXXqSFTswdZ14xCS5So=
|
github.com/fluxcd/helm-controller/api v1.5.2 h1:3xeFPcsvibingV32y8MRj040ydZIPOMCTToMXOkhtUo=
|
||||||
github.com/fluxcd/helm-controller/api v1.5.3/go.mod h1:lTgeUmtVYExMKp7mRDncsr4JwHTz3LFtLjRJZeR98lI=
|
github.com/fluxcd/helm-controller/api v1.5.2/go.mod h1:lTgeUmtVYExMKp7mRDncsr4JwHTz3LFtLjRJZeR98lI=
|
||||||
github.com/fluxcd/image-automation-controller/api v1.1.1 h1:uiu7kjdVoW8/461HOemX6I7RcPornEzQliWgTg6LnWI=
|
github.com/fluxcd/image-automation-controller/api v1.1.1 h1:uiu7kjdVoW8/461HOemX6I7RcPornEzQliWgTg6LnWI=
|
||||||
github.com/fluxcd/image-automation-controller/api v1.1.1/go.mod h1:lkD/drkD6Wc+2SDjVj5KqfozEucTLFexWgby/5ft660=
|
github.com/fluxcd/image-automation-controller/api v1.1.1/go.mod h1:lkD/drkD6Wc+2SDjVj5KqfozEucTLFexWgby/5ft660=
|
||||||
github.com/fluxcd/image-reflector-controller/api v1.1.1 h1:4Bj1abzVnjj8+b/293kNeFMRJc+y2wO8Z12ReZ/gA0w=
|
github.com/fluxcd/image-reflector-controller/api v1.1.1 h1:4Bj1abzVnjj8+b/293kNeFMRJc+y2wO8Z12ReZ/gA0w=
|
||||||
@@ -186,40 +186,40 @@ github.com/fluxcd/notification-controller/api v1.8.2 h1:TDrXohUC5Gh3BF+v2ux9/zEG
|
|||||||
github.com/fluxcd/notification-controller/api v1.8.2/go.mod h1:ozgJGQPy0dG5eOsLZlwAr6n0q/y6+TWd1fGOtavlXJA=
|
github.com/fluxcd/notification-controller/api v1.8.2/go.mod h1:ozgJGQPy0dG5eOsLZlwAr6n0q/y6+TWd1fGOtavlXJA=
|
||||||
github.com/fluxcd/pkg/apis/acl v0.9.0 h1:wBpgsKT+jcyZEcM//OmZr9RiF8klL3ebrDp2u2ThsnA=
|
github.com/fluxcd/pkg/apis/acl v0.9.0 h1:wBpgsKT+jcyZEcM//OmZr9RiF8klL3ebrDp2u2ThsnA=
|
||||||
github.com/fluxcd/pkg/apis/acl v0.9.0/go.mod h1:TttNS+gocsGLwnvmgVi3/Yscwqrjc17+vhgYfqkfrV4=
|
github.com/fluxcd/pkg/apis/acl v0.9.0/go.mod h1:TttNS+gocsGLwnvmgVi3/Yscwqrjc17+vhgYfqkfrV4=
|
||||||
github.com/fluxcd/pkg/apis/event v0.25.0 h1:zdwytvDhG+fk+Ywl5DOtv7TklkrVgM21WHm1f+YhleE=
|
github.com/fluxcd/pkg/apis/event v0.24.1 h1:TClVdn02aiq3sAl9BuzLjjTIxm3JJ83fJ9nchtBa4qg=
|
||||||
github.com/fluxcd/pkg/apis/event v0.25.0/go.mod h1:TlK8HWYrTwl0raqBRC+ROoNpYW5fdVnwcwOBOx5Kzw8=
|
github.com/fluxcd/pkg/apis/event v0.24.1/go.mod h1:TlK8HWYrTwl0raqBRC+ROoNpYW5fdVnwcwOBOx5Kzw8=
|
||||||
github.com/fluxcd/pkg/apis/kustomize v1.16.0 h1:PhWXEhqQqsisIpwp1/wHvTvo+MO+GGzsBPoN0ZnRE3Y=
|
github.com/fluxcd/pkg/apis/kustomize v1.15.1 h1:t9QZh+3ZS8EKmlxrnnbcKZcGTrg8FDvMF1T8BHMCuqI=
|
||||||
github.com/fluxcd/pkg/apis/kustomize v1.16.0/go.mod h1:IZOy4CCtR/hxMGb7erK1RfbGnczVv4/dRBoVD37AywI=
|
github.com/fluxcd/pkg/apis/kustomize v1.15.1/go.mod h1:IZOy4CCtR/hxMGb7erK1RfbGnczVv4/dRBoVD37AywI=
|
||||||
github.com/fluxcd/pkg/apis/meta v1.26.0 h1:dxP1FfBpTCYso6odzRcltVnnRuBb2VyhhgV0VX9YbUE=
|
github.com/fluxcd/pkg/apis/meta v1.25.1 h1:WG1GIC/SOz0GjxT0uVuO6AMicQ3yFsk6bDozCnq+fto=
|
||||||
github.com/fluxcd/pkg/apis/meta v1.26.0/go.mod h1:c7o6mJGLCMvNrfdinGZehkrdZuFT9vZdZNrn66DtVD0=
|
github.com/fluxcd/pkg/apis/meta v1.25.1/go.mod h1:c7o6mJGLCMvNrfdinGZehkrdZuFT9vZdZNrn66DtVD0=
|
||||||
github.com/fluxcd/pkg/auth v0.40.0 h1:p6Kw6KH+z8oRqngKhmTt8ILKD/rC+8tP87a//kLZhi8=
|
github.com/fluxcd/pkg/auth v0.38.4 h1:xVsJ1rakUm5zS2tOKguZOQc5g6wLgCNxW2a9exidd4M=
|
||||||
github.com/fluxcd/pkg/auth v0.40.0/go.mod h1:Oq/hIEKUMTbL2bv5blf+EhC/jXXJLsOjIMtJj/AtG3Y=
|
github.com/fluxcd/pkg/auth v0.38.4/go.mod h1:KTXOh770ukcyQfC8NavEFzm110ORSQRan0v/kjzgFXs=
|
||||||
github.com/fluxcd/pkg/cache v0.13.0 h1:MqtlgOwIVcGKKgV422e39O+KFSVMWuExKeRaMDBjJlk=
|
github.com/fluxcd/pkg/cache v0.13.0 h1:MqtlgOwIVcGKKgV422e39O+KFSVMWuExKeRaMDBjJlk=
|
||||||
github.com/fluxcd/pkg/cache v0.13.0/go.mod h1:0xRZ1hitrIFQ6pl68ke2wZLbIqA2VLzY78HpDo9DVxs=
|
github.com/fluxcd/pkg/cache v0.13.0/go.mod h1:0xRZ1hitrIFQ6pl68ke2wZLbIqA2VLzY78HpDo9DVxs=
|
||||||
github.com/fluxcd/pkg/chartutil v1.23.0 h1:ohstQEVnrBIbN85FGu83hnmAohLl0PdOoPlsM6+cjyI=
|
github.com/fluxcd/pkg/chartutil v1.22.1 h1:ufI9LJ4d5T79h9ruBQRoRcSmuI/KkcwEqWdxu/9Xub8=
|
||||||
github.com/fluxcd/pkg/chartutil v1.23.0/go.mod h1:kFhmD6DwBgRsvC1ilINsomargMi2WbqvSndWQLikkLc=
|
github.com/fluxcd/pkg/chartutil v1.22.1/go.mod h1:4/2mpNLyfox3uey++hG21AePPsMWekdhSWAtSdDiubQ=
|
||||||
github.com/fluxcd/pkg/envsubst v1.5.0 h1:S07mo+MkGhptdHA4pRze5HPKlc8tHxKswNdcMZi1WDY=
|
github.com/fluxcd/pkg/envsubst v1.5.0 h1:S07mo+MkGhptdHA4pRze5HPKlc8tHxKswNdcMZi1WDY=
|
||||||
github.com/fluxcd/pkg/envsubst v1.5.0/go.mod h1:c3a8DYI855sZUubHFYQbjfjop6Wu4/zg1cLyf7SnCes=
|
github.com/fluxcd/pkg/envsubst v1.5.0/go.mod h1:c3a8DYI855sZUubHFYQbjfjop6Wu4/zg1cLyf7SnCes=
|
||||||
github.com/fluxcd/pkg/git v0.46.0 h1:QMh0+ZzQ2jO6rIGj4ffR5trZ8g/cxvt8cVajReJ8Iyw=
|
github.com/fluxcd/pkg/git v0.43.1 h1:lw29P44wueKzQk79KnYyvisfw//cxg0S4cDeTYx+Slo=
|
||||||
github.com/fluxcd/pkg/git v0.46.0/go.mod h1:iHcIjx9c8zye3PQiajTJYxgOMRiy7WCs+hfLKDswpfI=
|
github.com/fluxcd/pkg/git v0.43.1/go.mod h1:3R/AjCe7ee7FqWcAG+2IiuJPOCxrGHF4SCGkuvKS6OQ=
|
||||||
github.com/fluxcd/pkg/gittestserver v0.26.0 h1:+RZrCzFRsE+d5WaqAoqaPCEgcgv/jZp6+f7DS0+Ynb8=
|
github.com/fluxcd/pkg/gittestserver v0.25.1 h1:40Ridmy1xKxBM9ItDn012R4VKmaoDqzvGaC5g7xv+mw=
|
||||||
github.com/fluxcd/pkg/gittestserver v0.26.0/go.mod h1:7fybYb0yej1fFNiF1ohs0Jr0XzyaZQ/cRh3AFEoCtuc=
|
github.com/fluxcd/pkg/gittestserver v0.25.1/go.mod h1:7fybYb0yej1fFNiF1ohs0Jr0XzyaZQ/cRh3AFEoCtuc=
|
||||||
github.com/fluxcd/pkg/kustomize v1.28.0 h1:0RuFVczJRabbt8frHZ/ql8aqte6BOOKk274O09l6/hE=
|
github.com/fluxcd/pkg/kustomize v1.27.1 h1:BLOBNLb2N5ObttZA8XJhZ2NqNY1ZjBqQtTpNlIx8/L4=
|
||||||
github.com/fluxcd/pkg/kustomize v1.28.0/go.mod h1:cW08mnngSP8MJYb6mDmMvxH8YjNATdiML0udb37dk+M=
|
github.com/fluxcd/pkg/kustomize v1.27.1/go.mod h1:A2RQTe9woDPiwJDWFlkoP4oF9eX9DeXr89FEkKnSObk=
|
||||||
github.com/fluxcd/pkg/oci v0.63.0 h1:ZPKTT2C+gWYjhP63xC76iTPdYE9w3ABcsDq77uhAgwo=
|
github.com/fluxcd/pkg/oci v0.60.1 h1:mT6WBX+MBIcczzEnw/W4cfXyt5JSRNhRoB/UnJ72K6M=
|
||||||
github.com/fluxcd/pkg/oci v0.63.0/go.mod h1:qMPz4njvm6hJzdyGSb8ydSqrapXxTQwJonxHIsdeXSQ=
|
github.com/fluxcd/pkg/oci v0.60.1/go.mod h1:w2FGseUl3WGjwRMH/3h6MTI4gKahcBQtnGbn/TQVA34=
|
||||||
github.com/fluxcd/pkg/runtime v0.103.0 h1:J5y5GPhWdkyqIUBlaI1FP2N02TtZmsjbWhhZubuTSFk=
|
github.com/fluxcd/pkg/runtime v0.100.4 h1:rwvbeoeWN0BTJORJBISJJEkWn6DVfmWwynFl2GseWns=
|
||||||
github.com/fluxcd/pkg/runtime v0.103.0/go.mod h1:mbo2f3azo3yVQgm7XZGxQB6/2zvzQ5Wgtd8TjRRwwAw=
|
github.com/fluxcd/pkg/runtime v0.100.4/go.mod h1:M6LjRJ1hIe2s6E2ykFfae1Xy/rLvOFQf2QquMKmN350=
|
||||||
github.com/fluxcd/pkg/sourceignore v0.17.0 h1:Z72nruRMhC15zIEpWoDrAcJcJ1El6QDnP/aRDfE4WOA=
|
github.com/fluxcd/pkg/sourceignore v0.17.0 h1:Z72nruRMhC15zIEpWoDrAcJcJ1El6QDnP/aRDfE4WOA=
|
||||||
github.com/fluxcd/pkg/sourceignore v0.17.0/go.mod h1:3e/VmYLId0pI/H5sK7W9Ibif+j0Ahns9RxNjDMtTTfY=
|
github.com/fluxcd/pkg/sourceignore v0.17.0/go.mod h1:3e/VmYLId0pI/H5sK7W9Ibif+j0Ahns9RxNjDMtTTfY=
|
||||||
github.com/fluxcd/pkg/ssa v0.70.0 h1:IBylYPiTK1IEdCC2DvjKXIhwQcbd5VufXA9WS3zO+tE=
|
github.com/fluxcd/pkg/ssa v0.67.3 h1:mjuhH5fNOYstkF6jB7EeaWmfnt5T272Cup8ZD9O8YBQ=
|
||||||
github.com/fluxcd/pkg/ssa v0.70.0/go.mod h1:6igtlt7/zF+nNFQpa5ZAkkvtpL6o36NRU39/PqqC+Bg=
|
github.com/fluxcd/pkg/ssa v0.67.3/go.mod h1:6igtlt7/zF+nNFQpa5ZAkkvtpL6o36NRU39/PqqC+Bg=
|
||||||
github.com/fluxcd/pkg/ssh v0.24.0 h1:hrPlxs0hhXf32DRqs68VbsXs0XfQMphyRVIk0rYYJa4=
|
github.com/fluxcd/pkg/ssh v0.24.0 h1:hrPlxs0hhXf32DRqs68VbsXs0XfQMphyRVIk0rYYJa4=
|
||||||
github.com/fluxcd/pkg/ssh v0.24.0/go.mod h1:xWammEqalrpurpcMiixJRXtynRQtBEoqheyU5F/vWrg=
|
github.com/fluxcd/pkg/ssh v0.24.0/go.mod h1:xWammEqalrpurpcMiixJRXtynRQtBEoqheyU5F/vWrg=
|
||||||
github.com/fluxcd/pkg/tar v0.17.0 h1:uNxbFXy8ly8C7fJ8D7w3rjTNJFrb4Hp1aY/30XkfvxY=
|
github.com/fluxcd/pkg/tar v0.17.0 h1:uNxbFXy8ly8C7fJ8D7w3rjTNJFrb4Hp1aY/30XkfvxY=
|
||||||
github.com/fluxcd/pkg/tar v0.17.0/go.mod h1:b1xyIRYDD0ket4SV5u0UXYv+ZdN/O/HmIO5jZQdHQls=
|
github.com/fluxcd/pkg/tar v0.17.0/go.mod h1:b1xyIRYDD0ket4SV5u0UXYv+ZdN/O/HmIO5jZQdHQls=
|
||||||
github.com/fluxcd/pkg/version v0.14.0 h1:T3llSc8sUnsuFrW5ng2ePSfXwGXUKv0YG9QXf0ErhWw=
|
github.com/fluxcd/pkg/version v0.12.0 h1:MGbdbNf2D5wazMqAkNPn+Lh5j+oY0gxQJFTGyet5Hfc=
|
||||||
github.com/fluxcd/pkg/version v0.14.0/go.mod h1:YHdg/78kzf+kCqS+SqSOiUxum5AjxlixiqwpX6AUZB8=
|
github.com/fluxcd/pkg/version v0.12.0/go.mod h1:YHdg/78kzf+kCqS+SqSOiUxum5AjxlixiqwpX6AUZB8=
|
||||||
github.com/fluxcd/source-controller/api v1.8.1 h1:49HiJF5mNEdZTwueQMRahTVts35B+xhN5CsuOAL9gQ0=
|
github.com/fluxcd/source-controller/api v1.8.1 h1:49HiJF5mNEdZTwueQMRahTVts35B+xhN5CsuOAL9gQ0=
|
||||||
github.com/fluxcd/source-controller/api v1.8.1/go.mod h1:HgZ6NSH1cyOE2jRoNwln1xEwr9ETvrLeiy1o4O04vQM=
|
github.com/fluxcd/source-controller/api v1.8.1/go.mod h1:HgZ6NSH1cyOE2jRoNwln1xEwr9ETvrLeiy1o4O04vQM=
|
||||||
github.com/fluxcd/source-watcher/api/v2 v2.1.1 h1:1LfT50ty+78MKKbschAZl28QbVqIyjaNq17KmW5wPJI=
|
github.com/fluxcd/source-watcher/api/v2 v2.1.1 h1:1LfT50ty+78MKKbschAZl28QbVqIyjaNq17KmW5wPJI=
|
||||||
|
|||||||
@@ -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/releases/download/v1.5.3/helm-controller.crds.yaml
|
- https://github.com/fluxcd/helm-controller/releases/download/v1.5.2/helm-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/helm-controller/releases/download/v1.5.3/helm-controller.deployment.yaml
|
- https://github.com/fluxcd/helm-controller/releases/download/v1.5.2/helm-controller.deployment.yaml
|
||||||
- account.yaml
|
- account.yaml
|
||||||
transformers:
|
transformers:
|
||||||
- labels.yaml
|
- labels.yaml
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ kind: Kustomization
|
|||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/source-controller/releases/download/v1.8.1/source-controller.crds.yaml
|
- https://github.com/fluxcd/source-controller/releases/download/v1.8.1/source-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/kustomize-controller/releases/download/v1.8.2/kustomize-controller.crds.yaml
|
- https://github.com/fluxcd/kustomize-controller/releases/download/v1.8.2/kustomize-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/helm-controller/releases/download/v1.5.3/helm-controller.crds.yaml
|
- https://github.com/fluxcd/helm-controller/releases/download/v1.5.2/helm-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/notification-controller/releases/download/v1.8.2/notification-controller.crds.yaml
|
- https://github.com/fluxcd/notification-controller/releases/download/v1.8.2/notification-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/image-reflector-controller/releases/download/v1.1.1/image-reflector-controller.crds.yaml
|
- https://github.com/fluxcd/image-reflector-controller/releases/download/v1.1.1/image-reflector-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/image-automation-controller/releases/download/v1.1.1/image-automation-controller.crds.yaml
|
- https://github.com/fluxcd/image-automation-controller/releases/download/v1.1.1/image-automation-controller.crds.yaml
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
# RFC-0010 Multi-Tenant Workload Identity
|
# RFC-0010 Multi-Tenant Workload Identity
|
||||||
|
|
||||||
**Status:** implemented
|
**Status:** implementable
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Status represents the current state of the RFC.
|
||||||
|
Must be one of `provisional`, `implementable`, `implemented`, `deferred`, `rejected`, `withdrawn`, or `replaced`.
|
||||||
|
-->
|
||||||
|
|
||||||
**Creation date:** 2025-02-22
|
**Creation date:** 2025-02-22
|
||||||
|
|
||||||
**Last update:** 2026-03-13
|
**Last update:** 2025-04-29
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
@@ -1415,11 +1420,10 @@ options to call `gcp.NewTokenSource()` and feed this token source to the
|
|||||||
`HelmRepository` and `HelmChart`, as well as for SOPS decryption
|
`HelmRepository` and `HelmChart`, as well as for SOPS decryption
|
||||||
in the `Kustomization` API and Azure Event Hubs in the
|
in the `Kustomization` API and Azure Event Hubs in the
|
||||||
`Provider` API.
|
`Provider` API.
|
||||||
* In Flux 2.7 object-level workload identity was introduced for all
|
|
||||||
the remaining APIs that support cloud providers, i.e. `Bucket`,
|
<!--
|
||||||
`GitRepository` and `ImageUpdateAutomation`, and also all the
|
Major milestones in the lifecycle of the RFC such as:
|
||||||
remaining types for the `Provider` API, i.e. `azuredevops` and
|
- The first Flux release where an initial version of the RFC was available.
|
||||||
`googlepubsub`. In addition, support for controller and
|
- The version of Flux where the RFC graduated to general availability.
|
||||||
object-level workload identity was introduced for the
|
- The version of Flux where the RFC was retired or superseded.
|
||||||
`Kustomization` and `HelmRelease` APIs for remote cluster
|
-->
|
||||||
access.
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
# RFC-0011: OpenTelemetry Tracing
|
# RFC-0011: OpenTelemetry Tracing
|
||||||
|
|
||||||
**Status:** implemented
|
**Status:** provisional
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Status represents the current state of the RFC.
|
||||||
|
Must be one of `provisional`, `implementable`, `implemented`, `deferred`, `rejected`, `withdrawn`, or `replaced`.
|
||||||
|
-->
|
||||||
|
|
||||||
**Creation date:** 2025-04-24
|
**Creation date:** 2025-04-24
|
||||||
|
|
||||||
**Last update:** 2026-03-13
|
**Last update:** 2025-08-13
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
The aim is to be able to collect traces via OpenTelemetry (OTel) across all Flux related objects, such as HelmReleases, Kustomizations and among others. These may be sent towards a tracing provider where may be potentially stored and visualized. Flux does not have any responsibility on storing and visualizing those, it keeps being completely stateless. Thereby, being seamless for the user, the implementation is going to be part of the already existing `Alert` API Type. Therefore, `EventSources` is going to discriminate the events belonging to the specific sources, which are going to be looked up to and send them out towards the `Provider` set. In this way, it could facilitate the observability and monitoring of Flux related objects.
|
The aim is to be able to collect traces via OpenTelemetry (OTel) across all Flux related objects, such as HelmReleases, Kustomizations and among others. These may be sent towards a tracing provider where may be potentially stored and visualized. Flux does not have any responsibility on storing and visualizing those, it keeps being completely stateless. Thereby, being seamless for the user, the implementation is going to be part of the already existing `Alert` API Type. Therefore, `EventSources` is going to discriminate the events belonging to the specific sources, which are going to be looked up to and send them out towards the `Provider` set. In this way, it could facilitate the observability and monitoring of Flux related objects.
|
||||||
@@ -205,4 +210,9 @@ This design ensures trace continuity even in challenging distributed environment
|
|||||||
|
|
||||||
## Implementation History
|
## Implementation History
|
||||||
|
|
||||||
* RFC implemented and generally available in Flux [v2.7.0](https://github.com/fluxcd/flux2/releases/tag/v2.7.0)
|
<!--
|
||||||
|
Major milestones in the lifecycle of the RFC such as:
|
||||||
|
- The first Flux release where an initial version of the RFC was available.
|
||||||
|
- The version of Flux where the RFC graduated to general availability.
|
||||||
|
- The version of Flux where the RFC was retired or superseded.
|
||||||
|
-->
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# RFC-0012 External Artifact
|
# RFC-0012 External Artifact
|
||||||
|
|
||||||
**Status:** implemented
|
**Status:** provisional
|
||||||
|
|
||||||
**Creation date:** 2025-04-08
|
**Creation date:** 2025-04-08
|
||||||
|
|
||||||
**Last update:** 2026-03-13
|
**Last update:** 2025-09-03
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
@@ -319,4 +319,9 @@ control the adoption of the `ExternalArtifact` feature in their clusters.
|
|||||||
|
|
||||||
## Implementation History
|
## Implementation History
|
||||||
|
|
||||||
* RFC implemented and generally available in Flux [v2.7.0](https://github.com/fluxcd/flux2/releases/tag/v2.7.0)
|
<!--
|
||||||
|
Major milestones in the lifecycle of the RFC such as:
|
||||||
|
- The first Flux release where an initial version of the RFC was available.
|
||||||
|
- The version of Flux where the RFC graduated to general availability.
|
||||||
|
- The version of Flux where the RFC was retired or superseded.
|
||||||
|
-->
|
||||||
|
|||||||
@@ -1,312 +0,0 @@
|
|||||||
# RFC-XXXX Flux CLI Plugin System
|
|
||||||
|
|
||||||
**Status:** provisional
|
|
||||||
|
|
||||||
**Creation date:** 2026-03-30
|
|
||||||
|
|
||||||
**Last update:** 2026-03-30
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
This RFC proposes a plugin system for the Flux CLI that allows external CLI tools to be
|
|
||||||
discoverable and invocable as `flux <name>` subcommands. Plugins are installed from a
|
|
||||||
centralized catalog hosted on GitHub, with SHA-256 checksum verification and automatic
|
|
||||||
version updates. The design follows the established kubectl plugin pattern used across
|
|
||||||
the Kubernetes ecosystem.
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
The Flux CLI currently has no mechanism for extending its functionality with external tools.
|
|
||||||
Projects like [flux-operator](https://github.com/controlplaneio-fluxcd/flux-operator) and
|
|
||||||
[flux-local](https://github.com/allenporter/flux-local) provide complementary CLI tools
|
|
||||||
that users install and invoke separately. This creates a fragmented user experience where
|
|
||||||
Flux-related workflows require switching between multiple binaries with different flag
|
|
||||||
conventions and discovery mechanisms.
|
|
||||||
|
|
||||||
The Kubernetes ecosystem has a proven model for CLI extensibility: kubectl plugins are
|
|
||||||
executables prefixed with `kubectl-` that can be discovered, installed via
|
|
||||||
[krew](https://krew.sigs.k8s.io/), and invoked as `kubectl <name>`. This model has
|
|
||||||
been widely adopted and is well understood by Kubernetes users.
|
|
||||||
|
|
||||||
### Goals
|
|
||||||
|
|
||||||
- Allow external CLI tools to be invoked as `flux <name>` subcommands without modifying
|
|
||||||
the external binary.
|
|
||||||
- Provide a `flux plugin install` command to download plugins from a centralized catalog
|
|
||||||
with checksum verification.
|
|
||||||
- Support shell completion for plugin subcommands by delegating to the plugin's own
|
|
||||||
Cobra `__complete` command.
|
|
||||||
- Support plugins written as scripts (Python, Bash, etc.) via symlinks into the
|
|
||||||
plugin directory.
|
|
||||||
- Ensure built-in commands always take priority over plugins.
|
|
||||||
- Keep the plugin system lightweight with zero impact on non-plugin Flux commands.
|
|
||||||
|
|
||||||
### Non-Goals
|
|
||||||
|
|
||||||
- Plugin dependency management (plugins are standalone binaries).
|
|
||||||
- Cosign/SLSA signature verification (SHA-256 only in v1beta1; signatures can be added later).
|
|
||||||
- Automatic update checks on startup (users run `flux plugin update` explicitly).
|
|
||||||
- Private catalog authentication (users can use `$FLUXCD_PLUGIN_CATALOG` with TLS).
|
|
||||||
- Flag sharing between Flux and plugins (`--namespace`, `--context`, etc. are not
|
|
||||||
forwarded; plugins manage their own flags).
|
|
||||||
|
|
||||||
## Proposal
|
|
||||||
|
|
||||||
### Plugin Discovery
|
|
||||||
|
|
||||||
Plugins are executables prefixed with `flux-` placed in a single plugin directory.
|
|
||||||
The `flux-<name>` binary maps to the `flux <name>` command. For example,
|
|
||||||
`flux-operator` becomes `flux operator`.
|
|
||||||
|
|
||||||
The default plugin directory is `~/.fluxcd/plugins/`. Users can override it with the
|
|
||||||
`$FLUXCD_PLUGINS` environment variable. Only this single directory is scanned.
|
|
||||||
|
|
||||||
When a plugin is discovered, it appears under a "Plugin Commands:" group in `flux --help`:
|
|
||||||
|
|
||||||
```
|
|
||||||
Plugin Commands:
|
|
||||||
operator Runs the operator plugin
|
|
||||||
|
|
||||||
Additional Commands:
|
|
||||||
bootstrap Deploy Flux on a cluster the GitOps way.
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Plugin Execution
|
|
||||||
|
|
||||||
On macOS and Linux, `flux operator export report` replaces the current process with
|
|
||||||
`flux-operator export report` via `syscall.Exec`, matching kubectl's behavior.
|
|
||||||
On Windows, the plugin runs as a child process with full I/O passthrough.
|
|
||||||
All arguments after the plugin name are passed through verbatim with
|
|
||||||
`DisableFlagParsing: true`.
|
|
||||||
|
|
||||||
### Shell Completion
|
|
||||||
|
|
||||||
Shell completion is delegated to the plugin binary via Cobra's `__complete` protocol.
|
|
||||||
When the user types `flux operator get <TAB>`, Flux runs
|
|
||||||
`flux-operator __complete get ""` and returns the results. This works automatically
|
|
||||||
for all Cobra-based plugins (like flux-operator). Non-Cobra plugins gracefully degrade
|
|
||||||
to no completions.
|
|
||||||
|
|
||||||
### Plugin Catalog
|
|
||||||
|
|
||||||
A dedicated GitHub repository ([fluxcd/plugins](https://github.com/fluxcd/plugins))
|
|
||||||
serves as the plugin catalog. Each plugin has a YAML manifest:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: cli.fluxcd.io/v1beta1
|
|
||||||
kind: Plugin
|
|
||||||
name: operator
|
|
||||||
description: Flux Operator CLI
|
|
||||||
homepage: https://fluxoperator.dev/
|
|
||||||
source: https://github.com/controlplaneio-fluxcd/flux-operator
|
|
||||||
bin: flux-operator
|
|
||||||
versions:
|
|
||||||
- version: 0.45.0
|
|
||||||
platforms:
|
|
||||||
- os: darwin
|
|
||||||
arch: arm64
|
|
||||||
url: https://github.com/.../flux-operator_0.45.0_darwin_arm64.tar.gz
|
|
||||||
checksum: sha256:cd85d5d84d264...
|
|
||||||
- os: linux
|
|
||||||
arch: amd64
|
|
||||||
url: https://github.com/.../flux-operator_0.45.0_linux_amd64.tar.gz
|
|
||||||
checksum: sha256:96198da969096...
|
|
||||||
```
|
|
||||||
|
|
||||||
A generated `catalog.yaml` (`PluginCatalog` kind) contains static metadata for all
|
|
||||||
plugins, enabling `flux plugin search` with a single HTTP fetch.
|
|
||||||
|
|
||||||
### CLI Commands
|
|
||||||
|
|
||||||
| Command | Description |
|
|
||||||
|---------|-------------|
|
|
||||||
| `flux plugin list` (alias: `ls`) | List installed plugins with versions and paths |
|
|
||||||
| `flux plugin install <name>[@<version>]` | Install a plugin from the catalog |
|
|
||||||
| `flux plugin uninstall <name>` | Remove a plugin binary and receipt |
|
|
||||||
| `flux plugin update [name]` | Update one or all installed plugins |
|
|
||||||
| `flux plugin search [query]` | Search the plugin catalog |
|
|
||||||
|
|
||||||
### Install Flow
|
|
||||||
|
|
||||||
1. Fetch `plugins/<name>.yaml` from the catalog URL
|
|
||||||
2. Validate `apiVersion: cli.fluxcd.io/v1beta1` and `kind: Plugin`
|
|
||||||
3. Resolve version (latest if unspecified, or match `@version`)
|
|
||||||
4. Find platform entry matching `runtime.GOOS` / `runtime.GOARCH`
|
|
||||||
5. Download archive to temp file with SHA-256 checksum verification
|
|
||||||
6. Extract only the declared binary from the archive (tar.gz or zip), streaming
|
|
||||||
directly to disk without buffering in memory
|
|
||||||
7. Write binary to plugin directory as `flux-<name>` (mode `0755`)
|
|
||||||
8. Write install receipt (`flux-<name>.yaml`) recording version, platform, download URL, checksum and timestamp
|
|
||||||
|
|
||||||
Install is idempotent -- reinstalling overwrites the binary and receipt.
|
|
||||||
|
|
||||||
### Install Receipts
|
|
||||||
|
|
||||||
When a plugin is installed via `flux plugin install`, a receipt file is written
|
|
||||||
next to the binary:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: operator
|
|
||||||
version: "0.45.0"
|
|
||||||
installedAt: "2026-03-30T10:00:00Z"
|
|
||||||
platform:
|
|
||||||
os: darwin
|
|
||||||
arch: arm64
|
|
||||||
url: https://github.com/.../flux-operator_0.45.0_darwin_arm64.tar.gz
|
|
||||||
checksum: sha256:cd85d5d84d264...
|
|
||||||
```
|
|
||||||
|
|
||||||
Receipts enable `flux plugin list` to show versions, `flux plugin update` to compare
|
|
||||||
installed vs. latest, and provenance tracking. Manually installed plugins (no receipt)
|
|
||||||
show `manual` in listings and are skipped by `flux plugin update`.
|
|
||||||
|
|
||||||
### User Stories
|
|
||||||
|
|
||||||
#### Flux User Installs a Plugin
|
|
||||||
|
|
||||||
As a Flux user, I want to install the Flux Operator CLI as a plugin so that I can
|
|
||||||
manage Flux instances using `flux operator` instead of a separate `flux-operator` binary.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flux plugin install operator
|
|
||||||
flux operator get instance -n flux-system
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Flux User Updates Plugins
|
|
||||||
|
|
||||||
As a Flux user, I want to update all my installed plugins to the latest versions
|
|
||||||
with a single command.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flux plugin update
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Flux User Symlinks a Python Plugin
|
|
||||||
|
|
||||||
As a Flux user, I want to use [flux-local](https://github.com/allenporter/flux-local)
|
|
||||||
(a Python tool) as a Flux CLI plugin by symlinking it into the plugin directory.
|
|
||||||
Since flux-local is not a Go binary distributed via the catalog, I install it with
|
|
||||||
pip and register it manually.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv venv
|
|
||||||
source .venv/bin/activate
|
|
||||||
uv pip install flux-local
|
|
||||||
ln -s "$(pwd)/.venv/bin/flux-local" ~/.fluxcd/plugins/flux-local
|
|
||||||
flux local test
|
|
||||||
```
|
|
||||||
|
|
||||||
Manually symlinked plugins show `manual` in `flux plugin list` and are skipped by
|
|
||||||
`flux plugin update`.
|
|
||||||
|
|
||||||
#### Flux User Discovers Available Plugins
|
|
||||||
|
|
||||||
As a Flux user, I want to search for available plugins so that I can extend my
|
|
||||||
Flux CLI with community tools.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flux plugin search
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Plugin Author Publishes a Plugin
|
|
||||||
|
|
||||||
As a plugin author, I want to submit my tool to the Flux plugin catalog so that
|
|
||||||
Flux users can install it with `flux plugin install <name>`.
|
|
||||||
|
|
||||||
1. Release binary with GoReleaser (produces tarballs/zips + checksums)
|
|
||||||
2. Submit a PR to `fluxcd/plugins` with `plugins/<name>.yaml`
|
|
||||||
3. Subsequent releases are picked up by automated polling workflows
|
|
||||||
|
|
||||||
### Alternatives
|
|
||||||
|
|
||||||
#### PATH-based Discovery (kubectl model)
|
|
||||||
|
|
||||||
kubectl discovers plugins by scanning `$PATH` for `kubectl-*` executables. This is
|
|
||||||
simple but has drawbacks:
|
|
||||||
|
|
||||||
- Scanning the entire PATH is slow on some systems
|
|
||||||
- No control over what's discoverable (any `flux-*` binary on PATH becomes a plugin)
|
|
||||||
- No install/update mechanism built in (requires a separate tool like krew)
|
|
||||||
|
|
||||||
The single-directory approach is faster, more predictable, and integrates install/update
|
|
||||||
directly into the CLI.
|
|
||||||
|
|
||||||
## Design Details
|
|
||||||
|
|
||||||
### Package Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
internal/plugin/
|
|
||||||
discovery.go # Plugin dir scanning, DI-based Handler
|
|
||||||
completion.go # Shell completion via Cobra __complete protocol
|
|
||||||
exec_unix.go # syscall.Exec (//go:build !windows)
|
|
||||||
exec_windows.go # os/exec fallback (//go:build windows)
|
|
||||||
catalog.go # Catalog fetching, manifest parsing, version/platform resolution
|
|
||||||
install.go # Download, verify, extract, receipts
|
|
||||||
update.go # Compare receipts vs catalog, update check
|
|
||||||
|
|
||||||
cmd/flux/
|
|
||||||
plugin.go # Cobra command registration, all plugin subcommands
|
|
||||||
```
|
|
||||||
|
|
||||||
The `internal/plugin` package uses dependency injection (injectable `ReadDir`, `Stat`,
|
|
||||||
`GetEnv`, `HomeDir` on a `Handler` struct) for testability. Tests mock these functions
|
|
||||||
directly without filesystem fixtures.
|
|
||||||
|
|
||||||
### Plugin Directory
|
|
||||||
|
|
||||||
- **Default**: `~/.fluxcd/plugins/` -- auto-created by install/update commands
|
|
||||||
(best-effort, no error if filesystem is read-only).
|
|
||||||
- **Override**: `$FLUXCD_PLUGINS` env var replaces the default directory path.
|
|
||||||
When set, the CLI does not auto-create the directory.
|
|
||||||
|
|
||||||
### Startup Behavior
|
|
||||||
|
|
||||||
`registerPlugins()` is called in `main()` before `rootCmd.Execute()`. It scans the
|
|
||||||
plugin directory and registers discovered plugins as Cobra subcommands. The scan is
|
|
||||||
lightweight (a single `ReadDir` call) and only occurs if the plugin directory exists.
|
|
||||||
Built-in commands always take priority.
|
|
||||||
|
|
||||||
### Manifest Validation
|
|
||||||
|
|
||||||
Both plugin manifests and the catalog are validated after fetching:
|
|
||||||
|
|
||||||
- `apiVersion` must be `cli.fluxcd.io/v1beta1`
|
|
||||||
- `kind` must be `Plugin` or `PluginCatalog` respectively
|
|
||||||
- Checksum format is `<algorithm>:<hex>` (currently `sha256:...`), allowing future
|
|
||||||
algorithm migration without schema changes
|
|
||||||
|
|
||||||
### Security Considerations
|
|
||||||
|
|
||||||
- **Checksum verification**: All downloaded archives are verified against SHA-256
|
|
||||||
checksums declared in the catalog manifest before extraction.
|
|
||||||
- **Path traversal protection**: Archive extraction guards against tar traversal.
|
|
||||||
- **Response size limits**: HTTP responses from the catalog are capped at 10 MiB to
|
|
||||||
prevent unbounded memory allocation from malicious servers.
|
|
||||||
- **No code execution during discovery**: Plugin directory scanning only reads directory
|
|
||||||
entries and file metadata. No plugin binary is executed during startup.
|
|
||||||
- **Retryable fetching**: All HTTP/S operations use automatic retries for transient network failures.
|
|
||||||
|
|
||||||
### Catalog Repository CI
|
|
||||||
|
|
||||||
The `fluxcd/plugins` repository includes CI workflows that:
|
|
||||||
|
|
||||||
1. Validate plugin manifests on every PR (schema, name consistency, URL reachability,
|
|
||||||
checksum verification, binary presence in archives, no builtin collisions)
|
|
||||||
2. Regenerate `catalog.yaml` when plugins are added or removed
|
|
||||||
3. Automatically poll upstream repositories for new releases and create update PRs
|
|
||||||
|
|
||||||
### Known Limitations (v1beta1)
|
|
||||||
|
|
||||||
1. **No cosign/SLSA verification** -- SHA-256 only. Signature verification can be added later.
|
|
||||||
2. **No plugin dependencies** -- plugins are standalone binaries.
|
|
||||||
3. **No automatic update checks** -- users run `flux plugin update` explicitly.
|
|
||||||
4. **No private catalog auth** -- `$FLUXCD_PLUGIN_CATALOG` works for private URLs but no token injection.
|
|
||||||
5. **No version constraints** -- no `>=0.44.0` ranges. Exact version or latest only.
|
|
||||||
6. **Flag names differ between Flux and plugins** -- e.g., `--context` (flux) vs
|
|
||||||
`--kube-context` (flux-operator). This is a plugin concern, not a system concern.
|
|
||||||
|
|
||||||
## Implementation History
|
|
||||||
|
|
||||||
- **2026-03-30** PoC plugin catalog repository with example manifests and CI validation workflows available at [fluxcd/plugins](https://github.com/fluxcd/plugins).
|
|
||||||
@@ -11,10 +11,10 @@ require (
|
|||||||
github.com/fluxcd/image-reflector-controller/api v1.0.4
|
github.com/fluxcd/image-reflector-controller/api v1.0.4
|
||||||
github.com/fluxcd/kustomize-controller/api v1.7.3
|
github.com/fluxcd/kustomize-controller/api v1.7.3
|
||||||
github.com/fluxcd/notification-controller/api v1.7.5
|
github.com/fluxcd/notification-controller/api v1.7.5
|
||||||
github.com/fluxcd/pkg/apis/event v0.25.0
|
github.com/fluxcd/pkg/apis/event v0.24.1
|
||||||
github.com/fluxcd/pkg/apis/meta v1.26.0
|
github.com/fluxcd/pkg/apis/meta v1.25.1
|
||||||
github.com/fluxcd/pkg/git v0.46.0
|
github.com/fluxcd/pkg/git v0.43.1
|
||||||
github.com/fluxcd/pkg/runtime v0.103.0
|
github.com/fluxcd/pkg/runtime v0.100.4
|
||||||
github.com/fluxcd/source-controller/api v1.7.4
|
github.com/fluxcd/source-controller/api v1.7.4
|
||||||
github.com/fluxcd/test-infra/tftestenv v0.0.0-20250626232827-e0ca9c3f8d7b
|
github.com/fluxcd/test-infra/tftestenv v0.0.0-20250626232827-e0ca9c3f8d7b
|
||||||
github.com/go-git/go-git/v5 v5.16.5
|
github.com/go-git/go-git/v5 v5.16.5
|
||||||
@@ -66,9 +66,9 @@ require (
|
|||||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fluxcd/pkg/apis/acl v0.9.0 // indirect
|
github.com/fluxcd/pkg/apis/acl v0.9.0 // indirect
|
||||||
github.com/fluxcd/pkg/apis/kustomize v1.16.0 // indirect
|
github.com/fluxcd/pkg/apis/kustomize v1.15.1 // indirect
|
||||||
github.com/fluxcd/pkg/ssh v0.24.0 // indirect
|
github.com/fluxcd/pkg/ssh v0.24.0 // indirect
|
||||||
github.com/fluxcd/pkg/version v0.14.0 // indirect
|
github.com/fluxcd/pkg/version v0.12.0 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.7.0 // indirect
|
github.com/go-git/go-billy/v5 v5.7.0 // indirect
|
||||||
|
|||||||
@@ -136,22 +136,22 @@ github.com/fluxcd/notification-controller/api v1.7.5 h1:6CO5bKyjodiK9exQFOdBcz0X
|
|||||||
github.com/fluxcd/notification-controller/api v1.7.5/go.mod h1:IciwSg8Q0pVtdbsyDyEXx/MxBKWeagxAazpm64C8oCE=
|
github.com/fluxcd/notification-controller/api v1.7.5/go.mod h1:IciwSg8Q0pVtdbsyDyEXx/MxBKWeagxAazpm64C8oCE=
|
||||||
github.com/fluxcd/pkg/apis/acl v0.9.0 h1:wBpgsKT+jcyZEcM//OmZr9RiF8klL3ebrDp2u2ThsnA=
|
github.com/fluxcd/pkg/apis/acl v0.9.0 h1:wBpgsKT+jcyZEcM//OmZr9RiF8klL3ebrDp2u2ThsnA=
|
||||||
github.com/fluxcd/pkg/apis/acl v0.9.0/go.mod h1:TttNS+gocsGLwnvmgVi3/Yscwqrjc17+vhgYfqkfrV4=
|
github.com/fluxcd/pkg/apis/acl v0.9.0/go.mod h1:TttNS+gocsGLwnvmgVi3/Yscwqrjc17+vhgYfqkfrV4=
|
||||||
github.com/fluxcd/pkg/apis/event v0.25.0 h1:zdwytvDhG+fk+Ywl5DOtv7TklkrVgM21WHm1f+YhleE=
|
github.com/fluxcd/pkg/apis/event v0.24.1 h1:TClVdn02aiq3sAl9BuzLjjTIxm3JJ83fJ9nchtBa4qg=
|
||||||
github.com/fluxcd/pkg/apis/event v0.25.0/go.mod h1:TlK8HWYrTwl0raqBRC+ROoNpYW5fdVnwcwOBOx5Kzw8=
|
github.com/fluxcd/pkg/apis/event v0.24.1/go.mod h1:TlK8HWYrTwl0raqBRC+ROoNpYW5fdVnwcwOBOx5Kzw8=
|
||||||
github.com/fluxcd/pkg/apis/kustomize v1.16.0 h1:PhWXEhqQqsisIpwp1/wHvTvo+MO+GGzsBPoN0ZnRE3Y=
|
github.com/fluxcd/pkg/apis/kustomize v1.15.1 h1:t9QZh+3ZS8EKmlxrnnbcKZcGTrg8FDvMF1T8BHMCuqI=
|
||||||
github.com/fluxcd/pkg/apis/kustomize v1.16.0/go.mod h1:IZOy4CCtR/hxMGb7erK1RfbGnczVv4/dRBoVD37AywI=
|
github.com/fluxcd/pkg/apis/kustomize v1.15.1/go.mod h1:IZOy4CCtR/hxMGb7erK1RfbGnczVv4/dRBoVD37AywI=
|
||||||
github.com/fluxcd/pkg/apis/meta v1.26.0 h1:dxP1FfBpTCYso6odzRcltVnnRuBb2VyhhgV0VX9YbUE=
|
github.com/fluxcd/pkg/apis/meta v1.25.1 h1:WG1GIC/SOz0GjxT0uVuO6AMicQ3yFsk6bDozCnq+fto=
|
||||||
github.com/fluxcd/pkg/apis/meta v1.26.0/go.mod h1:c7o6mJGLCMvNrfdinGZehkrdZuFT9vZdZNrn66DtVD0=
|
github.com/fluxcd/pkg/apis/meta v1.25.1/go.mod h1:c7o6mJGLCMvNrfdinGZehkrdZuFT9vZdZNrn66DtVD0=
|
||||||
github.com/fluxcd/pkg/git v0.46.0 h1:QMh0+ZzQ2jO6rIGj4ffR5trZ8g/cxvt8cVajReJ8Iyw=
|
github.com/fluxcd/pkg/git v0.43.1 h1:lw29P44wueKzQk79KnYyvisfw//cxg0S4cDeTYx+Slo=
|
||||||
github.com/fluxcd/pkg/git v0.46.0/go.mod h1:iHcIjx9c8zye3PQiajTJYxgOMRiy7WCs+hfLKDswpfI=
|
github.com/fluxcd/pkg/git v0.43.1/go.mod h1:3R/AjCe7ee7FqWcAG+2IiuJPOCxrGHF4SCGkuvKS6OQ=
|
||||||
github.com/fluxcd/pkg/gittestserver v0.26.0 h1:+RZrCzFRsE+d5WaqAoqaPCEgcgv/jZp6+f7DS0+Ynb8=
|
github.com/fluxcd/pkg/gittestserver v0.25.1 h1:40Ridmy1xKxBM9ItDn012R4VKmaoDqzvGaC5g7xv+mw=
|
||||||
github.com/fluxcd/pkg/gittestserver v0.26.0/go.mod h1:7fybYb0yej1fFNiF1ohs0Jr0XzyaZQ/cRh3AFEoCtuc=
|
github.com/fluxcd/pkg/gittestserver v0.25.1/go.mod h1:7fybYb0yej1fFNiF1ohs0Jr0XzyaZQ/cRh3AFEoCtuc=
|
||||||
github.com/fluxcd/pkg/runtime v0.103.0 h1:J5y5GPhWdkyqIUBlaI1FP2N02TtZmsjbWhhZubuTSFk=
|
github.com/fluxcd/pkg/runtime v0.100.4 h1:rwvbeoeWN0BTJORJBISJJEkWn6DVfmWwynFl2GseWns=
|
||||||
github.com/fluxcd/pkg/runtime v0.103.0/go.mod h1:mbo2f3azo3yVQgm7XZGxQB6/2zvzQ5Wgtd8TjRRwwAw=
|
github.com/fluxcd/pkg/runtime v0.100.4/go.mod h1:M6LjRJ1hIe2s6E2ykFfae1Xy/rLvOFQf2QquMKmN350=
|
||||||
github.com/fluxcd/pkg/ssh v0.24.0 h1:hrPlxs0hhXf32DRqs68VbsXs0XfQMphyRVIk0rYYJa4=
|
github.com/fluxcd/pkg/ssh v0.24.0 h1:hrPlxs0hhXf32DRqs68VbsXs0XfQMphyRVIk0rYYJa4=
|
||||||
github.com/fluxcd/pkg/ssh v0.24.0/go.mod h1:xWammEqalrpurpcMiixJRXtynRQtBEoqheyU5F/vWrg=
|
github.com/fluxcd/pkg/ssh v0.24.0/go.mod h1:xWammEqalrpurpcMiixJRXtynRQtBEoqheyU5F/vWrg=
|
||||||
github.com/fluxcd/pkg/version v0.14.0 h1:T3llSc8sUnsuFrW5ng2ePSfXwGXUKv0YG9QXf0ErhWw=
|
github.com/fluxcd/pkg/version v0.12.0 h1:MGbdbNf2D5wazMqAkNPn+Lh5j+oY0gxQJFTGyet5Hfc=
|
||||||
github.com/fluxcd/pkg/version v0.14.0/go.mod h1:YHdg/78kzf+kCqS+SqSOiUxum5AjxlixiqwpX6AUZB8=
|
github.com/fluxcd/pkg/version v0.12.0/go.mod h1:YHdg/78kzf+kCqS+SqSOiUxum5AjxlixiqwpX6AUZB8=
|
||||||
github.com/fluxcd/source-controller/api v1.7.4 h1:+EOVnRA9LmLxOx7J273l7IOEU39m+Slt/nQGBy69ygs=
|
github.com/fluxcd/source-controller/api v1.7.4 h1:+EOVnRA9LmLxOx7J273l7IOEU39m+Slt/nQGBy69ygs=
|
||||||
github.com/fluxcd/source-controller/api v1.7.4/go.mod h1:ruf49LEgZRBfcP+eshl2n9SX1MfHayCcViAIGnZcaDY=
|
github.com/fluxcd/source-controller/api v1.7.4/go.mod h1:ruf49LEgZRBfcP+eshl2n9SX1MfHayCcViAIGnZcaDY=
|
||||||
github.com/fluxcd/test-infra/tftestenv v0.0.0-20250626232827-e0ca9c3f8d7b h1:FSPtvaVgL8azcyweqLmD71elAw4vozuXH/QvsJQ7tg0=
|
github.com/fluxcd/test-infra/tftestenv v0.0.0-20250626232827-e0ca9c3f8d7b h1:FSPtvaVgL8azcyweqLmD71elAw4vozuXH/QvsJQ7tg0=
|
||||||
|
|||||||
Reference in New Issue
Block a user