Skip to content

Commit 3086ae4

Browse files
authored
Merge pull request #740 from fluxcd/refactor-generator
Refactor: Extract generator to internal package
2 parents 99b2eae + 731188e commit 3086ae4

20 files changed

+163
-99
lines changed

controllers/kustomization_controller.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import (
6060

6161
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
6262
"github.com/fluxcd/kustomize-controller/internal/decryptor"
63+
"github.com/fluxcd/kustomize-controller/internal/generator"
6364
)
6465

6566
// +kubebuilder:rbac:groups=kustomize.toolkit.fluxcd.io,resources=kustomizations,verbs=get;list;watch;create;update;patch;delete
@@ -593,7 +594,7 @@ func (r *KustomizationReconciler) getSource(ctx context.Context, kustomization k
593594
}
594595

595596
func (r *KustomizationReconciler) generate(kustomization kustomizev1.Kustomization, workDir string, dirPath string) error {
596-
_, err := NewGenerator(workDir, kustomization).WriteFile(dirPath)
597+
_, err := generator.NewGenerator(workDir, kustomization).WriteFile(dirPath)
597598
return err
598599
}
599600

@@ -614,7 +615,7 @@ func (r *KustomizationReconciler) build(ctx context.Context, workDir string, kus
614615
return nil, fmt.Errorf("error decrypting env sources: %w", err)
615616
}
616617

617-
m, err := secureBuildKustomization(workDir, dirPath, !r.NoRemoteBases)
618+
m, err := generator.Build(workDir, dirPath, !r.NoRemoteBases)
618619
if err != nil {
619620
return nil, fmt.Errorf("kustomize build failed: %w", err)
620621
}
@@ -642,7 +643,7 @@ func (r *KustomizationReconciler) build(ctx context.Context, workDir string, kus
642643

643644
// run variable substitutions
644645
if kustomization.Spec.PostBuild != nil {
645-
outRes, err := substituteVariables(ctx, r.Client, kustomization, res)
646+
outRes, err := generator.SubstituteVariables(ctx, r.Client, kustomization, res)
646647
if err != nil {
647648
return nil, fmt.Errorf("var substitution failed for '%s': %w", res.GetName(), err)
648649
}

internal/generator/build.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2020 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package generator
18+
19+
import (
20+
"fmt"
21+
"sync"
22+
23+
securefs "github.com/fluxcd/pkg/kustomize/filesys"
24+
"sigs.k8s.io/kustomize/api/krusty"
25+
"sigs.k8s.io/kustomize/api/resmap"
26+
kustypes "sigs.k8s.io/kustomize/api/types"
27+
"sigs.k8s.io/kustomize/kyaml/filesys"
28+
)
29+
30+
// buildMutex protects against kustomize concurrent map read/write panic
31+
var buildMutex sync.Mutex
32+
33+
// Build wraps krusty.MakeKustomizer with the following settings:
34+
// - secure on-disk FS denying operations outside root
35+
// - load files from outside the kustomization dir path
36+
// (but not outside root)
37+
// - disable plugins except for the builtin ones
38+
func Build(root, dirPath string, allowRemoteBases bool) (_ resmap.ResMap, err error) {
39+
var fs filesys.FileSystem
40+
41+
// Create secure FS for root with or without remote base support
42+
if allowRemoteBases {
43+
fs, err = securefs.MakeFsOnDiskSecureBuild(root)
44+
if err != nil {
45+
return nil, err
46+
}
47+
} else {
48+
fs, err = securefs.MakeFsOnDiskSecure(root)
49+
if err != nil {
50+
return nil, err
51+
}
52+
}
53+
54+
// Temporary workaround for concurrent map read and map write bug
55+
// https://github.com/kubernetes-sigs/kustomize/issues/3659
56+
buildMutex.Lock()
57+
defer buildMutex.Unlock()
58+
59+
// Kustomize tends to panic in unpredicted ways due to (accidental)
60+
// invalid object data; recover when this happens to ensure continuity of
61+
// operations
62+
defer func() {
63+
if r := recover(); r != nil {
64+
err = fmt.Errorf("recovered from kustomize build panic: %v", r)
65+
}
66+
}()
67+
68+
buildOptions := &krusty.Options{
69+
LoadRestrictions: kustypes.LoadRestrictionsNone,
70+
PluginConfig: kustypes.DisabledPluginConfig(),
71+
}
72+
73+
k := krusty.MakeKustomizer(buildOptions)
74+
return k.Run(fs, dirPath)
75+
}

internal/generator/build_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Copyright 2022 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package generator
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/gomega"
23+
)
24+
25+
func Test_Buid(t *testing.T) {
26+
t.Run("remote build", func(t *testing.T) {
27+
g := NewWithT(t)
28+
29+
_, err := Build("testdata/remote", "testdata/remote", true)
30+
g.Expect(err).ToNot(HaveOccurred())
31+
})
32+
33+
t.Run("no remote build", func(t *testing.T) {
34+
g := NewWithT(t)
35+
36+
_, err := Build("testdata/remote", "testdata/remote", false)
37+
g.Expect(err).To(HaveOccurred())
38+
})
39+
}
40+
41+
func Test_Buid_panic(t *testing.T) {
42+
t.Run("build panic", func(t *testing.T) {
43+
g := NewWithT(t)
44+
45+
_, err := Build("testdata/panic", "testdata/panic", false)
46+
g.Expect(err).To(HaveOccurred())
47+
g.Expect(err.Error()).To(ContainSubstring("recovered from kustomize build panic"))
48+
// Run again to ensure the lock is released
49+
_, err = Build("testdata/panic", "testdata/panic", false)
50+
g.Expect(err).To(HaveOccurred())
51+
})
52+
}
53+
54+
func Test_Buid_rel_basedir(t *testing.T) {
55+
g := NewWithT(t)
56+
57+
_, err := Build("testdata/relbase", "testdata/relbase/clusters/staging/flux-system", false)
58+
g.Expect(err).ToNot(HaveOccurred())
59+
}

controllers/kustomization_generator.go renamed to internal/generator/generator.go

+4-55
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,24 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package controllers
17+
package generator
1818

1919
import (
2020
"encoding/json"
2121
"fmt"
2222
"os"
2323
"path/filepath"
24+
2425
"strings"
25-
"sync"
2626

27+
securefs "github.com/fluxcd/pkg/kustomize/filesys"
2728
"sigs.k8s.io/kustomize/api/konfig"
28-
"sigs.k8s.io/kustomize/api/krusty"
2929
"sigs.k8s.io/kustomize/api/provider"
30-
"sigs.k8s.io/kustomize/api/resmap"
3130
kustypes "sigs.k8s.io/kustomize/api/types"
32-
"sigs.k8s.io/kustomize/kyaml/filesys"
3331
"sigs.k8s.io/yaml"
3432

35-
"github.com/fluxcd/pkg/apis/kustomize"
36-
securefs "github.com/fluxcd/pkg/kustomize/filesys"
37-
3833
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
34+
"github.com/fluxcd/pkg/apis/kustomize"
3935
)
4036

4137
type KustomizeGenerator struct {
@@ -238,50 +234,3 @@ func adaptSelector(selector *kustomize.Selector) (output *kustypes.Selector) {
238234
}
239235
return
240236
}
241-
242-
// TODO: remove mutex when kustomize fixes the concurrent map read/write panic
243-
var kustomizeBuildMutex sync.Mutex
244-
245-
// secureBuildKustomization wraps krusty.MakeKustomizer with the following settings:
246-
// - secure on-disk FS denying operations outside root
247-
// - load files from outside the kustomization dir path
248-
// (but not outside root)
249-
// - disable plugins except for the builtin ones
250-
func secureBuildKustomization(root, dirPath string, allowRemoteBases bool) (_ resmap.ResMap, err error) {
251-
var fs filesys.FileSystem
252-
253-
// Create secure FS for root with or without remote base support
254-
if allowRemoteBases {
255-
fs, err = securefs.MakeFsOnDiskSecureBuild(root)
256-
if err != nil {
257-
return nil, err
258-
}
259-
} else {
260-
fs, err = securefs.MakeFsOnDiskSecure(root)
261-
if err != nil {
262-
return nil, err
263-
}
264-
}
265-
266-
// Temporary workaround for concurrent map read and map write bug
267-
// https://github.com/kubernetes-sigs/kustomize/issues/3659
268-
kustomizeBuildMutex.Lock()
269-
defer kustomizeBuildMutex.Unlock()
270-
271-
// Kustomize tends to panic in unpredicted ways due to (accidental)
272-
// invalid object data; recover when this happens to ensure continuity of
273-
// operations
274-
defer func() {
275-
if r := recover(); r != nil {
276-
err = fmt.Errorf("recovered from kustomize build panic: %v", r)
277-
}
278-
}()
279-
280-
buildOptions := &krusty.Options{
281-
LoadRestrictions: kustypes.LoadRestrictionsNone,
282-
PluginConfig: kustypes.DisabledPluginConfig(),
283-
}
284-
285-
k := krusty.MakeKustomizer(buildOptions)
286-
return k.Run(fs, dirPath)
287-
}

controllers/kustomization_generator_test.go renamed to internal/generator/generator_test.go

+2-38
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package controllers
17+
package generator
1818

1919
import (
2020
"os"
@@ -30,43 +30,7 @@ import (
3030
. "github.com/onsi/gomega"
3131
)
3232

33-
func Test_secureBuildKustomization(t *testing.T) {
34-
t.Run("remote build", func(t *testing.T) {
35-
g := NewWithT(t)
36-
37-
_, err := secureBuildKustomization("testdata/remote", "testdata/remote", true)
38-
g.Expect(err).ToNot(HaveOccurred())
39-
})
40-
41-
t.Run("no remote build", func(t *testing.T) {
42-
g := NewWithT(t)
43-
44-
_, err := secureBuildKustomization("testdata/remote", "testdata/remote", false)
45-
g.Expect(err).To(HaveOccurred())
46-
})
47-
}
48-
49-
func Test_secureBuildKustomization_panic(t *testing.T) {
50-
t.Run("build panic", func(t *testing.T) {
51-
g := NewWithT(t)
52-
53-
_, err := secureBuildKustomization("testdata/panic", "testdata/panic", false)
54-
g.Expect(err).To(HaveOccurred())
55-
g.Expect(err.Error()).To(ContainSubstring("recovered from kustomize build panic"))
56-
// Run again to ensure the lock is released
57-
_, err = secureBuildKustomization("testdata/panic", "testdata/panic", false)
58-
g.Expect(err).To(HaveOccurred())
59-
})
60-
}
61-
62-
func Test_secureBuildKustomization_rel_basedir(t *testing.T) {
63-
g := NewWithT(t)
64-
65-
_, err := secureBuildKustomization("testdata/relbase", "testdata/relbase/clusters/staging/flux-system", false)
66-
g.Expect(err).ToNot(HaveOccurred())
67-
}
68-
69-
func TestGeneratorWriteFile(t *testing.T) {
33+
func TestGenerator_WriteFile(t *testing.T) {
7034
tests := []struct {
7135
name string
7236
dir string

controllers/kustomization_varsub.go renamed to internal/generator/varsub.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
package controllers
1+
/*
2+
Copyright 2021 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package generator
218

319
import (
420
"context"
@@ -21,10 +37,10 @@ import (
2137
// the var names before substitution
2238
const varsubRegex = "^[_[:alpha:]][_[:alpha:][:digit:]]*$"
2339

24-
// substituteVariables replaces the vars with their values in the specified resource.
40+
// SubstituteVariables replaces the vars with their values in the specified resource.
2541
// If a resource is labeled or annotated with
2642
// 'kustomize.toolkit.fluxcd.io/substitute: disabled' the substitution is skipped.
27-
func substituteVariables(
43+
func SubstituteVariables(
2844
ctx context.Context,
2945
kubeClient client.Client,
3046
kustomization kustomizev1.Kustomization,

0 commit comments

Comments
 (0)