Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ pass config between KB and external plugin #3526

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ import (
"io"
"os"

"v1/scaffolds"

"sigs.k8s.io/kubebuilder/v3/pkg/plugin/external"
"v1/scaffolds"
)

// Run will run the actual steps of the plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21
require (
github.com/spf13/pflag v1.0.5
sigs.k8s.io/kubebuilder/v3 v3.14.0
sigs.k8s.io/yaml v1.4.0
)

require (
Expand All @@ -14,3 +15,5 @@ require (
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
)

replace sigs.k8s.io/kubebuilder/v3 => ../../../../../../../
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb h1:LCMfzVg3sflxTs4UvuP4D8CkoZnfHLe2qzqgDn/4OHs=
Expand Down Expand Up @@ -38,11 +39,10 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/kubebuilder/v3 v3.14.0 h1:DVrHb6ADfGQKk/4NiMFOO7XIJK58maXhYIwlHSFy84I=
sigs.k8s.io/kubebuilder/v3 v3.14.0/go.mod h1:vh/9c7elEE2h0wB+Gxy1t63f8WuPrLO1ExoQR6ms8L0=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ limitations under the License.
package scaffolds

import (
"v1/scaffolds/internal/templates"

"github.com/spf13/pflag"

"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin/external"
"v1/scaffolds/internal/templates"
)

var InitFlags = []external.Flag{
Expand Down Expand Up @@ -58,6 +57,31 @@ func InitCmd(pr *external.PluginRequest) external.PluginResponse {
flags.Parse(pr.Args)
domain, _ := flags.GetString("domain")

// Update the project config
cfg := pr.Config

pluginChain := cfg.GetPluginChain()
pluginChain = append(pluginChain, pflag.Arg(0))
cfg.SetPluginChain(pluginChain)

if cfg.GetProjectName() == "" {
cfg.SetProjectName("externalplugin")
}

rsc := resource.Resource{
GVK: resource.GVK{
Group: "group",
Version: "v1",
Kind: "Externalpluginsample",
Domain: "my.domain",
},
}

cfg.UpdateResource(rsc)

// Update the PluginResponse with the modified updated config
pluginResponse.Config = cfg

initFile := templates.NewInitFile(templates.WithDomain(domain))

// Phase 2 Plugins uses the concept of a "universe" to represent the filesystem for a plugin.
Expand Down
11 changes: 10 additions & 1 deletion pkg/plugin/external/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ limitations under the License.

package external

import "sigs.k8s.io/kubebuilder/v3/pkg/plugin"
import (
"sigs.k8s.io/kubebuilder/v3/pkg/config"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
)

// PluginRequest contains all information kubebuilder received from the CLI
// and plugins executed before it.
Expand All @@ -35,6 +38,9 @@ type PluginRequest struct {
// Universe represents the modified file contents that gets updated over a series of plugin runs
// across the plugin chain. Initially, it starts out as empty.
Universe map[string]string `json:"universe"`

// Config stores the project configuration file.
Config config.Config `json:"config"`
}

// PluginResponse is returned to kubebuilder by the plugin and contains all files
Expand Down Expand Up @@ -63,6 +69,9 @@ type PluginResponse struct {
// Flags contains the plugin specific flags that the plugin returns to Kubebuilder when it receives
// a request for a list of supported flags from Kubebuilder
Flags []Flag `json:"flags,omitempty"`

// Config stores the project configuration file.
Config config.Config `json:"config"`
}

// Flag is meant to represent a CLI flag that is used by Kubebuilder to define flags that are parsed
Expand Down
25 changes: 22 additions & 3 deletions pkg/plugins/external/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ limitations under the License.
package external

import (
"github.com/spf13/afero"
"github.com/spf13/pflag"

"sigs.k8s.io/kubebuilder/v3/pkg/config"
"sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml"
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
Expand All @@ -32,8 +35,9 @@ const (
)

type createAPISubcommand struct {
Path string
Args []string
Path string
Args []string
config config.Config
}

func (p *createAPISubcommand) InjectResource(*resource.Resource) error {
Expand All @@ -50,16 +54,31 @@ func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) {
}

func (p *createAPISubcommand) Scaffold(fs machinery.Filesystem) error {
cfg := yaml.New(machinery.Filesystem{FS: afero.NewOsFs()})
if err := cfg.Load(); err != nil {
return err
}

req := external.PluginRequest{
APIVersion: defaultAPIVersion,
Command: "create api",
Args: p.Args,
Config: cfg.Config(),
}

err := handlePluginResponse(fs, req, p.Path)
err := handlePluginResponse(fs, req, p.Path, p)
if err != nil {
return err
}

return nil
}

func (p *createAPISubcommand) InjectConfig(c config.Config) error {
p.config = c
return nil
}

func (p *createAPISubcommand) GetConfig() config.Config {
return p.config
}
17 changes: 14 additions & 3 deletions pkg/plugins/external/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package external
import (
"github.com/spf13/pflag"

"sigs.k8s.io/kubebuilder/v3/pkg/config"
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin/external"
Expand All @@ -27,8 +28,9 @@ import (
var _ plugin.EditSubcommand = &editSubcommand{}

type editSubcommand struct {
Path string
Args []string
Path string
Args []string
config config.Config
}

func (p *editSubcommand) UpdateMetadata(_ plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
Expand All @@ -46,10 +48,19 @@ func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
Args: p.Args,
}

err := handlePluginResponse(fs, req, p.Path)
err := handlePluginResponse(fs, req, p.Path, p)
if err != nil {
return err
}

return nil
}

func (p *editSubcommand) InjectConfig(c config.Config) error {
p.config = c
return nil
}

func (p *editSubcommand) GetConfig() config.Config {
return p.config
}
15 changes: 14 additions & 1 deletion pkg/plugins/external/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/spf13/afero"
"github.com/spf13/pflag"
"sigs.k8s.io/kubebuilder/v3/pkg/config"
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin/external"
Expand All @@ -51,6 +52,12 @@ type ExecOutputGetter interface {

type execOutputGetter struct{}

// PluginConfigHandler is an interface to update the config modified by external plugin.
type PluginConfigHandler interface {
GetConfig() config.Config
InjectConfig(config.Config) error
}

func (e *execOutputGetter) GetExecOutput(request []byte, path string) ([]byte, error) {
cmd := exec.Command(path) //nolint:gosec
cmd.Stdin = bytes.NewBuffer(request)
Expand Down Expand Up @@ -149,7 +156,8 @@ func getUniverseMap(fs machinery.Filesystem) (map[string]string, error) {
return universe, nil
}

func handlePluginResponse(fs machinery.Filesystem, req external.PluginRequest, path string) error {
// nolint:lll
func handlePluginResponse(fs machinery.Filesystem, req external.PluginRequest, path string, p PluginConfigHandler) error {
var err error

req.Universe, err = getUniverseMap(fs)
Expand All @@ -167,6 +175,11 @@ func handlePluginResponse(fs machinery.Filesystem, req external.PluginRequest, p
return fmt.Errorf("error getting current directory: %v", err)
}

// update the config
if err := p.InjectConfig(res.Config); err != nil {
return fmt.Errorf("error injecting the updated config from PluginResponse: %w", err)
}

for filename, data := range res.Universe {
path := filepath.Join(currentDir, filename)
dir := filepath.Dir(path)
Expand Down
47 changes: 44 additions & 3 deletions pkg/plugins/external/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ limitations under the License.
package external

import (
"os"

"github.com/spf13/afero"
"github.com/spf13/pflag"
Eileen-Yu marked this conversation as resolved.
Show resolved Hide resolved

"sigs.k8s.io/kubebuilder/v3/pkg/config"
"sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml"
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin/external"
Expand All @@ -27,8 +32,9 @@ import (
var _ plugin.InitSubcommand = &initSubcommand{}

type initSubcommand struct {
Path string
Args []string
Path string
Args []string
config config.Config
}

func (p *initSubcommand) UpdateMetadata(_ plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
Expand All @@ -40,16 +46,51 @@ func (p *initSubcommand) BindFlags(fs *pflag.FlagSet) {
}

func (p *initSubcommand) Scaffold(fs machinery.Filesystem) error {
fileName := "PROJECT"

if _, err := os.Stat(fileName); os.IsNotExist(err) {
Copy link
Member

@camilamacedo86 camilamacedo86 Feb 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not create the file
We should fail in this case.
The CLI is who has the responsibility to create it.
the external plugin just read and update.

file, err := os.Create(fileName)
if err != nil {
return err
}
defer func() {
if cerr := file.Close(); cerr != nil {
if err == nil {
err = cerr
}
}
}()
}

cfg := yaml.New(machinery.Filesystem{FS: afero.NewOsFs()})
if err := cfg.New(config.Version{Number: 3}); err != nil {
return err
}

if err := cfg.Load(); err != nil {
return err
}

req := external.PluginRequest{
APIVersion: defaultAPIVersion,
Command: "init",
Args: p.Args,
Config: cfg.Config(),
}

err := handlePluginResponse(fs, req, p.Path)
err := handlePluginResponse(fs, req, p.Path, p)
if err != nil {
return err
}

return nil
}

func (p *initSubcommand) InjectConfig(c config.Config) error {
p.config = c
return nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, to ensure that works could we add some tests to this one?

In the e2e test that we have now, could we not validate the config is filled and with the data that is expected?
Also, could we try to pass a value unto the config and see if that will be persisted in the PROJECT file?

See here how we update the config with the data to have it in tracked in the PROJECT file: https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/plugins/golang/deploy-image/v1alpha1/api.go#L221-L247

Note that this code is reponsable for we track: https://github.com/kubernetes-sigs/kubebuilder/blob/master/testdata/project-v4-with-deploy-image/PROJECT#L8-L25


func (p *initSubcommand) GetConfig() config.Config {
return p.config
}
17 changes: 14 additions & 3 deletions pkg/plugins/external/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package external
import (
"github.com/spf13/pflag"

"sigs.k8s.io/kubebuilder/v3/pkg/config"
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
Expand All @@ -28,8 +29,9 @@ import (
var _ plugin.CreateWebhookSubcommand = &createWebhookSubcommand{}

type createWebhookSubcommand struct {
Path string
Args []string
Path string
Args []string
config config.Config
}

func (p *createWebhookSubcommand) InjectResource(*resource.Resource) error {
Expand All @@ -52,10 +54,19 @@ func (p *createWebhookSubcommand) Scaffold(fs machinery.Filesystem) error {
Args: p.Args,
}

err := handlePluginResponse(fs, req, p.Path)
err := handlePluginResponse(fs, req, p.Path, p)
if err != nil {
return err
}

return nil
}

func (p *createWebhookSubcommand) InjectConfig(c config.Config) error {
p.config = c
return nil
}

func (p *createWebhookSubcommand) GetConfig() config.Config {
return p.config
}
Loading
Loading