Recursively build and diff Kustomizations
Signed-off-by: Boris Kreitchman <bkreitch@gmail.com>
This commit is contained in:
@@ -53,7 +53,12 @@ flux build kustomization my-app --path ./path/to/local/manifests \
|
|||||||
# Exclude files by providing a comma separated list of entries that follow the .gitignore pattern fromat.
|
# Exclude files by providing a comma separated list of entries that follow the .gitignore pattern fromat.
|
||||||
flux build kustomization my-app --path ./path/to/local/manifests \
|
flux build kustomization my-app --path ./path/to/local/manifests \
|
||||||
--kustomization-file ./path/to/local/my-app.yaml \
|
--kustomization-file ./path/to/local/my-app.yaml \
|
||||||
--ignore-paths "/to_ignore/**/*.yaml,ignore.yaml"`,
|
--ignore-paths "/to_ignore/**/*.yaml,ignore.yaml
|
||||||
|
|
||||||
|
# Run recursively on all encountered Kustomizations
|
||||||
|
flux build kustomization my-app --path ./path/to/local/manifests \
|
||||||
|
--recursive \
|
||||||
|
--local-sources GitRepository/flux-system/my-repo=./path/to/local/git"`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||||
RunE: buildKsCmdRun,
|
RunE: buildKsCmdRun,
|
||||||
}
|
}
|
||||||
@@ -64,6 +69,8 @@ type buildKsFlags struct {
|
|||||||
ignorePaths []string
|
ignorePaths []string
|
||||||
dryRun bool
|
dryRun bool
|
||||||
strictSubst bool
|
strictSubst bool
|
||||||
|
recursive bool
|
||||||
|
localSources map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
var buildKsArgs buildKsFlags
|
var buildKsArgs buildKsFlags
|
||||||
@@ -75,6 +82,8 @@ func init() {
|
|||||||
buildKsCmd.Flags().BoolVar(&buildKsArgs.dryRun, "dry-run", false, "Dry run mode.")
|
buildKsCmd.Flags().BoolVar(&buildKsArgs.dryRun, "dry-run", false, "Dry run mode.")
|
||||||
buildKsCmd.Flags().BoolVar(&buildKsArgs.strictSubst, "strict-substitute", false,
|
buildKsCmd.Flags().BoolVar(&buildKsArgs.strictSubst, "strict-substitute", false,
|
||||||
"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.")
|
"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")
|
||||||
buildCmd.AddCommand(buildKsCmd)
|
buildCmd.AddCommand(buildKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +120,8 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) (err error) {
|
|||||||
build.WithNamespace(*kubeconfigArgs.Namespace),
|
build.WithNamespace(*kubeconfigArgs.Namespace),
|
||||||
build.WithIgnore(buildKsArgs.ignorePaths),
|
build.WithIgnore(buildKsArgs.ignorePaths),
|
||||||
build.WithStrictSubstitute(buildKsArgs.strictSubst),
|
build.WithStrictSubstitute(buildKsArgs.strictSubst),
|
||||||
|
build.WithRecursive(buildKsArgs.recursive),
|
||||||
|
build.WithLocalSources(buildKsArgs.localSources),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
builder, err = build.NewBuilder(name, buildKsArgs.path,
|
builder, err = build.NewBuilder(name, buildKsArgs.path,
|
||||||
@@ -119,6 +130,8 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) (err error) {
|
|||||||
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
|
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
|
||||||
build.WithIgnore(buildKsArgs.ignorePaths),
|
build.WithIgnore(buildKsArgs.ignorePaths),
|
||||||
build.WithStrictSubstitute(buildKsArgs.strictSubst),
|
build.WithStrictSubstitute(buildKsArgs.strictSubst),
|
||||||
|
build.WithRecursive(buildKsArgs.recursive),
|
||||||
|
build.WithLocalSources(buildKsArgs.localSources),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ func TestBuildKustomization(t *testing.T) {
|
|||||||
resultFile: "./testdata/build-kustomization/podinfo-with-ignore-result.yaml",
|
resultFile: "./testdata/build-kustomization/podinfo-with-ignore-result.yaml",
|
||||||
assertFunc: "assertGoldenTemplateFile",
|
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",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := map[string]string{
|
tmpl := map[string]string{
|
||||||
@@ -157,6 +163,12 @@ spec:
|
|||||||
resultFile: "./testdata/build-kustomization/podinfo-with-var-substitution-result.yaml",
|
resultFile: "./testdata/build-kustomization/podinfo-with-var-substitution-result.yaml",
|
||||||
assertFunc: "assertGoldenTemplateFile",
|
assertFunc: "assertGoldenTemplateFile",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "build with recursive",
|
||||||
|
args: "build kustomization podinfo --kustomization-file " + tmpFile + " --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",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := map[string]string{
|
tmpl := map[string]string{
|
||||||
|
|||||||
@@ -44,7 +44,12 @@ flux diff kustomization my-app --path ./path/to/local/manifests \
|
|||||||
# Exclude files by providing a comma separated list of entries that follow the .gitignore pattern fromat.
|
# Exclude files by providing a comma separated list of entries that follow the .gitignore pattern fromat.
|
||||||
flux diff kustomization my-app --path ./path/to/local/manifests \
|
flux diff kustomization my-app --path ./path/to/local/manifests \
|
||||||
--kustomization-file ./path/to/local/my-app.yaml \
|
--kustomization-file ./path/to/local/my-app.yaml \
|
||||||
--ignore-paths "/to_ignore/**/*.yaml,ignore.yaml"`,
|
--ignore-paths "/to_ignore/**/*.yaml,ignore.yaml
|
||||||
|
|
||||||
|
# Run recursively on all encountered Kustomizations
|
||||||
|
flux diff kustomization my-app --path ./path/to/local/manifests \
|
||||||
|
--recursive \
|
||||||
|
--local-sources GitRepository/flux-system/my-repo=./path/to/local/git"`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||||
RunE: diffKsCmdRun,
|
RunE: diffKsCmdRun,
|
||||||
}
|
}
|
||||||
@@ -55,6 +60,8 @@ type diffKsFlags struct {
|
|||||||
ignorePaths []string
|
ignorePaths []string
|
||||||
progressBar bool
|
progressBar bool
|
||||||
strictSubst bool
|
strictSubst bool
|
||||||
|
recursive bool
|
||||||
|
localSources map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
var diffKsArgs diffKsFlags
|
var diffKsArgs diffKsFlags
|
||||||
@@ -66,6 +73,8 @@ func init() {
|
|||||||
diffKsCmd.Flags().StringVar(&diffKsArgs.kustomizationFile, "kustomization-file", "", "Path to the Flux Kustomization YAML file.")
|
diffKsCmd.Flags().StringVar(&diffKsArgs.kustomizationFile, "kustomization-file", "", "Path to the Flux Kustomization YAML file.")
|
||||||
diffKsCmd.Flags().BoolVar(&diffKsArgs.strictSubst, "strict-substitute", false,
|
diffKsCmd.Flags().BoolVar(&diffKsArgs.strictSubst, "strict-substitute", false,
|
||||||
"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.")
|
"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")
|
||||||
diffCmd.AddCommand(diffKsCmd)
|
diffCmd.AddCommand(diffKsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +110,9 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
build.WithProgressBar(),
|
build.WithProgressBar(),
|
||||||
build.WithIgnore(diffKsArgs.ignorePaths),
|
build.WithIgnore(diffKsArgs.ignorePaths),
|
||||||
build.WithStrictSubstitute(diffKsArgs.strictSubst),
|
build.WithStrictSubstitute(diffKsArgs.strictSubst),
|
||||||
|
build.WithRecursive(diffKsArgs.recursive),
|
||||||
|
build.WithLocalSources(diffKsArgs.localSources),
|
||||||
|
build.WithSingleKustomization(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
builder, err = build.NewBuilder(name, diffKsArgs.path,
|
builder, err = build.NewBuilder(name, diffKsArgs.path,
|
||||||
@@ -109,6 +121,9 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
|
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
|
||||||
build.WithIgnore(diffKsArgs.ignorePaths),
|
build.WithIgnore(diffKsArgs.ignorePaths),
|
||||||
build.WithStrictSubstitute(diffKsArgs.strictSubst),
|
build.WithStrictSubstitute(diffKsArgs.strictSubst),
|
||||||
|
build.WithRecursive(diffKsArgs.recursive),
|
||||||
|
build.WithLocalSources(diffKsArgs.localSources),
|
||||||
|
build.WithSingleKustomization(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +153,12 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case <-sigc:
|
case <-sigc:
|
||||||
|
if diffKsArgs.progressBar {
|
||||||
|
err := builder.StopSpinner()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
fmt.Println("Build cancelled... exiting.")
|
fmt.Println("Build cancelled... exiting.")
|
||||||
return builder.Cancel()
|
return builder.Cancel()
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
|
|||||||
@@ -97,6 +97,12 @@ func TestDiffKustomization(t *testing.T) {
|
|||||||
objectFile: "",
|
objectFile: "",
|
||||||
assert: assertGoldenFile("./testdata/diff-kustomization/nothing-is-deployed.golden"),
|
assert: assertGoldenFile("./testdata/diff-kustomization/nothing-is-deployed.golden"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "diff with recursive",
|
||||||
|
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo-with-my-app --progress-bar=false --recursive --local-sources GitRepository/default/podinfo=./testdata/build-kustomization",
|
||||||
|
objectFile: "./testdata/diff-kustomization/my-app.yaml",
|
||||||
|
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-recursive.golden"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := map[string]string{
|
tmpl := map[string]string{
|
||||||
|
|||||||
@@ -429,7 +429,9 @@ func resetCmdArgs() {
|
|||||||
tail: -1,
|
tail: -1,
|
||||||
fluxNamespace: rootArgs.defaults.Namespace,
|
fluxNamespace: rootArgs.defaults.Namespace,
|
||||||
}
|
}
|
||||||
buildKsArgs = buildKsFlags{}
|
buildKsArgs = buildKsFlags{
|
||||||
|
localSources: map[string]string{},
|
||||||
|
}
|
||||||
checkArgs = checkFlags{}
|
checkArgs = checkFlags{}
|
||||||
createArgs = createFlags{}
|
createArgs = createFlags{}
|
||||||
deleteArgs = deleteFlags{}
|
deleteArgs = deleteFlags{}
|
||||||
|
|||||||
6
cmd/flux/testdata/build-kustomization/my-app/configmap.yaml
vendored
Normal file
6
cmd/flux/testdata/build-kustomization/my-app/configmap.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
var: test
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: my-app
|
||||||
29
cmd/flux/testdata/build-kustomization/podinfo-with-my-app-result.yaml
vendored
Normal file
29
cmd/flux/testdata/build-kustomization/podinfo-with-my-app-result.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: podinfo
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }}
|
||||||
|
name: my-app
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
force: true
|
||||||
|
interval: 5m0s
|
||||||
|
path: ./my-app
|
||||||
|
prune: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: podinfo
|
||||||
|
targetNamespace: default
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
var: test
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: my-app
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: default
|
||||||
|
name: my-app
|
||||||
|
namespace: default
|
||||||
|
---
|
||||||
4
cmd/flux/testdata/build-kustomization/podinfo-with-my-app/kustomization.yaml
vendored
Normal file
4
cmd/flux/testdata/build-kustomization/podinfo-with-my-app/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- ./my-app.yaml
|
||||||
14
cmd/flux/testdata/build-kustomization/podinfo-with-my-app/my-app.yaml
vendored
Normal file
14
cmd/flux/testdata/build-kustomization/podinfo-with-my-app/my-app.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: my-app
|
||||||
|
spec:
|
||||||
|
interval: 5m0s
|
||||||
|
path: ./my-app
|
||||||
|
force: true
|
||||||
|
prune: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: podinfo
|
||||||
|
targetNamespace: default
|
||||||
2
cmd/flux/testdata/diff-kustomization/diff-with-recursive.golden
vendored
Normal file
2
cmd/flux/testdata/diff-kustomization/diff-with-recursive.golden
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
📁 Kustomization/default/my-app changed
|
||||||
|
► ConfigMap/default/my-app created
|
||||||
18
cmd/flux/testdata/diff-kustomization/my-app.yaml
vendored
Normal file
18
cmd/flux/testdata/diff-kustomization/my-app.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: podinfo
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }}
|
||||||
|
name: my-app
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
interval: 5m0s
|
||||||
|
path: ./my-app
|
||||||
|
force: true
|
||||||
|
prune: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: podinfo
|
||||||
|
targetNamespace: default
|
||||||
@@ -25,11 +25,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/theckman/yacspin"
|
"github.com/theckman/yacspin"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@@ -51,13 +53,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
controllerName = "kustomize-controller"
|
controllerName = "kustomize-controller"
|
||||||
controllerGroup = "kustomize.toolkit.fluxcd.io"
|
controllerGroup = "kustomize.toolkit.fluxcd.io"
|
||||||
mask = "**SOPS**"
|
mask = "**SOPS**"
|
||||||
dockercfgSecretType = "kubernetes.io/dockerconfigjson"
|
dockercfgSecretType = "kubernetes.io/dockerconfigjson"
|
||||||
typeField = "type"
|
typeField = "type"
|
||||||
dataField = "data"
|
dataField = "data"
|
||||||
stringDataField = "stringData"
|
stringDataField = "stringData"
|
||||||
|
spinnerDryRunMessage = "running dry-run"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultTimeout = 80 * time.Second
|
var defaultTimeout = 80 * time.Second
|
||||||
@@ -81,6 +84,10 @@ type Builder struct {
|
|||||||
spinner *yacspin.Spinner
|
spinner *yacspin.Spinner
|
||||||
dryRun bool
|
dryRun bool
|
||||||
strictSubst bool
|
strictSubst bool
|
||||||
|
recursive bool
|
||||||
|
localSources map[string]string
|
||||||
|
// diff needs to handle kustomizations one by one
|
||||||
|
singleKustomization bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuilderOptionFunc is a function that configures a Builder
|
// BuilderOptionFunc is a function that configures a Builder
|
||||||
@@ -110,7 +117,7 @@ func WithProgressBar() BuilderOptionFunc {
|
|||||||
CharSet: yacspin.CharSets[59],
|
CharSet: yacspin.CharSets[59],
|
||||||
Suffix: "Kustomization diffing...",
|
Suffix: "Kustomization diffing...",
|
||||||
SuffixAutoColon: true,
|
SuffixAutoColon: true,
|
||||||
Message: "running dry-run",
|
Message: spinnerDryRunMessage,
|
||||||
StopCharacter: "✓",
|
StopCharacter: "✓",
|
||||||
StopColors: []string{"fgGreen"},
|
StopColors: []string{"fgGreen"},
|
||||||
}
|
}
|
||||||
@@ -175,6 +182,55 @@ func WithIgnore(ignore []string) BuilderOptionFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithRecursive sets the recursive field
|
||||||
|
func WithRecursive(recursive bool) BuilderOptionFunc {
|
||||||
|
return func(b *Builder) error {
|
||||||
|
b.recursive = recursive
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLocalSources sets the local sources field
|
||||||
|
func WithLocalSources(localSources map[string]string) BuilderOptionFunc {
|
||||||
|
return func(b *Builder) error {
|
||||||
|
b.localSources = localSources
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSingleKustomization sets the single kustomization field to true
|
||||||
|
func WithSingleKustomization() BuilderOptionFunc {
|
||||||
|
return func(b *Builder) error {
|
||||||
|
b.singleKustomization = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withClientConfigFrom copies client and restMapper fields
|
||||||
|
func withClientConfigFrom(in *Builder) BuilderOptionFunc {
|
||||||
|
return func(b *Builder) error {
|
||||||
|
b.client = in.client
|
||||||
|
b.restMapper = in.restMapper
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withClientConfigFrom copies spinner field
|
||||||
|
func withSpinnerFrom(in *Builder) BuilderOptionFunc {
|
||||||
|
return func(b *Builder) error {
|
||||||
|
b.spinner = in.spinner
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withKustomization sets the kustomization field
|
||||||
|
func withKustomization(k *kustomizev1.Kustomization) BuilderOptionFunc {
|
||||||
|
return func(b *Builder) error {
|
||||||
|
b.kustomization = k
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewBuilder returns a new Builder
|
// NewBuilder returns a new Builder
|
||||||
// It takes a kustomization name and a path to the resources
|
// It takes a kustomization name and a path to the resources
|
||||||
// It also takes a list of BuilderOptionFunc to configure the builder
|
// It also takes a list of BuilderOptionFunc to configure the builder
|
||||||
@@ -269,6 +325,27 @@ func (b *Builder) Build() ([]*unstructured.Unstructured, error) {
|
|||||||
ssautil.SetCommonMetadata(objects, m.Labels, m.Annotations)
|
ssautil.SetCommonMetadata(objects, m.Labels, m.Annotations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.recursive && !b.singleKustomization {
|
||||||
|
var objectsToAdd []*unstructured.Unstructured
|
||||||
|
for _, obj := range objects {
|
||||||
|
if isKustomization(obj) {
|
||||||
|
k, err := toKustomization(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !kustomizationsEqual(k, b.kustomization) {
|
||||||
|
subObjects, err := b.kustomizationBuild(k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objectsToAdd = append(objectsToAdd, subObjects...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objects = append(objects, objectsToAdd...)
|
||||||
|
}
|
||||||
|
|
||||||
return objects, nil
|
return objects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +358,11 @@ func (b *Builder) build() (m resmap.ResMap, err error) {
|
|||||||
if !b.dryRun {
|
if !b.dryRun {
|
||||||
liveKus, err = b.getKustomization(ctx)
|
liveKus, err = b.getKustomization(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get kustomization object: %w", err)
|
if !apierrors.IsNotFound(err) || b.kustomization == nil {
|
||||||
|
return nil, fmt.Errorf("failed to get kustomization object: %w", err)
|
||||||
|
}
|
||||||
|
// use provided Kustomization
|
||||||
|
liveKus = b.kustomization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
k, err := b.resolveKustomization(liveKus)
|
k, err := b.resolveKustomization(liveKus)
|
||||||
@@ -334,6 +415,46 @@ func (b *Builder) build() (m resmap.ResMap, err error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) kustomizationBuild(k *kustomizev1.Kustomization) ([]*unstructured.Unstructured, error) {
|
||||||
|
resourcesPath, err := b.kustomizationPath(k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
subBuilder, err := NewBuilder(k.Name, resourcesPath,
|
||||||
|
// use same client
|
||||||
|
withClientConfigFrom(b),
|
||||||
|
// kustomization will be used if there is no live kustomization
|
||||||
|
withKustomization(k),
|
||||||
|
WithTimeout(b.timeout),
|
||||||
|
WithNamespace(k.Namespace),
|
||||||
|
WithIgnore(b.ignore),
|
||||||
|
WithStrictSubstitute(b.strictSubst),
|
||||||
|
WithRecursive(b.recursive),
|
||||||
|
WithLocalSources(b.localSources),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return subBuilder.Build()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) kustomizationPath(k *kustomizev1.Kustomization) (string, error) {
|
||||||
|
sourceRef := k.Spec.SourceRef.DeepCopy()
|
||||||
|
if sourceRef.Namespace == "" {
|
||||||
|
sourceRef.Namespace = k.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceKey := sourceRef.String()
|
||||||
|
localPath, ok := b.localSources[sourceKey]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("cannot get local path for %s of kustomization %s", sourceKey, k.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(localPath, k.Spec.Path), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Builder) unMarshallKustomization() (*kustomizev1.Kustomization, error) {
|
func (b *Builder) unMarshallKustomization() (*kustomizev1.Kustomization, error) {
|
||||||
data, err := os.ReadFile(b.kustomizationFile)
|
data, err := os.ReadFile(b.kustomizationFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -423,6 +544,28 @@ func (b *Builder) do(ctx context.Context, kustomization kustomizev1.Kustomizatio
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isKustomization(object *unstructured.Unstructured) bool {
|
||||||
|
return strings.HasPrefix(object.GetAPIVersion(), kustomizev1.GroupVersion.Group+"/") &&
|
||||||
|
object.GetKind() == kustomizev1.KustomizationKind
|
||||||
|
}
|
||||||
|
|
||||||
|
func toKustomization(object *unstructured.Unstructured) (*kustomizev1.Kustomization, error) {
|
||||||
|
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert to unstructured: %w", err)
|
||||||
|
}
|
||||||
|
k := &kustomizev1.Kustomization{}
|
||||||
|
err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj, k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert to kustomization: %w", err)
|
||||||
|
}
|
||||||
|
return k, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func kustomizationsEqual(k1 *kustomizev1.Kustomization, k2 *kustomizev1.Kustomization) bool {
|
||||||
|
return k1.Name == k2.Name && k1.Namespace == k2.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Builder) setOwnerLabels(res *resource.Resource) error {
|
func (b *Builder) setOwnerLabels(res *resource.Resource) error {
|
||||||
labels := res.GetLabels()
|
labels := res.GetLabels()
|
||||||
|
|
||||||
@@ -583,12 +726,7 @@ func (b *Builder) Cancel() error {
|
|||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
err := b.stopSpinner()
|
err := kustomize.CleanDirectory(b.resourcesPath, b.action)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = kustomize.CleanDirectory(b.resourcesPath, b.action)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -596,7 +734,7 @@ func (b *Builder) Cancel() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) startSpinner() error {
|
func (b *Builder) StartSpinner() error {
|
||||||
if b.spinner == nil {
|
if b.spinner == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -609,7 +747,7 @@ func (b *Builder) startSpinner() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) stopSpinner() error {
|
func (b *Builder) StopSpinner() error {
|
||||||
if b.spinner == nil {
|
if b.spinner == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
@@ -361,3 +362,242 @@ func Test_ResolveKustomization(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_isKustomization(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expected bool
|
||||||
|
object *unstructured.Unstructured
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "flux kustomization",
|
||||||
|
object: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "kustomize.toolkit.fluxcd.io/v1",
|
||||||
|
"kind": "Kustomization",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "other kustomization",
|
||||||
|
object: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "kustomize.config.k8s.io/v1beta1",
|
||||||
|
"kind": "Kustomization",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong kind",
|
||||||
|
object: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "kustomize.toolkit.fluxcd.io/v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong object",
|
||||||
|
object: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
actual := isKustomization(tt.object)
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Fatalf("got '%v', want '%v'", actual, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_kustomizationsEqual(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
kustomization1 *kustomizev1.Kustomization
|
||||||
|
kustomization2 *kustomizev1.Kustomization
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "equal",
|
||||||
|
kustomization1: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "podinfo",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kustomization2: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "podinfo",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong name",
|
||||||
|
kustomization1: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "podinfo",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kustomization2: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-app",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong namespace",
|
||||||
|
kustomization1: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "podinfo",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kustomization2: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "podinfo",
|
||||||
|
Namespace: "my-ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong name and namespace",
|
||||||
|
kustomization1: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "podinfo",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kustomization2: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-app",
|
||||||
|
Namespace: "my-ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
actual := kustomizationsEqual(tt.kustomization1, tt.kustomization2)
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Fatalf("got '%v', want '%v'", actual, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_kustomizationPath(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
kustomization *kustomizev1.Kustomization
|
||||||
|
expected string
|
||||||
|
wantErr bool
|
||||||
|
errString string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "full repo",
|
||||||
|
kustomization: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-app",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
Spec: kustomizev1.KustomizationSpec{
|
||||||
|
Path: "my-path",
|
||||||
|
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
||||||
|
Kind: "GitRepository",
|
||||||
|
Name: "my-repo",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "path/to/local/git/my-path",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "repo without namespace",
|
||||||
|
kustomization: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-app",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
Spec: kustomizev1.KustomizationSpec{
|
||||||
|
Path: "my-path",
|
||||||
|
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
||||||
|
Kind: "GitRepository",
|
||||||
|
Name: "my-repo",
|
||||||
|
Namespace: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "path/to/local/git/my-path",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "repo not found",
|
||||||
|
kustomization: &kustomizev1.Kustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-app",
|
||||||
|
Namespace: "flux-system",
|
||||||
|
},
|
||||||
|
Spec: kustomizev1.KustomizationSpec{
|
||||||
|
Path: "my-path",
|
||||||
|
SourceRef: kustomizev1.CrossNamespaceSourceReference{
|
||||||
|
Kind: "GitRepository",
|
||||||
|
Name: "my-repo",
|
||||||
|
Namespace: "my-ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errString: "cannot get local path",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &Builder{
|
||||||
|
name: "podinfo",
|
||||||
|
namespace: "flux-system",
|
||||||
|
localSources: map[string]string{
|
||||||
|
"GitRepository/flux-system/my-repo": "./path/to/local/git",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
actual, err := b.kustomizationPath(tt.kustomization)
|
||||||
|
if !tt.wantErr {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err '%s'", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Errorf("got '%v', want '%v'", actual, tt.expected)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error but got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(err.Error(), tt.errString) {
|
||||||
|
t.Errorf("expected error '%s' to contain string '%s'", err.Error(), tt.errString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,6 +57,22 @@ func (b *Builder) Manager() (*ssa.ResourceManager, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) Diff() (string, bool, error) {
|
func (b *Builder) Diff() (string, bool, error) {
|
||||||
|
err := b.StartSpinner()
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
output, createdOrDrifted, diffErr := b.diff()
|
||||||
|
|
||||||
|
err = b.StopSpinner()
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, createdOrDrifted, diffErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) diff() (string, bool, error) {
|
||||||
output := strings.Builder{}
|
output := strings.Builder{}
|
||||||
createdOrDrifted := false
|
createdOrDrifted := false
|
||||||
objects, err := b.Build()
|
objects, err := b.Build()
|
||||||
@@ -77,11 +93,6 @@ func (b *Builder) Diff() (string, bool, error) {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
err = b.startSpinner()
|
|
||||||
if err != nil {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var diffErrs []error
|
var diffErrs []error
|
||||||
// create an inventory of objects to be reconciled
|
// create an inventory of objects to be reconciled
|
||||||
newInventory := newInventory()
|
newInventory := newInventory()
|
||||||
@@ -127,6 +138,30 @@ func (b *Builder) Diff() (string, bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addObjectsToInventory(newInventory, change)
|
addObjectsToInventory(newInventory, change)
|
||||||
|
|
||||||
|
if b.recursive && isKustomization(obj) && change.Action != ssa.CreatedAction {
|
||||||
|
k, err := toKustomization(obj)
|
||||||
|
if err != nil {
|
||||||
|
return "", createdOrDrifted, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !kustomizationsEqual(k, b.kustomization) {
|
||||||
|
subOutput, subCreatedOrDrifted, err := b.kustomizationDiff(k)
|
||||||
|
if err != nil {
|
||||||
|
diffErrs = append(diffErrs, err)
|
||||||
|
}
|
||||||
|
if subCreatedOrDrifted {
|
||||||
|
createdOrDrifted = true
|
||||||
|
output.WriteString(bunt.Sprint(fmt.Sprintf("📁 %s changed\n", ssautil.FmtUnstructured(obj))))
|
||||||
|
output.WriteString(subOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
// finished with Kustomization diff
|
||||||
|
if b.spinner != nil {
|
||||||
|
b.spinner.Message(spinnerDryRunMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.spinner != nil {
|
if b.spinner != nil {
|
||||||
@@ -149,12 +184,43 @@ func (b *Builder) Diff() (string, bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.stopSpinner()
|
return output.String(), createdOrDrifted, errors.Reduce(errors.Flatten(errors.NewAggregate(diffErrs)))
|
||||||
if err != nil {
|
}
|
||||||
return "", createdOrDrifted, err
|
|
||||||
|
func (b *Builder) kustomizationDiff(kustomization *kustomizev1.Kustomization) (string, bool, error) {
|
||||||
|
if b.spinner != nil {
|
||||||
|
b.spinner.Message(fmt.Sprintf("%s in %s", spinnerDryRunMessage, kustomization.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
return output.String(), createdOrDrifted, errors.Reduce(errors.Flatten(errors.NewAggregate(diffErrs)))
|
sourceRef := kustomization.Spec.SourceRef.DeepCopy()
|
||||||
|
if sourceRef.Namespace == "" {
|
||||||
|
sourceRef.Namespace = kustomization.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceKey := sourceRef.String()
|
||||||
|
localPath, ok := b.localSources[sourceKey]
|
||||||
|
if !ok {
|
||||||
|
return "", false, fmt.Errorf("cannot get local path for %s of kustomization %s", sourceKey, kustomization.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resourcesPath := filepath.Join(localPath, kustomization.Spec.Path)
|
||||||
|
subBuilder, err := NewBuilder(kustomization.Name, resourcesPath,
|
||||||
|
// use same client and spinner
|
||||||
|
withClientConfigFrom(b),
|
||||||
|
withSpinnerFrom(b),
|
||||||
|
WithTimeout(b.timeout),
|
||||||
|
WithNamespace(kustomization.Namespace),
|
||||||
|
WithIgnore(b.ignore),
|
||||||
|
WithStrictSubstitute(b.strictSubst),
|
||||||
|
WithRecursive(b.recursive),
|
||||||
|
WithLocalSources(b.localSources),
|
||||||
|
WithSingleKustomization(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return subBuilder.diff()
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeYamls(liveObject, mergedObject *unstructured.Unstructured) (string, string, string, error) {
|
func writeYamls(liveObject, mergedObject *unstructured.Unstructured) (string, string, string, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user