Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cmd/flux/build_kustomization.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type buildKsFlags struct {
strictSubst bool
recursive bool
localSources map[string]string
inMemoryBuild bool
}

var buildKsArgs buildKsFlags
Expand All @@ -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", true,
"Use in-memory filesystem during build.")
buildCmd.AddCommand(buildKsCmd)
}

Expand Down Expand Up @@ -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,
Expand All @@ -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),
)
}

Expand Down
42 changes: 42 additions & 0 deletions cmd/flux/build_kustomization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ func TestBuildKustomization(t *testing.T) {
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build podinfo (on-disk)",
args: "build kustomization podinfo --path ./testdata/build-kustomization/podinfo --in-memory-build=false",
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build podinfo without service",
args: "build kustomization podinfo --path ./testdata/build-kustomization/delete-service",
Expand All @@ -70,12 +76,24 @@ func TestBuildKustomization(t *testing.T) {
resultFile: "./testdata/build-kustomization/podinfo-with-ignore-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build ignore (on-disk)",
args: "build kustomization podinfo --path ./testdata/build-kustomization/ignore --ignore-paths \"!configmap.yaml,!secret.yaml\" --in-memory-build=false",
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 (on-disk)",
args: "build kustomization podinfo --path ./testdata/build-kustomization/podinfo-with-my-app --recursive --local-sources GitRepository/default/podinfo=./testdata/build-kustomization --in-memory-build=false",
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
}

tmpl := map[string]string{
Expand Down Expand Up @@ -145,6 +163,12 @@ spec:
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build podinfo (on-disk)",
args: "build kustomization podinfo --kustomization-file " + tmpFile + " --path ./testdata/build-kustomization/podinfo --in-memory-build=false",
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",
Expand Down Expand Up @@ -175,6 +199,18 @@ spec:
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build with recursive (on-disk)",
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=false",
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build with recursive in dry-run mode (on-disk)",
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=false --dry-run",
resultFile: "./testdata/build-kustomization/podinfo-with-my-app-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
}

tmpl := map[string]string{
Expand Down Expand Up @@ -241,6 +277,12 @@ func TestBuildKustomizationPathNormalization(t *testing.T) {
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build with absolute path (on-disk)",
args: "build kustomization podinfo --path " + absTestDataPath + " --in-memory-build=false",
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",
Expand Down
5 changes: 5 additions & 0 deletions cmd/flux/diff_kustomization.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type diffKsFlags struct {
strictSubst bool
recursive bool
localSources map[string]string
inMemoryBuild bool
}

var diffKsArgs diffKsFlags
Expand All @@ -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", true,
"Use in-memory filesystem during build.")
diffCmd.AddCommand(diffKsCmd)
}

Expand Down Expand Up @@ -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,
Expand All @@ -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),
)
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
github.com/fluxcd/pkg/chartutil v1.23.0
github.com/fluxcd/pkg/envsubst v1.5.0
github.com/fluxcd/pkg/git v0.46.0
github.com/fluxcd/pkg/kustomize v1.28.0
github.com/fluxcd/pkg/kustomize v1.29.0
github.com/fluxcd/pkg/oci v0.63.0
github.com/fluxcd/pkg/runtime v0.103.0
github.com/fluxcd/pkg/sourceignore v0.17.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ 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/kustomize v1.29.0 h1:B/5hr9wX6INwaQAZ6BGKVNvZm++A6qjgorUfoaBAwPw=
github.com/fluxcd/pkg/kustomize v1.29.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=
Expand Down
109 changes: 93 additions & 16 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -378,24 +461,24 @@ 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
}

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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
Loading