Skip to content

Commit 5e4aa7c

Browse files
authored
Merge pull request #1339 from Adirio/scaffold-enhancement/machinery
Move scaffold machinery to its own internal package
2 parents c6e1ee1 + f96d453 commit 5e4aa7c

File tree

14 files changed

+1421
-140
lines changed

14 files changed

+1421
-140
lines changed

cmd/api.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131

3232
"sigs.k8s.io/kubebuilder/cmd/internal"
3333
"sigs.k8s.io/kubebuilder/internal/config"
34+
"sigs.k8s.io/kubebuilder/pkg/model"
3435
"sigs.k8s.io/kubebuilder/pkg/model/resource"
3536
"sigs.k8s.io/kubebuilder/pkg/scaffold"
3637
"sigs.k8s.io/kubebuilder/plugins/addon"
@@ -222,7 +223,7 @@ func (o *apiOptions) scaffolder(c *config.Config) (scaffold.Scaffolder, error) {
222223
}
223224

224225
// Load the requested plugins
225-
plugins := make([]scaffold.Plugin, 0)
226+
plugins := make([]model.Plugin, 0)
226227
switch strings.ToLower(o.pattern) {
227228
case "":
228229
// Default pattern

pkg/model/plugin.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
Copyright 2018 The Kubernetes 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 model
18+
19+
// Plugin is the interface that a plugin must implement
20+
// We will (later) have an ExecPlugin that implements this by exec-ing a binary
21+
type Plugin interface {
22+
// Pipe is the core plugin interface, that transforms a UniverseModel
23+
Pipe(*Universe) error
24+
}

pkg/scaffold/api.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"sigs.k8s.io/kubebuilder/internal/config"
2525
"sigs.k8s.io/kubebuilder/pkg/model"
2626
"sigs.k8s.io/kubebuilder/pkg/model/resource"
27+
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/machinery"
2728
controllerv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/v1/controller"
2829
crdv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/v1/crd"
2930
scaffoldv2 "sigs.k8s.io/kubebuilder/pkg/scaffold/v2"
@@ -38,7 +39,7 @@ type apiScaffolder struct {
3839
boilerplate string
3940
resource *resource.Resource
4041
// plugins is the list of plugins we should allow to transform our generated scaffolding
41-
plugins []Plugin
42+
plugins []model.Plugin
4243
// doResource indicates whether to scaffold API Resource or not
4344
doResource bool
4445
// doController indicates whether to scaffold controller files or not
@@ -50,7 +51,7 @@ func NewAPIScaffolder(
5051
boilerplate string,
5152
res *resource.Resource,
5253
doResource, doController bool,
53-
plugins []Plugin,
54+
plugins []model.Plugin,
5455
) Scaffolder {
5556
return &apiScaffolder{
5657
config: config,
@@ -90,7 +91,7 @@ func (s *apiScaffolder) scaffoldV1() error {
9091
fmt.Println(filepath.Join("pkg", "apis", s.resource.GroupPackageName, s.resource.Version,
9192
fmt.Sprintf("%s_types_test.go", strings.ToLower(s.resource.Kind))))
9293

93-
if err := NewScaffold().Execute(
94+
if err := machinery.NewScaffold().Execute(
9495
s.newUniverse(),
9596
&crdv1.Register{Resource: s.resource},
9697
&crdv1.Types{Resource: s.resource},
@@ -117,7 +118,7 @@ func (s *apiScaffolder) scaffoldV1() error {
117118
fmt.Println(filepath.Join("pkg", "controller", strings.ToLower(s.resource.Kind),
118119
fmt.Sprintf("%s_controller_test.go", strings.ToLower(s.resource.Kind))))
119120

120-
if err := NewScaffold().Execute(
121+
if err := machinery.NewScaffold().Execute(
121122
s.newUniverse(),
122123
&controllerv1.Controller{Resource: s.resource},
123124
&controllerv1.AddController{Resource: s.resource},
@@ -148,7 +149,7 @@ func (s *apiScaffolder) scaffoldV2() error {
148149
fmt.Sprintf("%s_types.go", strings.ToLower(s.resource.Kind))))
149150
}
150151

151-
if err := NewScaffold(s.plugins...).Execute(
152+
if err := machinery.NewScaffold(s.plugins...).Execute(
152153
s.newUniverse(),
153154
&scaffoldv2.Types{Resource: s.resource},
154155
&scaffoldv2.Group{Resource: s.resource},
@@ -162,7 +163,7 @@ func (s *apiScaffolder) scaffoldV2() error {
162163
}
163164

164165
kustomizationFile := &crdv2.Kustomization{Resource: s.resource}
165-
if err := NewScaffold().Execute(
166+
if err := machinery.NewScaffold().Execute(
166167
s.newUniverse(),
167168
kustomizationFile,
168169
&crdv2.KustomizeConfig{},
@@ -192,7 +193,7 @@ func (s *apiScaffolder) scaffoldV2() error {
192193
}
193194

194195
suiteTestFile := &controllerv2.SuiteTest{Resource: s.resource}
195-
if err := NewScaffold(s.plugins...).Execute(
196+
if err := machinery.NewScaffold(s.plugins...).Execute(
196197
s.newUniverse(),
197198
suiteTestFile,
198199
&controllerv2.Controller{Resource: s.resource},

pkg/scaffold/init.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"sigs.k8s.io/kubebuilder/internal/config"
2525
"sigs.k8s.io/kubebuilder/pkg/model"
2626
"sigs.k8s.io/kubebuilder/pkg/model/file"
27+
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/machinery"
2728
"sigs.k8s.io/kubebuilder/pkg/scaffold/project"
2829
scaffoldv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/v1"
2930
managerv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/v1/manager"
@@ -76,7 +77,7 @@ func (s *initScaffolder) Scaffold() error {
7677
return err
7778
}
7879

79-
if err := NewScaffold().Execute(
80+
if err := machinery.NewScaffold().Execute(
8081
s.newUniverse(), // Boilerplate is still empty by this call as desired
8182
&project.Boilerplate{
8283
Input: file.Input{Path: s.boilerplatePath},
@@ -93,7 +94,7 @@ func (s *initScaffolder) Scaffold() error {
9394
}
9495
s.boilerplate = string(boilerplateBytes)
9596

96-
if err := NewScaffold().Execute(
97+
if err := machinery.NewScaffold().Execute(
9798
s.newUniverse(),
9899
&project.GitIgnore{},
99100
&project.AuthProxyRole{},
@@ -113,7 +114,7 @@ func (s *initScaffolder) Scaffold() error {
113114
}
114115

115116
func (s *initScaffolder) scaffoldV1() error {
116-
return NewScaffold().Execute(
117+
return machinery.NewScaffold().Execute(
117118
s.newUniverse(),
118119
&project.KustomizeRBAC{},
119120
&scaffoldv1.KustomizeImagePatch{},
@@ -134,7 +135,7 @@ func (s *initScaffolder) scaffoldV1() error {
134135
}
135136

136137
func (s *initScaffolder) scaffoldV2() error {
137-
return NewScaffold().Execute(
138+
return machinery.NewScaffold().Execute(
138139
s.newUniverse(),
139140
&metricsauthv2.AuthProxyPatch{},
140141
&metricsauthv2.AuthProxyService{},
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
Copyright 2020 The Kubernetes 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 filesystem
18+
19+
import (
20+
"fmt"
21+
)
22+
23+
// This file contains the errors returned by the file system wrapper
24+
// They are not exported as they should not be created outside of this package
25+
// Exported functions are provided to check which kind of error was returned
26+
27+
// createDirectoryError is returned if the directory could not be created
28+
type createDirectoryError struct {
29+
path string
30+
err error
31+
}
32+
33+
func (e createDirectoryError) Error() string {
34+
return fmt.Sprintf("failed to create directory for %s: %v", e.path, e.err)
35+
}
36+
37+
// IsCreateDirectoryError checks if the returned error is because the directory
38+
// could not be created
39+
func IsCreateDirectoryError(e error) bool {
40+
_, ok := e.(createDirectoryError)
41+
return ok
42+
}
43+
44+
// createFileError is returned if the file could not be created
45+
type createFileError struct {
46+
path string
47+
err error
48+
}
49+
50+
func (e createFileError) Error() string {
51+
return fmt.Sprintf("failed to create %s: %v", e.path, e.err)
52+
}
53+
54+
// IsCreateFileError checks if the returned error is because the file could not
55+
// be created
56+
func IsCreateFileError(e error) bool {
57+
_, ok := e.(createFileError)
58+
return ok
59+
}
60+
61+
// writeFileError is returned if the filed could not be written to
62+
type writeFileError struct {
63+
path string
64+
err error
65+
}
66+
67+
func (e writeFileError) Error() string {
68+
return fmt.Sprintf("failed to write to %s: %v", e.path, e.err)
69+
}
70+
71+
// IsWriteFileError checks if the returned error is because the file could not
72+
// be written to
73+
func IsWriteFileError(e error) bool {
74+
_, ok := e.(writeFileError)
75+
return ok
76+
}
77+
78+
// closeFileError is returned if the file could not be created
79+
type closeFileError struct {
80+
path string
81+
err error
82+
}
83+
84+
func (e closeFileError) Error() string {
85+
return fmt.Sprintf("failed to close %s: %v", e.path, e.err)
86+
}
87+
88+
// IsCloseFileError checks if the returned error is because the file could not
89+
// be closed
90+
func IsCloseFileError(e error) bool {
91+
_, ok := e.(closeFileError)
92+
return ok
93+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
Copyright 2020 The Kubernetes 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 filesystem
18+
19+
import (
20+
"io"
21+
"os"
22+
"path/filepath"
23+
24+
"github.com/spf13/afero"
25+
)
26+
27+
const (
28+
createOrUpdate = os.O_WRONLY | os.O_CREATE | os.O_TRUNC
29+
30+
defaultDirectoryPermission os.FileMode = 0700
31+
defaultFilePermission os.FileMode = 0600
32+
)
33+
34+
// FileSystem is an IO wrapper to create files
35+
type FileSystem interface {
36+
// Exists checks if the file exists
37+
Exists(path string) (bool, error)
38+
39+
// Create creates the directory and file and returns a self-closing
40+
// io.Writer pointing to that file. If the file exists, it truncates it.
41+
Create(path string) (io.Writer, error)
42+
}
43+
44+
// fileSystem implements FileSystem
45+
type fileSystem struct {
46+
fs afero.Fs
47+
dirPerm os.FileMode
48+
filePerm os.FileMode
49+
fileMode int
50+
}
51+
52+
// New returns a new FileSystem
53+
func New(options ...Options) FileSystem {
54+
// Default values
55+
fs := fileSystem{
56+
fs: afero.NewOsFs(),
57+
dirPerm: defaultDirectoryPermission,
58+
filePerm: defaultFilePermission,
59+
fileMode: createOrUpdate,
60+
}
61+
62+
// Apply options
63+
for _, option := range options {
64+
option(&fs)
65+
}
66+
67+
return fs
68+
}
69+
70+
// Options configure FileSystem
71+
type Options func(system *fileSystem)
72+
73+
// DirectoryPermissions makes FileSystem.Create use the provided directory
74+
// permissions
75+
func DirectoryPermissions(dirPerm os.FileMode) Options {
76+
return func(fs *fileSystem) {
77+
fs.dirPerm = dirPerm
78+
}
79+
}
80+
81+
// FilePermissions makes FileSystem.Create use the provided file permissions
82+
func FilePermissions(filePerm os.FileMode) Options {
83+
return func(fs *fileSystem) {
84+
fs.filePerm = filePerm
85+
}
86+
}
87+
88+
// Exists implements FileSystem.Exists
89+
func (fs fileSystem) Exists(path string) (bool, error) {
90+
return afero.Exists(fs.fs, path)
91+
}
92+
93+
// Create implements FileSystem.Create
94+
func (fs fileSystem) Create(path string) (io.Writer, error) {
95+
// Create the directory if needed
96+
if err := fs.fs.MkdirAll(filepath.Dir(path), fs.dirPerm); err != nil {
97+
return nil, createDirectoryError{path, err}
98+
}
99+
100+
// Create or truncate the file
101+
wc, err := fs.fs.OpenFile(path, fs.fileMode, fs.filePerm)
102+
if err != nil {
103+
return nil, createFileError{path, err}
104+
}
105+
106+
return &file{path, wc}, nil
107+
}
108+
109+
// file implements io.Writer
110+
type file struct {
111+
path string
112+
io.WriteCloser
113+
}
114+
115+
// Write implements io.Writer.Write
116+
func (f *file) Write(content []byte) (n int, err error) {
117+
// Close the file when we end writing
118+
defer func() {
119+
if closeErr := f.Close(); err == nil && closeErr != nil {
120+
err = closeFileError{f.path, err}
121+
}
122+
}()
123+
124+
// Write the content
125+
n, err = f.WriteCloser.Write(content)
126+
if err != nil {
127+
return n, writeFileError{f.path, err}
128+
}
129+
130+
return n, nil
131+
}

0 commit comments

Comments
 (0)