From 7e5bef57a92da10076058cbaf180a5bd7158a49a Mon Sep 17 00:00:00 2001 From: David Simansky Date: Fri, 20 Oct 2023 12:22:26 +0200 Subject: [PATCH] More lazy init --- cmd/kn/main.go | 23 ++++--- pkg/kn/commands/service/describe.go | 10 ++- pkg/kn/plugin/context_sharing.go | 99 ++++++++++++++------------- pkg/kn/plugin/context_sharing_test.go | 11 ++- 4 files changed, 77 insertions(+), 66 deletions(-) diff --git a/cmd/kn/main.go b/cmd/kn/main.go index 2b110a18bc..c88980fba7 100644 --- a/cmd/kn/main.go +++ b/cmd/kn/main.go @@ -85,8 +85,9 @@ func run(args []string) error { } // FT: Context Sharing + var ctxManager *pluginpkg.ContextDataManager if config.GlobalConfig.ContextSharing() { - ctxManager, err := pluginpkg.NewContextManager() + ctxManager, err = pluginpkg.NewContextManager(pluginManager) if err != nil { return err } @@ -96,15 +97,6 @@ func run(args []string) error { println("error during write") } }(ctxManager) - - err = ctxManager.FetchManifests(pluginManager) - if err != nil { - return err - } - - // Inject shared context data as context.Context - contextData := ctxManager.FetchContextData(pluginManager) - rootCmd.SetContext(contextData) } if plugin != nil { @@ -113,7 +105,16 @@ func run(args []string) error { if err != nil { return err } - + if config.GlobalConfig.ContextSharing() { + if pwm, ok := plugin.(pluginpkg.PluginWithManifest); ok { + data, _ := ctxManager.FetchContextData() + err := pwm.ExecuteWithContext(data, argsWithoutCommands(args, plugin.CommandParts())) + if err != nil { + return &runError{err: err} + } + } + return nil + } err := plugin.Execute(argsWithoutCommands(args, plugin.CommandParts())) if err != nil { return &runError{err: err} diff --git a/pkg/kn/commands/service/describe.go b/pkg/kn/commands/service/describe.go index 06b283c0b9..8721dc870b 100644 --- a/pkg/kn/commands/service/describe.go +++ b/pkg/kn/commands/service/describe.go @@ -19,7 +19,7 @@ import ( "errors" "fmt" "io" - + "knative.dev/client/pkg/kn/plugin" "sort" "strconv" "strings" @@ -102,8 +102,12 @@ func NewServiceDescribeCommand(p *commands.KnParams) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { var serviceName string if len(args) != 1 { - if cmd.Context() != nil && cmd.Context().Value("service") != nil { - serviceName = cmd.Context().Value("service").(string) + if plugin.CtxManager != nil { + data, err := plugin.CtxManager.FetchContextData() + if err != nil { + return err + } + serviceName = data["service"] } if serviceName == "" { return errors.New("'service describe' requires the service name given as single argument") diff --git a/pkg/kn/plugin/context_sharing.go b/pkg/kn/plugin/context_sharing.go index 85463607fd..35ea0a1666 100644 --- a/pkg/kn/plugin/context_sharing.go +++ b/pkg/kn/plugin/context_sharing.go @@ -16,7 +16,6 @@ package plugin import ( "bytes" - "context" "encoding/json" "io/fs" "os" @@ -28,10 +27,6 @@ import ( ) //--TYPES-- -//TODO: move types into its own file - -//TODO: let's see if we need this map -//type ContextData map[string]string // Manifest represents plugin metadata type Manifest struct { @@ -44,7 +39,6 @@ type Manifest struct { // ProducesContextDataKeys is a list of keys for the ContextData that // a plugin can produce. Nil or an empty list declares that this // plugin is not ContextDataProducer - //TODO: well-known keys could be const, or this can be its own data structure ProducesContextDataKeys []string `json:"producesKeys,omitempty"` // ConsumesContextDataKeys is a list of keys from a ContextData that a @@ -65,7 +59,7 @@ type PluginWithManifest interface { GetContextData() map[string]string // ExecuteWithContext - ExecuteWithContext(ctx context.Context, args []string) error + ExecuteWithContext(ctx map[string]string, args []string) error } //--TYPES-- @@ -74,32 +68,19 @@ var CtxManager *ContextDataManager type ContextDataManager struct { //ContextData map[string]ContextData `json:"-"` - Producers map[string][]string `json:"producers"` - Consumers map[string][]string `json:"consumers"` - Manifests map[string]Manifest `json:"manifests"` + PluginManager *Manager `json:"-"` + Producers map[string][]string `json:"producers"` + Consumers map[string][]string `json:"consumers"` + Manifests map[string]Manifest `json:"manifests"` } -func NewContextManager() (*ContextDataManager, error) { +func NewContextManager(pluginManager *Manager) (*ContextDataManager, error) { if CtxManager == nil { - //println("opening file...") - //file, err := os.Open(filepath.Join(filepath.Dir(config.GlobalConfig.ConfigFile()), "context.json")) - //if err != nil { - // return nil, err - //} - //decoder := json.NewDecoder(file) - //ctxManager = &ContextDataManager{} - //if err := decoder.Decode(ctxManager); err != nil { - // return nil, err - //} - //out := new(bytes.Buffer) - //enc := json.NewEncoder(out) - //enc.SetIndent("", " ") - //enc.Encode(ctxManager) - //println(out.String()) CtxManager = &ContextDataManager{ - Producers: map[string][]string{}, - Consumers: map[string][]string{}, - Manifests: map[string]Manifest{}, + PluginManager: pluginManager, + Producers: map[string][]string{}, + Consumers: map[string][]string{}, + Manifests: map[string]Manifest{}, } } return CtxManager, nil @@ -115,27 +96,31 @@ func (c *ContextDataManager) GetProducesKeys(pluginName string) []string { return c.Manifests[pluginName].ProducesContextDataKeys } -func (c *ContextDataManager) FetchContextData(pluginManager *Manager) context.Context { - ctx := context.Background() - for _, p := range pluginManager.GetInternalPlugins() { +func (c *ContextDataManager) FetchContextData() (map[string]string, error) { + // Load cached data first + if err := c.loadCache(); err != nil { + return nil, err + } + + // Fetch manifests + if err := c.FetchManifests(); err != nil { + return nil, err + } + + // Get context data, limited to func only + for _, p := range c.PluginManager.GetInternalPlugins() { if p.Name() == "kn func" { if pwm, ok := p.(PluginWithManifest); ok { - data := pwm.GetContextData() - - for k, v := range data { - ctx = context.WithValue(ctx, k, v) - } + return pwm.GetContextData(), nil } } } - //ctx = context.WithValue(ctx, "service", "hello") - return ctx - + return map[string]string{}, nil } // FetchManifests it tries to retrieve manifest from both inlined and external plugins -func (c *ContextDataManager) FetchManifests(pluginManager *Manager) error { - for _, plugin := range pluginManager.GetInternalPlugins() { +func (c *ContextDataManager) FetchManifests() error { + for _, plugin := range c.PluginManager.GetInternalPlugins() { manifest := &Manifest{} // For the integrity build the same name format as external plugins pluginName := "kn-" + strings.Join(plugin.CommandParts(), "-") @@ -150,7 +135,7 @@ func (c *ContextDataManager) FetchManifests(pluginManager *Manager) error { c.populateDataKeys(manifest, pluginName) } } - plugins, err := pluginManager.ListPlugins() + plugins, err := c.PluginManager.ListPlugins() if err != nil { return err } @@ -210,16 +195,38 @@ func fetchExternalManifest(p Plugin) *Manifest { return manifest } -// TODO: store to file actually +func (c *ContextDataManager) loadCache() error { + cacheFile := filepath.Join(filepath.Dir(config.GlobalConfig.ConfigFile()), "context.json") + if _, err := os.Stat(cacheFile); err != nil { + if os.IsNotExist(err) { + return nil // No cache file yet + } else { + return err + } + } + + file, err := os.Open(cacheFile) + if err != nil { + return err + } + decoder := json.NewDecoder(file) + ctxManager := &ContextDataManager{} + if err := decoder.Decode(ctxManager); err != nil { + return err + } + c.Manifests = ctxManager.Manifests + c.Producers = ctxManager.Producers + c.Consumers = ctxManager.Consumers + return nil +} + // WriteCache store data back to cache file func (c *ContextDataManager) WriteCache() error { - //println("\n====\nContext Data to be stored:") out := new(bytes.Buffer) enc := json.NewEncoder(out) enc.SetIndent("", " ") if err := enc.Encode(c); err != nil { return nil } - //println(out.String()) return os.WriteFile(filepath.Join(filepath.Dir(config.GlobalConfig.ConfigFile()), "context.json"), out.Bytes(), fs.FileMode(0664)) } diff --git a/pkg/kn/plugin/context_sharing_test.go b/pkg/kn/plugin/context_sharing_test.go index 852feec8e3..17e97e9fd4 100644 --- a/pkg/kn/plugin/context_sharing_test.go +++ b/pkg/kn/plugin/context_sharing_test.go @@ -17,7 +17,6 @@ package plugin import ( - "context" "os" "path/filepath" "strings" @@ -34,7 +33,7 @@ type testPluginWithManifest struct { func (t testPluginWithManifest) GetManifest() *Manifest { return t.manifest } func (t testPluginWithManifest) GetContextData() map[string]string { return t.contextData } -func (t testPluginWithManifest) ExecuteWithContext(ctx context.Context, args []string) error { +func (t testPluginWithManifest) ExecuteWithContext(ctx map[string]string, args []string) error { return nil } @@ -86,10 +85,10 @@ func TestFetchManifest(t *testing.T) { }) if len(tc.cmdPart) == 0 { - ctxManager, err := NewContextManager() + ctxManager, err := NewContextManager(c.pluginManager) assert.NilError(t, err) - err = ctxManager.FetchManifests(c.pluginManager) + err = ctxManager.FetchManifests() assert.NilError(t, err) assert.Assert(t, len(ctxManager.Manifests) == 0) return @@ -105,10 +104,10 @@ func TestFetchManifest(t *testing.T) { prepareInternalPlugins(testPlugin{parts: tc.cmdPart}) } - ctxManager, err := NewContextManager() + ctxManager, err := NewContextManager(c.pluginManager) assert.NilError(t, err) - err = ctxManager.FetchManifests(c.pluginManager) + err = ctxManager.FetchManifests() assert.NilError(t, err) assert.Assert(t, len(ctxManager.Manifests) == 1)