Skip to content

Commit 9f9daf8

Browse files
Add config passing to external plugins
Co-authored-by: camilamacedo86 <[email protected]>
1 parent b489cec commit 9f9daf8

File tree

7 files changed

+208
-5
lines changed

7 files changed

+208
-5
lines changed

pkg/plugin/external/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ type PluginRequest struct {
3535
// Universe represents the modified file contents that gets updated over a series of plugin runs
3636
// across the plugin chain. Initially, it starts out as empty.
3737
Universe map[string]string `json:"universe"`
38+
39+
// Config contains the PROJECT file config. This field may be empty if the
40+
// project is being initialized and the PROJECT file has not been created yet.
41+
Config map[string]interface{} `json:"config,omitempty"`
3842
}
3943

4044
// PluginResponse is returned to kubebuilder by the plugin and contains all files

pkg/plugins/external/api.go

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

22+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
2223
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
2324
"sigs.k8s.io/kubebuilder/v4/pkg/model/resource"
2425
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
@@ -32,6 +33,8 @@ const (
3233
)
3334

3435
type createAPISubcommand struct {
36+
config config.Config
37+
3538
Path string
3639
Args []string
3740
}
@@ -49,14 +52,19 @@ func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) {
4952
bindExternalPluginFlags(fs, "api", p.Path, p.Args)
5053
}
5154

55+
func (p *createAPISubcommand) InjectConfig(c config.Config) error {
56+
p.config = c
57+
return nil
58+
}
59+
5260
func (p *createAPISubcommand) Scaffold(fs machinery.Filesystem) error {
5361
req := external.PluginRequest{
5462
APIVersion: defaultAPIVersion,
5563
Command: "create api",
5664
Args: p.Args,
5765
}
5866

59-
err := handlePluginResponse(fs, req, p.Path)
67+
err := handlePluginResponse(fs, req, p.Path, p.config)
6068
if err != nil {
6169
return err
6270
}

pkg/plugins/external/edit.go

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

22+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
2223
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
2324
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
2425
"sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
@@ -27,6 +28,8 @@ import (
2728
var _ plugin.EditSubcommand = &editSubcommand{}
2829

2930
type editSubcommand struct {
31+
config config.Config
32+
3033
Path string
3134
Args []string
3235
}
@@ -39,14 +42,19 @@ func (p *editSubcommand) BindFlags(fs *pflag.FlagSet) {
3942
bindExternalPluginFlags(fs, "edit", p.Path, p.Args)
4043
}
4144

45+
func (p *editSubcommand) InjectConfig(c config.Config) error {
46+
p.config = c
47+
return nil
48+
}
49+
4250
func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
4351
req := external.PluginRequest{
4452
APIVersion: defaultAPIVersion,
4553
Command: "edit",
4654
Args: p.Args,
4755
}
4856

49-
err := handlePluginResponse(fs, req, p.Path)
57+
err := handlePluginResponse(fs, req, p.Path, p.config)
5058
if err != nil {
5159
return err
5260
}

pkg/plugins/external/external_test.go

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

31+
"sigs.k8s.io/kubebuilder/v4/pkg/config/v3"
3132
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
3233
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
3334
"sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
@@ -75,6 +76,28 @@ func (m *mockInValidOsWdGetter) GetCurrentDir() (string, error) {
7576
return "", fmt.Errorf("error getting current directory")
7677
}
7778

79+
// mockConfigOutputGetter captures the request to verify config is passed
80+
type mockConfigOutputGetter struct {
81+
capturedRequest *external.PluginRequest
82+
}
83+
84+
var _ ExecOutputGetter = &mockConfigOutputGetter{}
85+
86+
func (m *mockConfigOutputGetter) GetExecOutput(reqBytes []byte, _ string) ([]byte, error) {
87+
// Capture the request for verification
88+
m.capturedRequest = &external.PluginRequest{}
89+
if err := json.Unmarshal(reqBytes, m.capturedRequest); err != nil {
90+
return nil, fmt.Errorf("error unmarshalling request: %w", err)
91+
}
92+
93+
return []byte(`{
94+
"command": "init",
95+
"error": false,
96+
"error_msg": "none",
97+
"universe": {"LICENSE": "Apache 2.0 License\n"}
98+
}`), nil
99+
}
100+
78101
type mockValidFlagOutputGetter struct{}
79102

80103
func (m *mockValidFlagOutputGetter) GetExecOutput(_ []byte, _ string) ([]byte, error) {
@@ -754,6 +777,133 @@ var _ = Describe("Run external plugin using Scaffold", func() {
754777
}
755778
})
756779
})
780+
781+
Context("with config injection", func() {
782+
const filePerm os.FileMode = 755
783+
var (
784+
pluginFileName string
785+
args []string
786+
f afero.File
787+
fs machinery.Filesystem
788+
mockGetter *mockConfigOutputGetter
789+
cfg *v3.Cfg
790+
791+
err error
792+
)
793+
794+
BeforeEach(func() {
795+
mockGetter = &mockConfigOutputGetter{}
796+
outputGetter = mockGetter
797+
currentDirGetter = &mockValidOsWdGetter{}
798+
fs = machinery.Filesystem{
799+
FS: afero.NewMemMapFs(),
800+
}
801+
802+
pluginFileName = "externalPlugin.sh"
803+
pluginFilePath := filepath.Join("tmp", "externalPlugin", pluginFileName)
804+
805+
err = fs.FS.MkdirAll(filepath.Dir(pluginFilePath), filePerm)
806+
Expect(err).ToNot(HaveOccurred())
807+
808+
f, err = fs.FS.Create(pluginFilePath)
809+
Expect(err).ToNot(HaveOccurred())
810+
Expect(f).ToNot(BeNil())
811+
812+
_, err = fs.FS.Stat(pluginFilePath)
813+
Expect(err).ToNot(HaveOccurred())
814+
815+
args = []string{"--domain", "example.com"}
816+
817+
// Create a config instance
818+
cfg = &v3.Cfg{
819+
Version: v3.Version,
820+
Domain: "test.domain",
821+
Repository: "github.com/test/repo",
822+
Name: "test-project",
823+
}
824+
})
825+
826+
It("should pass config to external plugin on init subcommand", func() {
827+
i := initSubcommand{
828+
Path: pluginFileName,
829+
Args: args,
830+
config: cfg,
831+
}
832+
833+
err = i.Scaffold(fs)
834+
Expect(err).ToNot(HaveOccurred())
835+
836+
// Verify that config was captured in the request
837+
Expect(mockGetter.capturedRequest).ToNot(BeNil())
838+
Expect(mockGetter.capturedRequest.Config).ToNot(BeNil())
839+
Expect(mockGetter.capturedRequest.Config["domain"]).To(Equal("test.domain"))
840+
Expect(mockGetter.capturedRequest.Config["repo"]).To(Equal("github.com/test/repo"))
841+
Expect(mockGetter.capturedRequest.Config["projectName"]).To(Equal("test-project"))
842+
})
843+
844+
It("should pass config to external plugin on create api subcommand", func() {
845+
c := createAPISubcommand{
846+
Path: pluginFileName,
847+
Args: args,
848+
config: cfg,
849+
}
850+
851+
err = c.Scaffold(fs)
852+
Expect(err).ToNot(HaveOccurred())
853+
854+
// Verify that config was captured in the request
855+
Expect(mockGetter.capturedRequest).ToNot(BeNil())
856+
Expect(mockGetter.capturedRequest.Config).ToNot(BeNil())
857+
Expect(mockGetter.capturedRequest.Config["domain"]).To(Equal("test.domain"))
858+
})
859+
860+
It("should pass config to external plugin on create webhook subcommand", func() {
861+
c := createWebhookSubcommand{
862+
Path: pluginFileName,
863+
Args: args,
864+
config: cfg,
865+
}
866+
867+
err = c.Scaffold(fs)
868+
Expect(err).ToNot(HaveOccurred())
869+
870+
// Verify that config was captured in the request
871+
Expect(mockGetter.capturedRequest).ToNot(BeNil())
872+
Expect(mockGetter.capturedRequest.Config).ToNot(BeNil())
873+
Expect(mockGetter.capturedRequest.Config["domain"]).To(Equal("test.domain"))
874+
})
875+
876+
It("should pass config to external plugin on edit subcommand", func() {
877+
e := editSubcommand{
878+
Path: pluginFileName,
879+
Args: args,
880+
config: cfg,
881+
}
882+
883+
err = e.Scaffold(fs)
884+
Expect(err).ToNot(HaveOccurred())
885+
886+
// Verify that config was captured in the request
887+
Expect(mockGetter.capturedRequest).ToNot(BeNil())
888+
Expect(mockGetter.capturedRequest.Config).ToNot(BeNil())
889+
Expect(mockGetter.capturedRequest.Config["domain"]).To(Equal("test.domain"))
890+
})
891+
892+
It("should handle nil config gracefully", func() {
893+
i := initSubcommand{
894+
Path: pluginFileName,
895+
Args: args,
896+
config: nil,
897+
}
898+
899+
err = i.Scaffold(fs)
900+
Expect(err).ToNot(HaveOccurred())
901+
902+
// Verify that request was made but config is nil
903+
Expect(mockGetter.capturedRequest).ToNot(BeNil())
904+
Expect(mockGetter.capturedRequest.Config).To(BeNil())
905+
})
906+
})
757907
})
758908

759909
func getFlags() []external.Flag {

pkg/plugins/external/helpers.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ import (
3030

3131
"github.com/spf13/afero"
3232
"github.com/spf13/pflag"
33+
"sigs.k8s.io/yaml"
3334

35+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
3436
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
3537
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
3638
"sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
@@ -149,14 +151,29 @@ func getUniverseMap(fs machinery.Filesystem) (map[string]string, error) {
149151
return universe, nil
150152
}
151153

152-
func handlePluginResponse(fs machinery.Filesystem, req external.PluginRequest, path string) error {
154+
func handlePluginResponse(fs machinery.Filesystem, req external.PluginRequest, path string, cfg config.Config) error {
153155
var err error
154156

155157
req.Universe, err = getUniverseMap(fs)
156158
if err != nil {
157159
return fmt.Errorf("error getting universe map: %w", err)
158160
}
159161

162+
// Marshal config to include in the request if config is provided
163+
if cfg != nil {
164+
configData, err := cfg.MarshalYAML()
165+
if err != nil {
166+
return fmt.Errorf("error marshaling config: %w", err)
167+
}
168+
169+
var configMap map[string]interface{}
170+
if err = yaml.Unmarshal(configData, &configMap); err != nil {
171+
return fmt.Errorf("error unmarshaling config to map: %w", err)
172+
}
173+
174+
req.Config = configMap
175+
}
176+
160177
res, err := makePluginRequest(req, path)
161178
if err != nil {
162179
return fmt.Errorf("error making request to external plugin: %w", err)

pkg/plugins/external/init.go

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

22+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
2223
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
2324
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
2425
"sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
@@ -27,6 +28,8 @@ import (
2728
var _ plugin.InitSubcommand = &initSubcommand{}
2829

2930
type initSubcommand struct {
31+
config config.Config
32+
3033
Path string
3134
Args []string
3235
}
@@ -39,14 +42,19 @@ func (p *initSubcommand) BindFlags(fs *pflag.FlagSet) {
3942
bindExternalPluginFlags(fs, "init", p.Path, p.Args)
4043
}
4144

45+
func (p *initSubcommand) InjectConfig(c config.Config) error {
46+
p.config = c
47+
return nil
48+
}
49+
4250
func (p *initSubcommand) Scaffold(fs machinery.Filesystem) error {
4351
req := external.PluginRequest{
4452
APIVersion: defaultAPIVersion,
4553
Command: "init",
4654
Args: p.Args,
4755
}
4856

49-
err := handlePluginResponse(fs, req, p.Path)
57+
err := handlePluginResponse(fs, req, p.Path, p.config)
5058
if err != nil {
5159
return err
5260
}

pkg/plugins/external/webhook.go

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

22+
"sigs.k8s.io/kubebuilder/v4/pkg/config"
2223
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
2324
"sigs.k8s.io/kubebuilder/v4/pkg/model/resource"
2425
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
@@ -28,6 +29,8 @@ import (
2829
var _ plugin.CreateWebhookSubcommand = &createWebhookSubcommand{}
2930

3031
type createWebhookSubcommand struct {
32+
config config.Config
33+
3134
Path string
3235
Args []string
3336
}
@@ -45,14 +48,19 @@ func (p *createWebhookSubcommand) BindFlags(fs *pflag.FlagSet) {
4548
bindExternalPluginFlags(fs, "webhook", p.Path, p.Args)
4649
}
4750

51+
func (p *createWebhookSubcommand) InjectConfig(c config.Config) error {
52+
p.config = c
53+
return nil
54+
}
55+
4856
func (p *createWebhookSubcommand) Scaffold(fs machinery.Filesystem) error {
4957
req := external.PluginRequest{
5058
APIVersion: defaultAPIVersion,
5159
Command: "create webhook",
5260
Args: p.Args,
5361
}
5462

55-
err := handlePluginResponse(fs, req, p.Path)
63+
err := handlePluginResponse(fs, req, p.Path, p.config)
5664
if err != nil {
5765
return err
5866
}

0 commit comments

Comments
 (0)