Skip to content

Commit fc97428

Browse files
committed
phase 2 plugins metadata and examples for EP
Signed-off-by: Bryce Palmer <[email protected]>
1 parent 78ad658 commit fc97428

File tree

7 files changed

+236
-24
lines changed

7 files changed

+236
-24
lines changed

pkg/plugin/external/types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ limitations under the License.
1616

1717
package external
1818

19+
import "sigs.k8s.io/kubebuilder/v3/pkg/plugin"
20+
1921
// PluginRequest contains all information kubebuilder received from the CLI
2022
// and plugins executed before it.
2123
type PluginRequest struct {
@@ -47,7 +49,7 @@ type PluginResponse struct {
4749

4850
// Help contains the plugin specific help text that the plugin returns to Kubebuilder when it receives
4951
// `--help` flag from Kubebuilder.
50-
Help string `json:"help"`
52+
Metadata plugin.SubcommandMetadata `json:"metadata"`
5153

5254
// Universe in the PluginResponse represents the updated file contents that was written by the plugin.
5355
Universe map[string]string `json:"universe"`

pkg/plugins/external/api.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ func (p *createAPISubcommand) InjectResource(*resource.Resource) error {
4141
return nil
4242
}
4343

44+
func (p *createAPISubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
45+
setExternalPluginMetadata("api", p.Path, subcmdMeta)
46+
}
47+
4448
func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) {
4549
bindExternalPluginFlags(fs, "api", p.Path, p.Args)
4650
}

pkg/plugins/external/edit.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ type editSubcommand struct {
3131
Args []string
3232
}
3333

34+
func (p *editSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
35+
setExternalPluginMetadata("edit", p.Path, subcmdMeta)
36+
}
37+
3438
func (p *editSubcommand) BindFlags(fs *pflag.FlagSet) {
3539
bindExternalPluginFlags(fs, "edit", p.Path, p.Args)
3640
}

pkg/plugins/external/external_test.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/spf13/pflag"
3030

3131
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
32+
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
3233
"sigs.k8s.io/kubebuilder/v3/pkg/plugin/external"
3334
)
3435

@@ -86,6 +87,19 @@ func (m *mockValidFlagOutputGetter) GetExecOutput(req []byte, path string) ([]by
8687
return json.Marshal(response)
8788
}
8889

90+
type mockValidMEOutputGetter struct{}
91+
92+
func (m *mockValidMEOutputGetter) GetExecOutput(req []byte, path string) ([]byte, error) {
93+
response := external.PluginResponse{
94+
Command: "metadata",
95+
Error: false,
96+
Universe: nil,
97+
Metadata: getMetadata(),
98+
}
99+
100+
return json.Marshal(response)
101+
}
102+
89103
const externalPlugin = "myexternalplugin.sh"
90104
const floatVal = "float"
91105

@@ -486,6 +500,136 @@ var _ = Describe("Run external plugin using Scaffold", func() {
486500
}
487501
})
488502
})
503+
504+
// TODO(everettraven): Add tests for an external plugin setting the Metadata and Examples
505+
Context("Successfully retrieving metadata and examples from external plugin", func() {
506+
var (
507+
pluginFileName string
508+
metadata *plugin.SubcommandMetadata
509+
checkMetadata func()
510+
)
511+
BeforeEach(func() {
512+
outputGetter = &mockValidMEOutputGetter{}
513+
currentDirGetter = &mockValidOsWdGetter{}
514+
515+
pluginFileName = externalPlugin
516+
metadata = &plugin.SubcommandMetadata{}
517+
518+
checkMetadata = func() {
519+
Expect(metadata.Description).Should(Equal(getMetadata().Description))
520+
Expect(metadata.Examples).Should(Equal(getMetadata().Examples))
521+
}
522+
})
523+
524+
It("should use the external plugin's metadata and examples for `init` subcommand", func() {
525+
sc := initSubcommand{
526+
Path: pluginFileName,
527+
Args: nil,
528+
}
529+
530+
sc.UpdateMetadata(plugin.CLIMetadata{}, metadata)
531+
532+
checkMetadata()
533+
})
534+
535+
It("should use the external plugin's metadata and examples for `create api` subcommand", func() {
536+
sc := createAPISubcommand{
537+
Path: pluginFileName,
538+
Args: nil,
539+
}
540+
541+
sc.UpdateMetadata(plugin.CLIMetadata{}, metadata)
542+
543+
checkMetadata()
544+
})
545+
546+
It("should use the external plugin's metadata and examples for `create webhook` subcommand", func() {
547+
sc := createWebhookSubcommand{
548+
Path: pluginFileName,
549+
Args: nil,
550+
}
551+
552+
sc.UpdateMetadata(plugin.CLIMetadata{}, metadata)
553+
554+
checkMetadata()
555+
})
556+
557+
It("should use the external plugin's metadata and examples for `edit` subcommand", func() {
558+
sc := editSubcommand{
559+
Path: pluginFileName,
560+
Args: nil,
561+
}
562+
563+
sc.UpdateMetadata(plugin.CLIMetadata{}, metadata)
564+
565+
checkMetadata()
566+
})
567+
})
568+
569+
Context("Failing to retrieve metadata and examples from external plugin", func() {
570+
var (
571+
pluginFileName string
572+
metadata *plugin.SubcommandMetadata
573+
checkMetadata func()
574+
)
575+
BeforeEach(func() {
576+
outputGetter = &mockInValidOutputGetter{}
577+
currentDirGetter = &mockValidOsWdGetter{}
578+
579+
pluginFileName = externalPlugin
580+
metadata = &plugin.SubcommandMetadata{}
581+
582+
checkMetadata = func() {
583+
Expect(metadata.Description).Should(Equal(fmt.Sprintf(defaultMetadataTemplate, "myexternalplugin")))
584+
Expect(metadata.Examples).Should(BeEmpty())
585+
}
586+
})
587+
588+
It("should use the default metadata and examples for `init` subcommand", func() {
589+
sc := initSubcommand{
590+
Path: pluginFileName,
591+
Args: nil,
592+
}
593+
594+
sc.UpdateMetadata(plugin.CLIMetadata{}, metadata)
595+
596+
checkMetadata()
597+
})
598+
599+
It("should use the default metadata and examples for `create api` subcommand", func() {
600+
sc := createAPISubcommand{
601+
Path: pluginFileName,
602+
Args: nil,
603+
}
604+
605+
sc.UpdateMetadata(plugin.CLIMetadata{}, metadata)
606+
607+
checkMetadata()
608+
})
609+
610+
It("should use the default metadata and examples for `create webhook` subcommand", func() {
611+
sc := createWebhookSubcommand{
612+
Path: pluginFileName,
613+
Args: nil,
614+
}
615+
616+
sc.UpdateMetadata(plugin.CLIMetadata{}, metadata)
617+
618+
checkMetadata()
619+
})
620+
621+
It("should use the default metadata and examples for `edit` subcommand", func() {
622+
sc := editSubcommand{
623+
Path: pluginFileName,
624+
Args: nil,
625+
}
626+
627+
sc.UpdateMetadata(plugin.CLIMetadata{}, metadata)
628+
629+
checkMetadata()
630+
})
631+
})
632+
489633
})
490634

491635
func getFlags() []external.Flag {
@@ -516,3 +660,10 @@ func getFlags() []external.Flag {
516660
},
517661
}
518662
}
663+
664+
func getMetadata() plugin.SubcommandMetadata {
665+
return plugin.SubcommandMetadata{
666+
Description: "Test description",
667+
Examples: "Test examples",
668+
}
669+
}

pkg/plugins/external/helpers.go

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,19 @@ import (
2828

2929
"github.com/spf13/pflag"
3030
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
31+
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
3132
"sigs.k8s.io/kubebuilder/v3/pkg/plugin/external"
3233
)
3334

3435
var outputGetter ExecOutputGetter = &execOutputGetter{}
3536

37+
const defaultMetadataTemplate = `
38+
%s is an external plugin for scaffolding files to help with your Operator development.
39+
40+
For more information on how to use this external plugin, it is recommended to
41+
consult the external plugin's documentation.
42+
`
43+
3644
// ExecOutputGetter is an interface that implements the exec output method.
3745
type ExecOutputGetter interface {
3846
GetExecOutput(req []byte, path string) ([]byte, error)
@@ -70,27 +78,36 @@ func (o *osWdGetter) GetCurrentDir() (string, error) {
7078
return currentDir, nil
7179
}
7280

73-
func handlePluginResponse(fs machinery.Filesystem, req external.PluginRequest, path string) error {
74-
req.Universe = map[string]string{}
75-
81+
func makePluginRequest(req external.PluginRequest, path string) (*external.PluginResponse, error) {
7682
reqBytes, err := json.Marshal(req)
7783
if err != nil {
78-
return err
84+
return nil, err
7985
}
8086

8187
out, err := outputGetter.GetExecOutput(reqBytes, path)
8288
if err != nil {
83-
return err
89+
return nil, err
8490
}
8591

8692
res := external.PluginResponse{}
8793
if err := json.Unmarshal(out, &res); err != nil {
88-
return err
94+
return nil, err
8995
}
9096

9197
// Error if the plugin failed.
9298
if res.Error {
93-
return fmt.Errorf(strings.Join(res.ErrorMsgs, "\n"))
99+
return nil, fmt.Errorf(strings.Join(res.ErrorMsgs, "\n"))
100+
}
101+
102+
return &res, nil
103+
}
104+
105+
func handlePluginResponse(fs machinery.Filesystem, req external.PluginRequest, path string) error {
106+
req.Universe = map[string]string{}
107+
108+
res, err := makePluginRequest(req, path)
109+
if err != nil {
110+
return fmt.Errorf("error making request to external plugin: %w", err)
94111
}
95112

96113
currentDir, err := currentDirGetter.GetCurrentDir()
@@ -123,23 +140,9 @@ func handlePluginResponse(fs machinery.Filesystem, req external.PluginRequest, p
123140
func getExternalPluginFlags(req external.PluginRequest, path string) ([]external.Flag, error) {
124141
req.Universe = map[string]string{}
125142

126-
reqBytes, err := json.Marshal(req)
143+
res, err := makePluginRequest(req, path)
127144
if err != nil {
128-
return nil, err
129-
}
130-
131-
out, err := outputGetter.GetExecOutput(reqBytes, path)
132-
if err != nil {
133-
return nil, err
134-
}
135-
136-
res := external.PluginResponse{}
137-
if err := json.Unmarshal(out, &res); err != nil {
138-
return nil, err
139-
}
140-
141-
if res.Error {
142-
return nil, fmt.Errorf(strings.Join(res.ErrorMsgs, "\n"))
145+
return nil, fmt.Errorf("error making request to external plugin: %w", err)
143146
}
144147

145148
return res.Flags, nil
@@ -211,3 +214,43 @@ func bindExternalPluginFlags(fs *pflag.FlagSet, subcommand string, path string,
211214
bindSpecificFlags(fs, flags)
212215
}
213216
}
217+
218+
// setExternalPluginMetadata is a helper function that sets the subcommand
219+
// metadata that is used when the help text is shown for a subcommand.
220+
// It will attempt to get the Metadata from the external plugin. If the
221+
// external plugin returns no Metadata or an error, a default will be used.
222+
func setExternalPluginMetadata(subcommand, path string, subcmdMeta *plugin.SubcommandMetadata) {
223+
fileName := filepath.Base(path)
224+
subcmdMeta.Description = fmt.Sprintf(defaultMetadataTemplate, fileName[:len(fileName)-len(filepath.Ext(fileName))])
225+
226+
res, _ := getExternalPluginMetadata(subcommand, path)
227+
228+
if res != nil {
229+
if res.Description != "" {
230+
subcmdMeta.Description = res.Description
231+
}
232+
233+
if res.Examples != "" {
234+
subcmdMeta.Examples = res.Examples
235+
}
236+
}
237+
}
238+
239+
// fetchExternalPluginMetadata performs the actual request to the
240+
// external plugin to get the metadata. It returns the metadata
241+
// or an error if an error occurs during the fetch process.
242+
func getExternalPluginMetadata(subcommand, path string) (*plugin.SubcommandMetadata, error) {
243+
req := external.PluginRequest{
244+
APIVersion: defaultAPIVersion,
245+
Command: "metadata",
246+
Args: []string{"--" + subcommand},
247+
Universe: map[string]string{},
248+
}
249+
250+
res, err := makePluginRequest(req, path)
251+
if err != nil {
252+
return nil, fmt.Errorf("error making request to external plugin: %w", err)
253+
}
254+
255+
return &res.Metadata, nil
256+
}

pkg/plugins/external/init.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ type initSubcommand struct {
3131
Args []string
3232
}
3333

34+
func (p *initSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
35+
setExternalPluginMetadata("init", p.Path, subcmdMeta)
36+
}
37+
3438
func (p *initSubcommand) BindFlags(fs *pflag.FlagSet) {
3539
bindExternalPluginFlags(fs, "init", p.Path, p.Args)
3640
}

pkg/plugins/external/webhook.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ func (p *createWebhookSubcommand) InjectResource(*resource.Resource) error {
3737
return nil
3838
}
3939

40+
func (p *createWebhookSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
41+
setExternalPluginMetadata("webhook", p.Path, subcmdMeta)
42+
}
43+
4044
func (p *createWebhookSubcommand) BindFlags(fs *pflag.FlagSet) {
4145
bindExternalPluginFlags(fs, "webhook", p.Path, p.Args)
4246
}

0 commit comments

Comments
 (0)