mirror of
https://github.com/fluxcd/flux2.git
synced 2026-04-10 14:10:05 +00:00
Merge 5142a93367 into c432d380dd
This commit is contained in:
commit
f3e13c7b9d
8 changed files with 379 additions and 18 deletions
|
|
@ -72,6 +72,7 @@ type buildKsFlags struct {
|
|||
strictSubst bool
|
||||
recursive bool
|
||||
localSources map[string]string
|
||||
inMemoryBuild bool
|
||||
}
|
||||
|
||||
var buildKsArgs buildKsFlags
|
||||
|
|
@ -85,6 +86,8 @@ func init() {
|
|||
"When enabled, the post build substitutions will fail if a var without a default value is declared in files but is missing from the input vars.")
|
||||
buildKsCmd.Flags().BoolVarP(&buildKsArgs.recursive, "recursive", "r", false, "Recursively build Kustomizations")
|
||||
buildKsCmd.Flags().StringToStringVar(&buildKsArgs.localSources, "local-sources", nil, "Comma-separated list of repositories in format: Kind/namespace/name=path")
|
||||
buildKsCmd.Flags().BoolVar(&buildKsArgs.inMemoryBuild, "in-memory-build", false,
|
||||
"Use in-memory filesystem during build.")
|
||||
buildCmd.AddCommand(buildKsCmd)
|
||||
}
|
||||
|
||||
|
|
@ -130,6 +133,7 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) (err error) {
|
|||
build.WithStrictSubstitute(buildKsArgs.strictSubst),
|
||||
build.WithRecursive(buildKsArgs.recursive),
|
||||
build.WithLocalSources(buildKsArgs.localSources),
|
||||
build.WithInMemoryBuild(buildKsArgs.inMemoryBuild),
|
||||
)
|
||||
} else {
|
||||
builder, err = build.NewBuilder(name, buildKsArgs.path,
|
||||
|
|
@ -140,6 +144,7 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) (err error) {
|
|||
build.WithStrictSubstitute(buildKsArgs.strictSubst),
|
||||
build.WithRecursive(buildKsArgs.recursive),
|
||||
build.WithLocalSources(buildKsArgs.localSources),
|
||||
build.WithInMemoryBuild(buildKsArgs.inMemoryBuild),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ func TestBuildKustomization(t *testing.T) {
|
|||
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build podinfo with in-memory-build",
|
||||
args: "build kustomization podinfo --path ./testdata/build-kustomization/podinfo --in-memory-build",
|
||||
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build podinfo without service",
|
||||
args: "build kustomization podinfo --path ./testdata/build-kustomization/delete-service",
|
||||
|
|
@ -70,12 +76,24 @@ func TestBuildKustomization(t *testing.T) {
|
|||
resultFile: "./testdata/build-kustomization/podinfo-with-ignore-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build ignore with in-memory-build",
|
||||
args: "build kustomization podinfo --path ./testdata/build-kustomization/ignore --ignore-paths \"!configmap.yaml,!secret.yaml\" --in-memory-build",
|
||||
resultFile: "./testdata/build-kustomization/podinfo-with-ignore-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build with recursive",
|
||||
args: "build kustomization podinfo --path ./testdata/build-kustomization/podinfo-with-my-app --recursive --local-sources GitRepository/default/podinfo=./testdata/build-kustomization",
|
||||
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build with recursive and in-memory-build",
|
||||
args: "build kustomization podinfo --path ./testdata/build-kustomization/podinfo-with-my-app --recursive --local-sources GitRepository/default/podinfo=./testdata/build-kustomization --in-memory-build",
|
||||
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
}
|
||||
|
||||
tmpl := map[string]string{
|
||||
|
|
@ -145,6 +163,12 @@ spec:
|
|||
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build podinfo with in-memory-build",
|
||||
args: "build kustomization podinfo --kustomization-file " + tmpFile + " --path ./testdata/build-kustomization/podinfo --in-memory-build",
|
||||
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build podinfo without service",
|
||||
args: "build kustomization podinfo --kustomization-file " + tmpFile + " --path ./testdata/build-kustomization/delete-service",
|
||||
|
|
@ -175,6 +199,18 @@ spec:
|
|||
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build with recursive and in-memory-build",
|
||||
args: "build kustomization podinfo --kustomization-file " + tmpFile + " --path ./testdata/build-kustomization/podinfo-with-my-app --recursive --local-sources GitRepository/default/podinfo=./testdata/build-kustomization --in-memory-build",
|
||||
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build with recursive and in-memory-build in dry-run mode",
|
||||
args: "build kustomization podinfo --kustomization-file " + tmpFile + " --path ./testdata/build-kustomization/podinfo-with-my-app --recursive --local-sources GitRepository/default/podinfo=./testdata/build-kustomization --in-memory-build --dry-run",
|
||||
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
}
|
||||
|
||||
tmpl := map[string]string{
|
||||
|
|
@ -241,6 +277,12 @@ func TestBuildKustomizationPathNormalization(t *testing.T) {
|
|||
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build with absolute path and in-memory-build",
|
||||
args: "build kustomization podinfo --path " + absTestDataPath + " --in-memory-build",
|
||||
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
|
||||
assertFunc: "assertGoldenTemplateFile",
|
||||
},
|
||||
{
|
||||
name: "build with complex relative path (parent dir)",
|
||||
args: "build kustomization podinfo --path ./testdata/build-kustomization/../build-kustomization/podinfo",
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ type diffKsFlags struct {
|
|||
strictSubst bool
|
||||
recursive bool
|
||||
localSources map[string]string
|
||||
inMemoryBuild bool
|
||||
}
|
||||
|
||||
var diffKsArgs diffKsFlags
|
||||
|
|
@ -75,6 +76,8 @@ func init() {
|
|||
"When enabled, the post build substitutions will fail if a var without a default value is declared in files but is missing from the input vars.")
|
||||
diffKsCmd.Flags().BoolVarP(&diffKsArgs.recursive, "recursive", "r", false, "Recursively diff Kustomizations")
|
||||
diffKsCmd.Flags().StringToStringVar(&diffKsArgs.localSources, "local-sources", nil, "Comma-separated list of repositories in format: Kind/namespace/name=path")
|
||||
diffKsCmd.Flags().BoolVar(&diffKsArgs.inMemoryBuild, "in-memory-build", false,
|
||||
"Use in-memory filesystem during build.")
|
||||
diffCmd.AddCommand(diffKsCmd)
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +116,7 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||
build.WithRecursive(diffKsArgs.recursive),
|
||||
build.WithLocalSources(diffKsArgs.localSources),
|
||||
build.WithSingleKustomization(),
|
||||
build.WithInMemoryBuild(diffKsArgs.inMemoryBuild),
|
||||
)
|
||||
} else {
|
||||
builder, err = build.NewBuilder(name, diffKsArgs.path,
|
||||
|
|
@ -124,6 +128,7 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||
build.WithRecursive(diffKsArgs.recursive),
|
||||
build.WithLocalSources(diffKsArgs.localSources),
|
||||
build.WithSingleKustomization(),
|
||||
build.WithInMemoryBuild(diffKsArgs.inMemoryBuild),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -267,3 +267,5 @@ require (
|
|||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect
|
||||
)
|
||||
|
||||
replace github.com/fluxcd/pkg/kustomize => github.com/rycli/fluxcd-pkg/kustomize v0.0.0-20260329192052-94b031e1aca6
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -204,8 +204,6 @@ github.com/fluxcd/pkg/git v0.46.0 h1:QMh0+ZzQ2jO6rIGj4ffR5trZ8g/cxvt8cVajReJ8Iyw
|
|||
github.com/fluxcd/pkg/git v0.46.0/go.mod h1:iHcIjx9c8zye3PQiajTJYxgOMRiy7WCs+hfLKDswpfI=
|
||||
github.com/fluxcd/pkg/gittestserver v0.26.0 h1:+RZrCzFRsE+d5WaqAoqaPCEgcgv/jZp6+f7DS0+Ynb8=
|
||||
github.com/fluxcd/pkg/gittestserver v0.26.0/go.mod h1:7fybYb0yej1fFNiF1ohs0Jr0XzyaZQ/cRh3AFEoCtuc=
|
||||
github.com/fluxcd/pkg/kustomize v1.28.0 h1:0RuFVczJRabbt8frHZ/ql8aqte6BOOKk274O09l6/hE=
|
||||
github.com/fluxcd/pkg/kustomize v1.28.0/go.mod h1:cW08mnngSP8MJYb6mDmMvxH8YjNATdiML0udb37dk+M=
|
||||
github.com/fluxcd/pkg/oci v0.63.0 h1:ZPKTT2C+gWYjhP63xC76iTPdYE9w3ABcsDq77uhAgwo=
|
||||
github.com/fluxcd/pkg/oci v0.63.0/go.mod h1:qMPz4njvm6hJzdyGSb8ydSqrapXxTQwJonxHIsdeXSQ=
|
||||
github.com/fluxcd/pkg/runtime v0.103.0 h1:J5y5GPhWdkyqIUBlaI1FP2N02TtZmsjbWhhZubuTSFk=
|
||||
|
|
@ -504,6 +502,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
|
|||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/rycli/fluxcd-pkg/kustomize v0.0.0-20260329192052-94b031e1aca6 h1:a231NZKoN+nuPkqivk8u0kKA6OaWdB30CCurQigx/Tg=
|
||||
github.com/rycli/fluxcd-pkg/kustomize v0.0.0-20260329192052-94b031e1aca6/go.mod h1:cW08mnngSP8MJYb6mDmMvxH8YjNATdiML0udb37dk+M=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import (
|
|||
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||
"github.com/fluxcd/pkg/kustomize"
|
||||
buildfs "github.com/fluxcd/pkg/kustomize/filesys"
|
||||
runclient "github.com/fluxcd/pkg/runtime/client"
|
||||
ssautil "github.com/fluxcd/pkg/ssa/utils"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
|
|
@ -65,6 +66,65 @@ const (
|
|||
|
||||
var defaultTimeout = 80 * time.Second
|
||||
|
||||
// fsBackend controls how the kustomization manifest is generated
|
||||
// and which filesystem is used for the kustomize build.
|
||||
type fsBackend interface {
|
||||
Generate(gen *kustomize.Generator, dirPath string) (filesys.FileSystem, string, kustomize.Action, error)
|
||||
Cleanup(dirPath string, action kustomize.Action) error
|
||||
}
|
||||
|
||||
// onDiskFsBackend writes to the source directory.
|
||||
type onDiskFsBackend struct{}
|
||||
|
||||
func (onDiskFsBackend) Generate(gen *kustomize.Generator, dirPath string) (filesys.FileSystem, string, kustomize.Action, error) {
|
||||
action, err := gen.WriteFile(dirPath, kustomize.WithSaveOriginalKustomization())
|
||||
if err != nil {
|
||||
return nil, "", action, err
|
||||
}
|
||||
return filesys.MakeFsOnDisk(), dirPath, action, nil
|
||||
}
|
||||
|
||||
func (onDiskFsBackend) Cleanup(dirPath string, action kustomize.Action) error {
|
||||
return kustomize.CleanDirectory(dirPath, action)
|
||||
}
|
||||
|
||||
// inMemoryFsBackend builds in an in-memory filesystem without modifying the source directory.
|
||||
type inMemoryFsBackend struct{}
|
||||
|
||||
func (inMemoryFsBackend) Generate(gen *kustomize.Generator, dirPath string) (filesys.FileSystem, string, kustomize.Action, error) {
|
||||
manifest, kfilePath, action, err := gen.GenerateManifest(dirPath)
|
||||
if err != nil {
|
||||
return nil, "", action, err
|
||||
}
|
||||
|
||||
absDirPath, err := filepath.Abs(dirPath)
|
||||
if err != nil {
|
||||
return nil, "", action, fmt.Errorf("failed to resolve dirPath: %w", err)
|
||||
}
|
||||
absDirPath, err = filepath.EvalSymlinks(absDirPath)
|
||||
if err != nil {
|
||||
return nil, "", action, fmt.Errorf("failed to eval symlinks: %w", err)
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, "", action, fmt.Errorf("failed to get working directory: %w", err)
|
||||
}
|
||||
|
||||
diskFS, err := buildfs.MakeFsOnDiskSecure(cwd)
|
||||
if err != nil {
|
||||
return nil, "", action, fmt.Errorf("failed to create secure filesystem: %w", err)
|
||||
}
|
||||
fs := buildfs.MakeFsInMemory(diskFS)
|
||||
|
||||
if err := fs.WriteFile(filepath.Join(absDirPath, filepath.Base(kfilePath)), manifest); err != nil {
|
||||
return nil, "", action, err
|
||||
}
|
||||
return fs, absDirPath, action, nil
|
||||
}
|
||||
|
||||
func (inMemoryFsBackend) Cleanup(string, kustomize.Action) error { return nil }
|
||||
|
||||
// Builder builds yaml manifests
|
||||
// It retrieves the kustomization object from the k8s cluster
|
||||
// and overlays the manifests with the resources specified in the resourcesPath
|
||||
|
|
@ -88,6 +148,7 @@ type Builder struct {
|
|||
localSources map[string]string
|
||||
// diff needs to handle kustomizations one by one
|
||||
singleKustomization bool
|
||||
fsBackend fsBackend
|
||||
}
|
||||
|
||||
// BuilderOptionFunc is a function that configures a Builder
|
||||
|
|
@ -198,6 +259,16 @@ func WithLocalSources(localSources map[string]string) BuilderOptionFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// WithInMemoryBuild sets the in-memory build backend
|
||||
func WithInMemoryBuild(inMemoryBuild bool) BuilderOptionFunc {
|
||||
return func(b *Builder) error {
|
||||
if inMemoryBuild {
|
||||
b.fsBackend = inMemoryFsBackend{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSingleKustomization sets the single kustomization field to true
|
||||
func WithSingleKustomization() BuilderOptionFunc {
|
||||
return func(b *Builder) error {
|
||||
|
|
@ -223,6 +294,14 @@ func withSpinnerFrom(in *Builder) BuilderOptionFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// withFsBackend sets the build backend
|
||||
func withFsBackend(s fsBackend) BuilderOptionFunc {
|
||||
return func(b *Builder) error {
|
||||
b.fsBackend = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withKustomization sets the kustomization field
|
||||
func withKustomization(k *kustomizev1.Kustomization) BuilderOptionFunc {
|
||||
return func(b *Builder) error {
|
||||
|
|
@ -258,6 +337,10 @@ func NewBuilder(name, resources string, opts ...BuilderOptionFunc) (*Builder, er
|
|||
b.timeout = defaultTimeout
|
||||
}
|
||||
|
||||
if b.fsBackend == nil {
|
||||
b.fsBackend = onDiskFsBackend{}
|
||||
}
|
||||
|
||||
if b.dryRun && b.kustomizationFile == "" && b.kustomization == nil {
|
||||
return nil, fmt.Errorf("kustomization file is required for dry-run")
|
||||
}
|
||||
|
|
@ -378,9 +461,9 @@ func (b *Builder) build() (m resmap.ResMap, err error) {
|
|||
b.kustomization = k
|
||||
|
||||
// generate kustomization.yaml if needed
|
||||
action, er := b.generate(*k, b.resourcesPath)
|
||||
buildFS, buildDir, action, er := b.generate(*k, b.resourcesPath)
|
||||
if er != nil {
|
||||
errf := kustomize.CleanDirectory(b.resourcesPath, action)
|
||||
errf := b.fsBackend.Cleanup(b.resourcesPath, action)
|
||||
err = fmt.Errorf("failed to generate kustomization.yaml: %w", fmt.Errorf("%v %v", er, errf))
|
||||
return
|
||||
}
|
||||
|
|
@ -388,14 +471,14 @@ func (b *Builder) build() (m resmap.ResMap, err error) {
|
|||
b.action = action
|
||||
|
||||
defer func() {
|
||||
errf := b.Cancel()
|
||||
errf := b.fsBackend.Cleanup(b.resourcesPath, b.action)
|
||||
if err == nil {
|
||||
err = errf
|
||||
}
|
||||
}()
|
||||
|
||||
// build the kustomization
|
||||
m, err = b.do(ctx, *k, b.resourcesPath)
|
||||
m, err = b.do(ctx, *k, buildFS, buildDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -436,6 +519,7 @@ func (b *Builder) kustomizationBuild(k *kustomizev1.Kustomization) ([]*unstructu
|
|||
WithRecursive(b.recursive),
|
||||
WithLocalSources(b.localSources),
|
||||
WithDryRun(b.dryRun),
|
||||
withFsBackend(b.fsBackend),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -490,10 +574,10 @@ func (b *Builder) unMarshallKustomization() (*kustomizev1.Kustomization, error)
|
|||
return k, nil
|
||||
}
|
||||
|
||||
func (b *Builder) generate(kustomization kustomizev1.Kustomization, dirPath string) (kustomize.Action, error) {
|
||||
func (b *Builder) generate(kustomization kustomizev1.Kustomization, dirPath string) (filesys.FileSystem, string, kustomize.Action, error) {
|
||||
data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&kustomization)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, "", kustomize.UnchangedAction, err
|
||||
}
|
||||
|
||||
// a scanner will be used down the line to parse the list
|
||||
|
|
@ -505,12 +589,10 @@ func (b *Builder) generate(kustomization kustomizev1.Kustomization, dirPath stri
|
|||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
return gen.WriteFile(dirPath, kustomize.WithSaveOriginalKustomization())
|
||||
return b.fsBackend.Generate(gen, dirPath)
|
||||
}
|
||||
|
||||
func (b *Builder) do(ctx context.Context, kustomization kustomizev1.Kustomization, dirPath string) (resmap.ResMap, error) {
|
||||
fs := filesys.MakeFsOnDisk()
|
||||
|
||||
func (b *Builder) do(ctx context.Context, kustomization kustomizev1.Kustomization, fs filesys.FileSystem, dirPath string) (resmap.ResMap, error) {
|
||||
// acquire the lock
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
|
@ -734,12 +816,7 @@ func (b *Builder) Cancel() error {
|
|||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
err := kustomize.CleanDirectory(b.resourcesPath, b.action)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return b.fsBackend.Cleanup(b.resourcesPath, b.action)
|
||||
}
|
||||
|
||||
func (b *Builder) StartSpinner() error {
|
||||
|
|
|
|||
|
|
@ -18,12 +18,15 @@ package build
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/kustomize"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
|
@ -611,3 +614,229 @@ func Test_kustomizationPath(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// chdirTemp changes to the given directory and restores the original on cleanup.
|
||||
func chdirTemp(t *testing.T, dir string) {
|
||||
t.Helper()
|
||||
orig, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { os.Chdir(orig) })
|
||||
}
|
||||
|
||||
func Test_inMemoryFsBackend_Generate(t *testing.T) {
|
||||
srcDir := t.TempDir()
|
||||
chdirTemp(t, srcDir)
|
||||
|
||||
kusYAML := `apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- configmap.yaml
|
||||
`
|
||||
cmYAML := `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-cm
|
||||
data:
|
||||
key: value
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(srcDir, "kustomization.yaml"), []byte(kusYAML), 0o644); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(srcDir, "configmap.yaml"), []byte(cmYAML), 0o644); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
// snapshot source dir
|
||||
beforeFiles := map[string]string{}
|
||||
filepath.Walk(srcDir, func(p string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() {
|
||||
return err
|
||||
}
|
||||
data, _ := os.ReadFile(p)
|
||||
rel, _ := filepath.Rel(srcDir, p)
|
||||
beforeFiles[rel] = string(data)
|
||||
return nil
|
||||
})
|
||||
|
||||
ks := unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"apiVersion": "kustomize.toolkit.fluxcd.io/v1",
|
||||
"kind": "Kustomization",
|
||||
"metadata": map[string]interface{}{"name": "test", "namespace": "default"},
|
||||
"spec": map[string]interface{}{
|
||||
"targetNamespace": "my-ns",
|
||||
},
|
||||
}}
|
||||
gen := kustomize.NewGenerator(srcDir, ks)
|
||||
|
||||
backend := inMemoryFsBackend{}
|
||||
fs, dir, action, err := backend.Generate(gen, srcDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate: %v", err)
|
||||
}
|
||||
|
||||
if action != kustomize.UnchangedAction {
|
||||
t.Errorf("expected UnchangedAction, got %q", action)
|
||||
}
|
||||
|
||||
// kustomization.yaml should contain the merged targetNamespace
|
||||
data, err := fs.ReadFile(filepath.Join(dir, "kustomization.yaml"))
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile kustomization.yaml: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(data), "my-ns") {
|
||||
t.Errorf("expected kustomization to contain targetNamespace, got:\n%s", data)
|
||||
}
|
||||
|
||||
// resource file should be readable from disk through the memory fs
|
||||
data, err = fs.ReadFile(filepath.Join(dir, "configmap.yaml"))
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile configmap.yaml: %v", err)
|
||||
}
|
||||
if diff := cmp.Diff(string(data), cmYAML); diff != "" {
|
||||
t.Errorf("configmap mismatch: (-got +want)%s", diff)
|
||||
}
|
||||
|
||||
// source directory must be unmodified
|
||||
afterFiles := map[string]string{}
|
||||
filepath.Walk(srcDir, func(p string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() {
|
||||
return err
|
||||
}
|
||||
data, _ := os.ReadFile(p)
|
||||
rel, _ := filepath.Rel(srcDir, p)
|
||||
afterFiles[rel] = string(data)
|
||||
return nil
|
||||
})
|
||||
if diff := cmp.Diff(afterFiles, beforeFiles); diff != "" {
|
||||
t.Errorf("source directory was modified: (-got +want)%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_inMemoryFsBackend_Generate_parentRef(t *testing.T) {
|
||||
// tmpDir/
|
||||
// configmap.yaml (referenced as ../../configmap.yaml)
|
||||
// overlay/sub/kustomization.yaml
|
||||
tmpDir := t.TempDir()
|
||||
chdirTemp(t, tmpDir)
|
||||
|
||||
cmYAML := `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: parent-cm
|
||||
data:
|
||||
key: value
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, "configmap.yaml"), []byte(cmYAML), 0o644); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
overlayDir := filepath.Join(tmpDir, "overlay", "sub")
|
||||
if err := os.MkdirAll(overlayDir, 0o755); err != nil {
|
||||
t.Fatalf("mkdir: %v", err)
|
||||
}
|
||||
|
||||
kusYAML := `apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../configmap.yaml
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(overlayDir, "kustomization.yaml"), []byte(kusYAML), 0o644); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
ks := unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"apiVersion": "kustomize.toolkit.fluxcd.io/v1",
|
||||
"kind": "Kustomization",
|
||||
"metadata": map[string]interface{}{"name": "test", "namespace": "default"},
|
||||
"spec": map[string]interface{}{
|
||||
"targetNamespace": "parent-ns",
|
||||
},
|
||||
}}
|
||||
gen := kustomize.NewGenerator(overlayDir, ks)
|
||||
|
||||
backend := inMemoryFsBackend{}
|
||||
fs, dir, _, err := backend.Generate(gen, overlayDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate: %v", err)
|
||||
}
|
||||
|
||||
// ../../configmap.yaml must resolve through the disk layer
|
||||
m, err := kustomize.Build(fs, dir)
|
||||
if err != nil {
|
||||
t.Fatalf("kustomize.Build failed (parent ref not resolved): %v", err)
|
||||
}
|
||||
|
||||
resources := m.Resources()
|
||||
if len(resources) != 1 {
|
||||
t.Fatalf("expected 1 resource, got %d", len(resources))
|
||||
}
|
||||
if resources[0].GetName() != "parent-cm" {
|
||||
t.Errorf("expected resource name parent-cm, got %s", resources[0].GetName())
|
||||
}
|
||||
if resources[0].GetNamespace() != "parent-ns" {
|
||||
t.Errorf("expected namespace parent-ns, got %s", resources[0].GetNamespace())
|
||||
}
|
||||
}
|
||||
|
||||
func Test_inMemoryFsBackend_Generate_outsideCwd(t *testing.T) {
|
||||
// Two sibling temp dirs: one for the source tree, one as cwd.
|
||||
// The kustomization references a file that exists on disk but is
|
||||
// outside cwd, so the secure filesystem must reject it.
|
||||
//
|
||||
// parentDir/
|
||||
// outside/configmap.yaml (exists but outside cwd)
|
||||
// cwd/overlay/kustomization.yaml (references ../../outside/configmap.yaml)
|
||||
parentDir := t.TempDir()
|
||||
|
||||
outsideDir := filepath.Join(parentDir, "outside")
|
||||
if err := os.MkdirAll(outsideDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(outsideDir, "configmap.yaml"), []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: outside-cm
|
||||
`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cwdDir := filepath.Join(parentDir, "cwd")
|
||||
overlayDir := filepath.Join(cwdDir, "overlay")
|
||||
if err := os.MkdirAll(overlayDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(overlayDir, "kustomization.yaml"), []byte(`apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../outside/configmap.yaml
|
||||
`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set cwd to cwdDir so the secure root excludes outsideDir.
|
||||
chdirTemp(t, cwdDir)
|
||||
|
||||
ks := unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"apiVersion": "kustomize.toolkit.fluxcd.io/v1",
|
||||
"kind": "Kustomization",
|
||||
"metadata": map[string]interface{}{"name": "test", "namespace": "default"},
|
||||
}}
|
||||
gen := kustomize.NewGenerator(overlayDir, ks)
|
||||
|
||||
backend := inMemoryFsBackend{}
|
||||
fs, dir, _, err := backend.Generate(gen, overlayDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate: %v", err)
|
||||
}
|
||||
|
||||
// Build must fail because the resource is outside the secure root.
|
||||
_, err = kustomize.Build(fs, dir)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when referencing resource outside cwd, got nil")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ func (b *Builder) kustomizationDiff(kustomization *kustomizev1.Kustomization) (s
|
|||
WithRecursive(b.recursive),
|
||||
WithLocalSources(b.localSources),
|
||||
WithSingleKustomization(),
|
||||
withFsBackend(b.fsBackend),
|
||||
)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue