Skip to content

Commit 73cda2b

Browse files
authored
Merge pull request #2080 from Adirio/inject-filesystem
⚠️ Inject filesystem from CLI instead of creating several per command
2 parents 9742a11 + a2cc78c commit 73cda2b

File tree

33 files changed

+310
-201
lines changed

33 files changed

+310
-201
lines changed

pkg/cli/api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (c CLI) bindCreateAPI(ctx plugin.Context, cmd *cobra.Command) {
7676
return
7777
}
7878

79-
cfg, err := config.LoadInitialized()
79+
cfg, err := config.LoadInitialized(c.fs)
8080
if err != nil {
8181
cmdErr(cmd, err)
8282
return
@@ -88,6 +88,6 @@ func (c CLI) bindCreateAPI(ctx plugin.Context, cmd *cobra.Command) {
8888
subcommand.UpdateContext(&ctx)
8989
cmd.Long = ctx.Description
9090
cmd.Example = ctx.Examples
91-
cmd.RunE = runECmdFunc(cfg, subcommand,
91+
cmd.RunE = runECmdFunc(c.fs, cfg, subcommand,
9292
fmt.Sprintf("failed to create API with %q", plugin.KeyFor(createAPIPlugin)))
9393
}

pkg/cli/cli.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ import (
2121
"os"
2222
"strings"
2323

24+
"github.com/spf13/afero"
2425
"github.com/spf13/cobra"
2526
"github.com/spf13/pflag"
2627

2728
internalconfig "sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
2829
"sigs.k8s.io/kubebuilder/v3/pkg/config"
2930
cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
31+
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
3032
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
3133
)
3234

@@ -88,6 +90,9 @@ type CLI struct { //nolint:maligned
8890

8991
// Root command.
9092
cmd *cobra.Command
93+
94+
// Underlying fs
95+
fs machinery.Filesystem
9196
}
9297

9398
// New creates a new CLI instance.
@@ -131,6 +136,7 @@ func newCLI(options ...Option) (*CLI, error) {
131136
defaultProjectVersion: cfgv3.Version,
132137
defaultPlugins: make(map[config.Version][]string),
133138
plugins: make(map[string]plugin.Plugin),
139+
fs: machinery.Filesystem{FS: afero.NewOsFs()},
134140
}
135141

136142
// Apply provided options.
@@ -188,9 +194,9 @@ func (c *CLI) getInfoFromFlags() (string, []string, error) {
188194
}
189195

190196
// getInfoFromConfigFile obtains the project version and plugin keys from the project config file.
191-
func getInfoFromConfigFile() (config.Version, []string, error) {
197+
func (c CLI) getInfoFromConfigFile() (config.Version, []string, error) {
192198
// Read the project configuration file
193-
projectConfig, err := internalconfig.Read()
199+
projectConfig, err := internalconfig.Read(c.fs)
194200
switch {
195201
case err == nil:
196202
case os.IsNotExist(err):
@@ -294,7 +300,7 @@ func (c *CLI) getInfo() error {
294300
return err
295301
}
296302
// Get project version and plugin info from project configuration file
297-
cfgProjectVersion, cfgPlugins, _ := getInfoFromConfigFile()
303+
cfgProjectVersion, cfgPlugins, _ := c.getInfoFromConfigFile()
298304
// We discard the error because not being able to read a project configuration file
299305
// is not fatal for some commands. The ones that require it need to check its existence.
300306

pkg/cli/cli_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ import (
2323

2424
. "github.com/onsi/ginkgo"
2525
. "github.com/onsi/gomega"
26+
"github.com/spf13/afero"
2627
"github.com/spf13/cobra"
2728

2829
"sigs.k8s.io/kubebuilder/v3/pkg/config"
2930
cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
3031
cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
32+
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
3133
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
3234
)
3335

@@ -585,6 +587,7 @@ var _ = Describe("CLI", func() {
585587
defaultPlugins: map[config.Version][]string{
586588
projectVersion: pluginKeys,
587589
},
590+
fs: machinery.Filesystem{FS: afero.NewMemMapFs()},
588591
}
589592
c.cmd = c.newRootCmd()
590593
Expect(c.getInfo()).To(Succeed())

pkg/cli/cmd_helpers.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/spf13/cobra"
2323

2424
"sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
25+
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
2526
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
2627
)
2728

@@ -48,12 +49,13 @@ func errCmdFunc(err error) func(*cobra.Command, []string) error {
4849
// runECmdFunc returns a cobra RunE function that runs subcommand and saves the
4950
// config, which may have been modified by subcommand.
5051
func runECmdFunc(
52+
fs machinery.Filesystem,
5153
c *config.Config,
5254
subcommand plugin.Subcommand,
5355
msg string,
5456
) func(*cobra.Command, []string) error {
5557
return func(*cobra.Command, []string) error {
56-
if err := subcommand.Run(); err != nil {
58+
if err := subcommand.Run(fs); err != nil {
5759
return fmt.Errorf("%s: %v", msg, err)
5860
}
5961
return c.Save()

pkg/cli/edit.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (c CLI) bindEdit(ctx plugin.Context, cmd *cobra.Command) {
7676
return
7777
}
7878

79-
cfg, err := config.LoadInitialized()
79+
cfg, err := config.LoadInitialized(c.fs)
8080
if err != nil {
8181
cmdErr(cmd, err)
8282
return
@@ -88,6 +88,6 @@ func (c CLI) bindEdit(ctx plugin.Context, cmd *cobra.Command) {
8888
subcommand.UpdateContext(&ctx)
8989
cmd.Long = ctx.Description
9090
cmd.Example = ctx.Examples
91-
cmd.RunE = runECmdFunc(cfg, subcommand,
91+
cmd.RunE = runECmdFunc(c.fs, cfg, subcommand,
9292
fmt.Sprintf("failed to edit project with %q", plugin.KeyFor(editPlugin)))
9393
}

pkg/cli/init.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (c CLI) bindInit(ctx plugin.Context, cmd *cobra.Command) {
136136
return
137137
}
138138

139-
cfg, err := internalconfig.New(c.projectVersion, internalconfig.DefaultPath)
139+
cfg, err := internalconfig.New(c.fs, c.projectVersion, internalconfig.DefaultPath)
140140
if err != nil {
141141
cmdErr(cmd, fmt.Errorf("unable to initialize the project configuration: %w", err))
142142
return
@@ -151,11 +151,11 @@ func (c CLI) bindInit(ctx plugin.Context, cmd *cobra.Command) {
151151
cmd.RunE = func(*cobra.Command, []string) error {
152152
// Check if a config is initialized in the command runner so the check
153153
// doesn't erroneously fail other commands used in initialized projects.
154-
_, err := internalconfig.Read()
154+
_, err := internalconfig.Read(c.fs)
155155
if err == nil || os.IsExist(err) {
156156
log.Fatal("config already initialized")
157157
}
158-
if err := subcommand.Run(); err != nil {
158+
if err := subcommand.Run(c.fs); err != nil {
159159
return fmt.Errorf("failed to initialize project with %q: %v", plugin.KeyFor(initPlugin), err)
160160
}
161161
return cfg.Save()

pkg/cli/internal/config/config.go

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ import (
2222
"os"
2323

2424
"github.com/spf13/afero"
25-
"sigs.k8s.io/yaml"
2625

2726
"sigs.k8s.io/kubebuilder/v3/pkg/config"
27+
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
28+
"sigs.k8s.io/yaml"
2829
)
2930

3031
const (
@@ -81,13 +82,13 @@ func readFrom(fs afero.Fs, path string) (config.Config, error) {
8182
}
8283

8384
// Read obtains the configuration from the default path but doesn't allow to persist changes
84-
func Read() (config.Config, error) {
85-
return ReadFrom(DefaultPath)
85+
func Read(fs machinery.Filesystem) (config.Config, error) {
86+
return ReadFrom(fs, DefaultPath)
8687
}
8788

8889
// ReadFrom obtains the configuration from the provided path but doesn't allow to persist changes
89-
func ReadFrom(path string) (config.Config, error) {
90-
return readFrom(afero.NewOsFs(), path)
90+
func ReadFrom(fs machinery.Filesystem, path string) (config.Config, error) {
91+
return readFrom(fs.FS, path)
9192
}
9293

9394
// Config extends model/config.Config allowing to persist changes
@@ -105,7 +106,7 @@ type Config struct {
105106
}
106107

107108
// New creates a new configuration that will be stored at the provided path
108-
func New(version config.Version, path string) (*Config, error) {
109+
func New(fs machinery.Filesystem, version config.Version, path string) (*Config, error) {
109110
cfg, err := config.New(version)
110111
if err != nil {
111112
return nil, err
@@ -115,37 +116,33 @@ func New(version config.Version, path string) (*Config, error) {
115116
Config: cfg,
116117
path: path,
117118
mustNotExist: true,
118-
fs: afero.NewOsFs(),
119+
fs: fs.FS,
119120
}, nil
120121
}
121122

122123
// Load obtains the configuration from the default path allowing to persist changes (Save method)
123-
func Load() (*Config, error) {
124-
return LoadFrom(DefaultPath)
124+
func Load(fs machinery.Filesystem) (*Config, error) {
125+
return LoadFrom(fs, DefaultPath)
125126
}
126127

127128
// LoadInitialized calls Load() but returns helpful error messages if the config
128129
// does not exist.
129-
func LoadInitialized() (*Config, error) {
130-
c, err := Load()
130+
func LoadInitialized(fs machinery.Filesystem) (*Config, error) {
131+
c, err := Load(fs)
131132
if os.IsNotExist(err) {
132133
return nil, errors.New("unable to find configuration file, project must be initialized")
133134
}
134135
return c, err
135136
}
136137

137138
// LoadFrom obtains the configuration from the provided path allowing to persist changes (Save method)
138-
func LoadFrom(path string) (*Config, error) {
139-
fs := afero.NewOsFs()
140-
c, err := readFrom(fs, path)
141-
return &Config{Config: c, path: path, fs: fs}, err
139+
func LoadFrom(fs machinery.Filesystem, path string) (*Config, error) {
140+
c, err := readFrom(fs.FS, path)
141+
return &Config{Config: c, path: path, fs: fs.FS}, err
142142
}
143143

144144
// Save saves the configuration information
145145
func (c Config) Save() error {
146-
if c.fs == nil {
147-
c.fs = afero.NewOsFs()
148-
}
149146
// If path is unset, it was created directly with `Config{}`
150147
if c.path == "" {
151148
return saveError{errors.New("no information where it should be stored, " +

pkg/cli/webhook.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (c CLI) bindCreateWebhook(ctx plugin.Context, cmd *cobra.Command) {
7676
return
7777
}
7878

79-
cfg, err := config.LoadInitialized()
79+
cfg, err := config.LoadInitialized(c.fs)
8080
if err != nil {
8181
cmdErr(cmd, err)
8282
return
@@ -88,6 +88,6 @@ func (c CLI) bindCreateWebhook(ctx plugin.Context, cmd *cobra.Command) {
8888
subcommand.UpdateContext(&ctx)
8989
cmd.Long = ctx.Description
9090
cmd.Example = ctx.Examples
91-
cmd.RunE = runECmdFunc(cfg, subcommand,
91+
cmd.RunE = runECmdFunc(c.fs, cfg, subcommand,
9292
fmt.Sprintf("failed to create webhook with %q", plugin.KeyFor(createWebhookPlugin)))
9393
}

pkg/machinery/filesystem.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright 2021 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 machinery
18+
19+
import (
20+
"github.com/spf13/afero"
21+
)
22+
23+
// Filesystem abstracts the underlying disk for scaffolding
24+
type Filesystem struct {
25+
FS afero.Fs
26+
}

pkg/plugin/interfaces.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/spf13/pflag"
2121

2222
"sigs.k8s.io/kubebuilder/v3/pkg/config"
23+
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
2324
)
2425

2526
// Plugin is an interface that defines the common base for all plugins
@@ -53,7 +54,7 @@ type Subcommand interface {
5354
// command line flags.
5455
BindFlags(*pflag.FlagSet)
5556
// Run runs the subcommand.
56-
Run() error
57+
Run(fs machinery.Filesystem) error
5758
// InjectConfig passes a config to a plugin. The plugin may modify the config.
5859
// Initializing, loading, and saving the config is managed by the cli package.
5960
InjectConfig(config.Config)

0 commit comments

Comments
 (0)