Skip to content

Commit 1fbb697

Browse files
committed
Merge branch 'export-machinery' into plugin-resolution
2 parents 0675cd5 + 46e04f3 commit 1fbb697

File tree

181 files changed

+4715
-5169
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

181 files changed

+4715
-5169
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ test-unit: ## Run the unit tests
9797
.PHONY: test-coverage
9898
test-coverage: ## Run unit tests creating the output to report coverage
9999
- rm -rf *.out # Remove all coverage files if exists
100-
go test -race -failfast -tags=integration -coverprofile=coverage-all.out -coverpkg="./pkg/cli/...,./pkg/config/...,./pkg/internal/...,./pkg/model/...,./pkg/plugin/...,./pkg/plugins/golang,./pkg/plugins/internal/..." ./pkg/...
100+
go test -race -failfast -tags=integration -coverprofile=coverage-all.out -coverpkg="./pkg/cli/...,./pkg/config/...,./pkg/internal/...,./pkg/machinery/...,./pkg/model/...,./pkg/plugin/...,./pkg/plugins/golang" ./pkg/...
101101

102102
.PHONY: test-integration
103103
test-integration: ## Run the integration tests

cmd/main.go

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"sigs.k8s.io/kubebuilder/v3/pkg/cli"
2323
cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
2424
cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
25+
declarativev1 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/declarative/v1"
2526
pluginv2 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v2"
2627
pluginv3 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3"
2728
)
@@ -34,6 +35,7 @@ func main() {
3435
cli.WithPlugins(
3536
&pluginv2.Plugin{},
3637
&pluginv3.Plugin{},
38+
&declarativev1.Plugin{},
3739
),
3840
cli.WithDefaultPlugins(cfgv2.Version, &pluginv2.Plugin{}),
3941
cli.WithDefaultPlugins(cfgv3.Version, &pluginv3.Plugin{}),

pkg/cli/api.go

+31-53
Original file line numberDiff line numberDiff line change
@@ -14,80 +14,58 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package cli // nolint:dupl
17+
package cli //nolint:dupl
1818

1919
import (
2020
"fmt"
2121

2222
"github.com/spf13/cobra"
2323

24-
"sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
2524
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
2625
)
2726

27+
const apiErrorMsg = "failed to create API"
28+
2829
func (c CLI) newCreateAPICmd() *cobra.Command {
29-
ctx := c.newAPIContext()
3030
cmd := &cobra.Command{
31-
Use: "api",
32-
Short: "Scaffold a Kubernetes API",
33-
Long: ctx.Description,
34-
Example: ctx.Examples,
31+
Use: "api",
32+
Short: "Scaffold a Kubernetes API",
33+
Long: `Scaffold a Kubernetes API.
34+
`,
3535
RunE: errCmdFunc(
3636
fmt.Errorf("api subcommand requires an existing project"),
3737
),
3838
}
3939

40-
// Lookup the plugin for projectVersion and bind it to the command.
41-
c.bindCreateAPI(ctx, cmd)
42-
return cmd
43-
}
44-
45-
func (c CLI) newAPIContext() plugin.Context {
46-
return plugin.Context{
47-
CommandName: c.commandName,
48-
Description: `Scaffold a Kubernetes API.
49-
`,
50-
}
51-
}
52-
53-
// nolint:dupl
54-
func (c CLI) bindCreateAPI(ctx plugin.Context, cmd *cobra.Command) {
40+
// In case no plugin was resolved, instead of failing the construction of the CLI, fail the execution of
41+
// this subcommand. This allows the use of subcommands that do not require resolved plugins like help.
5542
if len(c.resolvedPlugins) == 0 {
56-
cmdErr(cmd, fmt.Errorf(noPluginError))
57-
return
43+
cmdErr(cmd, noResolvedPluginError{})
44+
return cmd
5845
}
5946

60-
var createAPIPlugin plugin.CreateAPI
61-
for _, p := range c.resolvedPlugins {
62-
tmpPlugin, isValid := p.(plugin.CreateAPI)
63-
if isValid {
64-
if createAPIPlugin != nil {
65-
err := fmt.Errorf("duplicate API creation plugins (%s, %s), use a more specific plugin key",
66-
plugin.KeyFor(createAPIPlugin), plugin.KeyFor(p))
67-
cmdErr(cmd, err)
68-
return
69-
}
70-
createAPIPlugin = tmpPlugin
71-
}
72-
}
47+
// Obtain the plugin keys and subcommands from the plugins that implement plugin.CreateAPI.
48+
pluginKeys, subcommands := c.filterSubcommands(
49+
func(p plugin.Plugin) bool {
50+
_, isValid := p.(plugin.CreateAPI)
51+
return isValid
52+
},
53+
func(p plugin.Plugin) plugin.Subcommand {
54+
return p.(plugin.CreateAPI).GetCreateAPISubcommand()
55+
},
56+
)
7357

74-
if createAPIPlugin == nil {
75-
cmdErr(cmd, fmt.Errorf("resolved plugins do not provide an API creation plugin: %v", c.pluginKeys))
76-
return
58+
// Verify that there is at least one remaining plugin.
59+
if len(*subcommands) == 0 {
60+
cmdErr(cmd, noAvailablePluginError{"API creation"})
61+
return cmd
7762
}
7863

79-
cfg, err := config.LoadInitialized()
80-
if err != nil {
81-
cmdErr(cmd, err)
82-
return
83-
}
64+
// Initialization methods.
65+
options := c.initializationMethods(cmd, subcommands)
66+
67+
// Execution methods.
68+
cmd.PreRunE, cmd.RunE, cmd.PostRunE = c.executionMethodsFuncs(pluginKeys, subcommands, options, apiErrorMsg)
8469

85-
subcommand := createAPIPlugin.GetCreateAPISubcommand()
86-
subcommand.InjectConfig(cfg.Config)
87-
subcommand.BindFlags(cmd.Flags())
88-
subcommand.UpdateContext(&ctx)
89-
cmd.Long = ctx.Description
90-
cmd.Example = ctx.Examples
91-
cmd.RunE = runECmdFunc(cfg, subcommand,
92-
fmt.Sprintf("failed to create API with %q", plugin.KeyFor(createAPIPlugin)))
70+
return cmd
9371
}

pkg/cli/cli.go

+20-8
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@ limitations under the License.
1717
package cli
1818

1919
import (
20+
"errors"
2021
"fmt"
2122
"os"
2223
"strings"
2324

25+
"github.com/spf13/afero"
2426
"github.com/spf13/cobra"
2527
"github.com/spf13/pflag"
2628

27-
internalconfig "sigs.k8s.io/kubebuilder/v3/pkg/cli/internal/config"
2829
"sigs.k8s.io/kubebuilder/v3/pkg/config"
30+
yamlstore "sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml"
2931
cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
3032
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
3133
)
@@ -36,8 +38,6 @@ const (
3638

3739
projectVersionFlag = "project-version"
3840
pluginsFlag = "plugins"
39-
40-
noPluginError = "invalid config file please verify that the version and layout fields are set and valid"
4141
)
4242

4343
// equalStringSlice checks if two string slices are equal.
@@ -88,6 +88,9 @@ type CLI struct { //nolint:maligned
8888

8989
// Root command.
9090
cmd *cobra.Command
91+
92+
// Underlying fs
93+
fs afero.Fs
9194
}
9295

9396
// New creates a new CLI instance.
@@ -131,6 +134,7 @@ func newCLI(options ...Option) (*CLI, error) {
131134
defaultProjectVersion: cfgv3.Version,
132135
defaultPlugins: make(map[config.Version][]string),
133136
plugins: make(map[string]plugin.Plugin),
137+
fs: afero.NewOsFs(),
134138
}
135139

136140
// Apply provided options.
@@ -188,18 +192,19 @@ func (c *CLI) getInfoFromFlags() (string, []string, error) {
188192
}
189193

190194
// getInfoFromConfigFile obtains the project version and plugin keys from the project config file.
191-
func getInfoFromConfigFile() (config.Version, []string, error) {
195+
func (c CLI) getInfoFromConfigFile() (config.Version, []string, error) {
192196
// Read the project configuration file
193-
projectConfig, err := internalconfig.Read()
197+
cfg := yamlstore.New(c.fs)
198+
err := cfg.Load()
194199
switch {
195200
case err == nil:
196-
case os.IsNotExist(err):
201+
case errors.Is(err, os.ErrNotExist):
197202
return config.Version{}, nil, nil
198203
default:
199204
return config.Version{}, nil, err
200205
}
201206

202-
return getInfoFromConfig(projectConfig)
207+
return getInfoFromConfig(cfg.Config())
203208
}
204209

205210
// getInfoFromConfig obtains the project version and plugin keys from the project config.
@@ -294,7 +299,7 @@ func (c *CLI) getInfo() error {
294299
return err
295300
}
296301
// Get project version and plugin info from project configuration file
297-
cfgProjectVersion, cfgPlugins, _ := getInfoFromConfigFile()
302+
cfgProjectVersion, cfgPlugins, _ := c.getInfoFromConfigFile()
298303
// We discard the error because not being able to read a project configuration file
299304
// is not fatal for some commands. The ones that require it need to check its existence.
300305

@@ -470,6 +475,13 @@ func (c CLI) printDeprecationWarnings() {
470475
}
471476
}
472477

478+
// metadata returns CLI's metadata.
479+
func (c CLI) metadata() plugin.CLIMetadata {
480+
return plugin.CLIMetadata{
481+
CommandName: c.commandName,
482+
}
483+
}
484+
473485
// Run executes the CLI utility.
474486
//
475487
// If an error is found, command help and examples will be printed.

pkg/cli/cli_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ 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"
@@ -585,6 +586,7 @@ var _ = Describe("CLI", func() {
585586
defaultPlugins: map[config.Version][]string{
586587
projectVersion: pluginKeys,
587588
},
589+
fs: afero.NewMemMapFs(),
588590
}
589591
c.cmd = c.newRootCmd()
590592
Expect(c.getInfo()).To(Succeed())

0 commit comments

Comments
 (0)