Skip to content

Commit

Permalink
Store abstraction for persisting Config
Browse files Browse the repository at this point in the history
- Use the cli filesystem to persist the project config
- Use PreRunE and PostRunE functions to handle configuration file loading and saving

Signed-off-by: Adrian Orive <[email protected]>
  • Loading branch information
Adirio committed Feb 4, 2021
1 parent 9eb1b07 commit 8b7e731
Show file tree
Hide file tree
Showing 22 changed files with 465 additions and 354 deletions.
17 changes: 7 additions & 10 deletions pkg/cli/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

"github.com/spf13/cobra"

"sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
yamlstore "sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
)

Expand Down Expand Up @@ -76,18 +76,15 @@ func (c cli) bindCreateAPI(ctx plugin.Context, cmd *cobra.Command) {
return
}

cfg, err := config.LoadInitialized()
if err != nil {
cmdErr(cmd, err)
return
}

subcommand := createAPIPlugin.GetCreateAPISubcommand()
subcommand.InjectConfig(cfg.Config)
subcommand.BindFlags(cmd.Flags())
subcommand.UpdateContext(&ctx)
cmd.Long = ctx.Description
cmd.Example = ctx.Examples
cmd.RunE = runECmdFunc(c.fs, cfg, subcommand,
fmt.Sprintf("failed to create API with %q", plugin.KeyFor(createAPIPlugin)))

cfg := yamlstore.New(c.fs)
msg := fmt.Sprintf("failed to create API with %q", plugin.KeyFor(createAPIPlugin))
cmd.PreRunE = preRunECmdFunc(subcommand, cfg, msg)
cmd.RunE = runECmdFunc(c.fs, subcommand, msg)
cmd.PostRunE = postRunECmdFunc(cfg, msg)
}
14 changes: 8 additions & 6 deletions pkg/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package cli

import (
"errors"
"fmt"
"os"
"strings"
Expand All @@ -25,8 +26,8 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"

internalconfig "sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
"sigs.k8s.io/kubebuilder/v3/pkg/config"
yamlstore "sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml"
cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
)
Expand Down Expand Up @@ -196,18 +197,19 @@ func (c *cli) getInfoFromFlags() (string, []string, error) {
}

// getInfoFromConfigFile obtains the project version and plugin keys from the project config file.
func getInfoFromConfigFile() (config.Version, []string, error) {
func (c cli) getInfoFromConfigFile() (config.Version, []string, error) {
// Read the project configuration file
projectConfig, err := internalconfig.Read()
cfg := yamlstore.New(c.fs)
err := cfg.Load()
switch {
case err == nil:
case os.IsNotExist(err):
case errors.Is(err, os.ErrNotExist):
return config.Version{}, nil, nil
default:
return config.Version{}, nil, err
}

return getInfoFromConfig(projectConfig)
return getInfoFromConfig(cfg.Config())
}

// getInfoFromConfig obtains the project version and plugin keys from the project config.
Expand Down Expand Up @@ -302,7 +304,7 @@ func (c *cli) getInfo() error {
return err
}
// Get project version and plugin info from project configuration file
cfgProjectVersion, cfgPlugins, _ := getInfoFromConfigFile()
cfgProjectVersion, cfgPlugins, _ := c.getInfoFromConfigFile()
// We discard the error because not being able to read a project configuration file
// is not fatal for some commands. The ones that require it need to check its existence.

Expand Down
2 changes: 2 additions & 0 deletions pkg/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/spf13/afero"
"github.com/spf13/cobra"

"sigs.k8s.io/kubebuilder/v3/pkg/config"
Expand Down Expand Up @@ -585,6 +586,7 @@ var _ = Describe("CLI", func() {
defaultPlugins: map[config.Version][]string{
projectVersion: pluginKeys,
},
fs: afero.NewMemMapFs(),
}
c.cmd = c.newRootCmd()
Expect(c.getInfo()).To(Succeed())
Expand Down
42 changes: 32 additions & 10 deletions pkg/cli/cmd_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ package cli

import (
"fmt"
"os"

"github.com/spf13/afero"
"github.com/spf13/cobra"

"sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
"sigs.k8s.io/kubebuilder/v3/pkg/config/store"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
)

Expand All @@ -46,18 +47,39 @@ func errCmdFunc(err error) func(*cobra.Command, []string) error {
}
}

// runECmdFunc returns a cobra RunE function that runs subcommand and saves the
// config, which may have been modified by subcommand.
func runECmdFunc(
fs afero.Fs,
c *config.Config,
subcommand plugin.Subcommand,
msg string,
) func(*cobra.Command, []string) error {
// preRunECmdFunc returns a cobra PreRunE function that loads the configuration file
// and injects it into the subcommand
func preRunECmdFunc(subcmd plugin.Subcommand, cfg store.Store, msg string) func(*cobra.Command, []string) error {
return func(*cobra.Command, []string) error {
err := cfg.Load()
if os.IsNotExist(err) {
return fmt.Errorf("%s: unable to find configuration file, project must be initialized", msg)
} else if err != nil {
return fmt.Errorf("%s: unable to load configuration file: %w", msg, err)
}

subcmd.InjectConfig(cfg.Config())
return nil
}
}

// runECmdFunc returns a cobra RunE function that runs subcommand
func runECmdFunc(fs afero.Fs, subcommand plugin.Subcommand, msg string) func(*cobra.Command, []string) error {
return func(*cobra.Command, []string) error {
if err := subcommand.Run(fs); err != nil {
return fmt.Errorf("%s: %v", msg, err)
}
return c.Save()
return nil
}
}

// postRunECmdFunc returns a cobra PostRunE function that saves the configuration file
func postRunECmdFunc(cfg store.Store, msg string) func(*cobra.Command, []string) error {
return func(*cobra.Command, []string) error {
err := cfg.Save()
if err != nil {
return fmt.Errorf("%s: unable to save configuration file: %w", msg, err)
}
return nil
}
}
17 changes: 7 additions & 10 deletions pkg/cli/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

"github.com/spf13/cobra"

"sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
yamlstore "sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
)

Expand Down Expand Up @@ -76,18 +76,15 @@ func (c cli) bindEdit(ctx plugin.Context, cmd *cobra.Command) {
return
}

cfg, err := config.LoadInitialized()
if err != nil {
cmdErr(cmd, err)
return
}

subcommand := editPlugin.GetEditSubcommand()
subcommand.InjectConfig(cfg.Config)
subcommand.BindFlags(cmd.Flags())
subcommand.UpdateContext(&ctx)
cmd.Long = ctx.Description
cmd.Example = ctx.Examples
cmd.RunE = runECmdFunc(c.fs, cfg, subcommand,
fmt.Sprintf("failed to edit project with %q", plugin.KeyFor(editPlugin)))

cfg := yamlstore.New(c.fs)
msg := fmt.Sprintf("failed to edit project with %q", plugin.KeyFor(editPlugin))
cmd.PreRunE = preRunECmdFunc(subcommand, cfg, msg)
cmd.RunE = runECmdFunc(c.fs, subcommand, msg)
cmd.PostRunE = postRunECmdFunc(cfg, msg)
}
36 changes: 18 additions & 18 deletions pkg/cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ limitations under the License.
package cli

import (
"errors"
"fmt"
"log"
"os"
"sort"
"strconv"
"strings"

"github.com/spf13/cobra"

internalconfig "sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
"sigs.k8s.io/kubebuilder/v3/pkg/config"
yamlstore "sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml"
cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
)
Expand Down Expand Up @@ -136,28 +136,28 @@ func (c cli) bindInit(ctx plugin.Context, cmd *cobra.Command) {
return
}

cfg, err := internalconfig.New(c.projectVersion, internalconfig.DefaultPath)
if err != nil {
cmdErr(cmd, fmt.Errorf("unable to initialize the project configuration: %w", err))
return
}

subcommand := initPlugin.GetInitSubcommand()
subcommand.InjectConfig(cfg.Config)
subcommand.BindFlags(cmd.Flags())
subcommand.UpdateContext(&ctx)
cmd.Long = ctx.Description
cmd.Example = ctx.Examples
cmd.RunE = func(*cobra.Command, []string) error {
// Check if a config is initialized in the command runner so the check
// doesn't erroneously fail other commands used in initialized projects.
_, err := internalconfig.Read()
if err == nil || os.IsExist(err) {
log.Fatal("config already initialized")

cfg := yamlstore.New(c.fs)
msg := fmt.Sprintf("failed to initialize project with %q", plugin.KeyFor(initPlugin))
cmd.PreRunE = func(*cobra.Command, []string) error {
// Check if a config is initialized.
if err := cfg.Load(); err == nil || !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("%s: already initialized", msg)
}
if err := subcommand.Run(c.fs); err != nil {
return fmt.Errorf("failed to initialize project with %q: %v", plugin.KeyFor(initPlugin), err)

err := cfg.New(c.projectVersion)
if err != nil {
return fmt.Errorf("%s: error initializing project configuration: %w", msg, err)
}
return cfg.Save()

subcommand.InjectConfig(cfg.Config())
return nil
}
cmd.RunE = runECmdFunc(c.fs, subcommand, msg)
cmd.PostRunE = postRunECmdFunc(cfg, msg)
}
Loading

0 comments on commit 8b7e731

Please sign in to comment.