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

hive: Reimplement on top of dig #21562

Merged
merged 2 commits into from
Oct 10, 2022
Merged

Conversation

joamaki
Copy link
Contributor

@joamaki joamaki commented Oct 3, 2022

This reimplements hive directly on top of the uber/dig library. This simplifies
dealing with lifecycles and logging, and most importantly allows implementing
the configuration flag registration in a straightforward way with an invoke.

The usage is also simplified as now there's only one library to learn. The
API is very similar to uber/fx, but has support for configs. At "hive" creation
time the config command-line flags are registered, but object graph instantiation
is deferred to start time. This allows supporting command-line applications with
many commands (with each their own object graph) and only instantiating when
we know which command to run.

Usage looks like this now:

type ExampleConfig struct { ... }
func (def ExampleConfig) Flags(flags *pflag.FlagSet) {
  flags.String("foo", def.Foo, "usage of foo")
  ...
}
var defaultConfig = ExampleConfig{...}

var Example = cell.Module(
  "example",
  
  cell.Config(defaultConfig), // Provides `ExampleConfig` and registers the flags

  cell.Provide(newExample, newSomethingElse),
  cell.Invoke(func(ex *Example, se *SomethingElse) error { ...}),

  cell.Decorate(
    func(thing *Thing) *Thing { /* return some augmented Thing */ },
    // Constructor gets augmented Thing:
    cell.Provide(newUserOfThing),
  )  
)
  • fx.Option is no longer used, so there's now just one library to learn and should be less confusing to users.
  • Using dig directly allows more fine-grained control of what gets instantiated when. This allows for way simpler implementation for configs.
  • Provide, Invoke and Module has the same API as Fx does. Decorate instead wraps a set of cells to "decorate" rather than to decorate in the scope of a Module.
  • Lifecycle is now implemented in pkg/hive, which allows more complicated uses like nested lifecycles that we need for the operator.
  • hive.New no longer takes flags and viper instance. Rather there's now RegisterFlags() and Viper().

@joamaki joamaki requested a review from glibsm October 3, 2022 18:09
@maintainer-s-little-helper maintainer-s-little-helper bot added the dont-merge/needs-release-note-label The author needs to describe the release impact of these changes. label Oct 3, 2022
@joamaki joamaki added the release-note/misc This PR makes changes that have no direct user impact. label Oct 4, 2022
@maintainer-s-little-helper maintainer-s-little-helper bot removed the dont-merge/needs-release-note-label The author needs to describe the release impact of these changes. label Oct 4, 2022
@joamaki joamaki changed the title DRAFT: hive: Reimplement on top of dig hive: Reimplement on top of dig Oct 4, 2022
@joamaki joamaki marked this pull request as ready for review October 4, 2022 09:31
@joamaki joamaki requested review from a team as code owners October 4, 2022 09:31
@joamaki
Copy link
Contributor Author

joamaki commented Oct 4, 2022

/test

pkg/k8s/resource/resource_test.go Outdated Show resolved Hide resolved
pkg/hive/internal/text.go Outdated Show resolved Hide resolved
@joamaki joamaki removed the request for review from glibsm October 5, 2022 11:15
@joamaki joamaki force-pushed the pr/joamaki/hive-dig-it branch 2 times, most recently from c7674b1 to fba199e Compare October 5, 2022 17:12
@joamaki
Copy link
Contributor Author

joamaki commented Oct 5, 2022

Revamped PrintObjects:

$ ./cilium-agent objects                                                                                                     
Cells:                                                                                                                                                                                    
                                                                                                                                                                                          
  gops:                                                                                                                                                                                   
    ⚙️  gops.GopsConfig:                                                                                                                                                                   
      gops-port[uint16]=9890                                                                                                                                                              
                                                                                                                                                                                          
    🛠️  pkg/gops.registerGopsHooks (cell.go:38) (func(hive.Lifecycle, logrus.FieldLogger, gops.GopsConfig))                                                                                
                                                                                                                                                                                          
                                                                                                                                                                                          
  k8s-client:                                                                                                                                                                             
    ⚙️  client.Config:                                                                                                                                                                     
      enable-k8s-api-discovery[bool]=false                                                                                                                                                
      k8s-api-server[string]=                                                                                                                                                             
      k8s-client-burst[int]=0                                                                                                                                                             
      k8s-client-qps[float32]=0                                                                                                                                                           
      k8s-heartbeat-timeout[duration]=30s                                                                                                                                                 
      k8s-kubeconfig-path[string]=                                                                                                                                                        
                                                                                                                                                                                          
    🚧️ pkg/k8s/client.newClientset (cell.go:106): func(hive.Lifecycle, logrus.FieldLogger, client.Config) (client.Clientset, error)                                                       
                                                                                                                                                                                          
                                                                                                                                                                                          
                                                                                                                                                                                          
  ⚙️  cmd.DaemonCellConfig:                                                                                                                                                                
    skip-daemon[bool]=false                                                                                                                                                               
                                                                                                                                                                                          
  🛠️  daemon/cmd.registerDaemonHooks (daemon_main.go:1570) (func(cmd.daemonParams) error)                                                                                                  
                                                                                                                                                                                          
  🚧️ pkg/node.newLocalNodeStore (local_node_store.go:71): func(node.LocalNodeStoreParams) (node.LocalNodeStore, error)                                                                    
                                                                                                                                                                                          
                                                                                                                                                                                          
Start hooks:                                                                                                                                                                              
                                                                                                                                                                                          
  • pkg/gops.registerGopsHooks.func1 (cell.go:43)                                                                                                                                         
  • pkg/node.newLocalNodeStore.func1 (local_node_store.go:80)                                                                                                                             
  • daemon/cmd.registerDaemonHooks.func1 (daemon_main.go:1591)                                                                                                                            
                                                                                                                                                                                          
Stop hooks:                                                                                                                                                                               
                                                                                                                                                                                          
  • daemon/cmd.registerDaemonHooks.func2 (daemon_main.go:1607)                               
  • pkg/node.newLocalNodeStore.func2 (local_node_store.go:95)                                
  • pkg/gops.registerGopsHooks.func2 (cell.go:50)                                            

Could still improve the config flag printing, but I think this is enough for now.

Copy link
Member

@glibsm glibsm left a comment

Choose a reason for hiding this comment

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

This is awesome!! Especially the printing of the objects that you've set up by implementing a different vision on top of dig (that's the reason the two were kept separate)

pkg/hive/shutdowner.go Outdated Show resolved Hide resolved
pkg/hive/cell/cell.go Show resolved Hide resolved
pkg/hive/hive_test.go Outdated Show resolved Hide resolved

func (p *provider) Apply(c container) error {
for _, ctor := range p.ctors {
if err := c.Provide(ctor, dig.Export(true)); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Just wanted to double check that dig.Export here is explicit and to stay forever. On first glance this seems like it eliminates scoping from hive entirely, is that correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep all that scope right now does is give a name to a group of cells. Afaik exactly same as fx? I was thinking of adding something like ProvidePrivate or possible change to Provide(ctor, opts…) form, so you’d do Provide(newPrivateThing, cell.Private).

I was a bit puzzled that fx doesn’t have this. Wasn’t needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added ProvidePrivate. It's nice that Provide takes ctor... so didn't go with optional.

}

func (d *decorator) Apply(c container) error {
scope := c.Scope("(decorate)")
Copy link
Member

Choose a reason for hiding this comment

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

Are decorators scoped out purely for logging and bookkeeping purposes?

Copy link
Contributor Author

@joamaki joamaki Oct 5, 2022

Choose a reason for hiding this comment

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

To me it seemed to make more sense to wrap things with a decorator rather than having the decorator be associated with a module and it’s scope. That’s why I ended up with this. Name of the scope could be better.

Hmm need to check what this ends up looking like in dot graph. Might not look quite right.

EDIT: dot-graph looks fine. Fixed up cilium-agent objects a bit for Decorate:

  🔀 daemon/cmd.init.0.func1 (root.go:92): func(cmd.DaemonCellConfig) cmd.DaemonCellConfig:
    🛠️ daemon/cmd.init.0.func2 (root.go:94): func(cmd.DaemonCellConfig)

You read the above as: there's a decorator (emoji conveying "swapping") that takes DaemonCellConfig and returns an augmented DaemonCellConfig, and it's wrapping an Invoke function.

This reimplements hive directly on top of the uber/dig library. This
simplifies dealing with lifecycles and logging, and most importantly
allows implementing the configuration flag registration in a
straightforward way with an invoke.

The usage is also simplified as now there's only one library to
learn. The API is now very similar to uber/fx, but with the extra
Config() option.

To simplify use of hive.New the flags are now registered afterwards
with RegisterFlags(), and hive creates its own viper instance that
can be retrieved with Viper().

See pkg/hive/example/main.go for summary of the API changes.

Signed-off-by: Jussi Maki <jussi@isovalent.com>
We need to make sure that LocalNodeStore gets started before registerDaemonHooks
as otherwise Get/Update blocks. Fix this by making registerDaemonHooks depend
on LocalNodeStore and move the setting of node.localNode to it.

Since the list of parameters for registerDaemonHooks is getting quite long,
add daemonParams struct.

Fixes: d62bd53 ("node: Add LocalNodeStore and convert getters/setters to it")
Signed-off-by: Jussi Maki <jussi@isovalent.com>
@joamaki
Copy link
Contributor Author

joamaki commented Oct 6, 2022

/test

Copy link
Member

@dylandreimerink dylandreimerink left a comment

Choose a reason for hiding this comment

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

LGTM!

@joamaki joamaki added the ready-to-merge This PR has passed all tests and received consensus from code owners to merge. label Oct 7, 2022
@maintainer-s-little-helper maintainer-s-little-helper bot removed the ready-to-merge This PR has passed all tests and received consensus from code owners to merge. label Oct 7, 2022
@sayboras sayboras merged commit f20d921 into cilium:master Oct 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-note/misc This PR makes changes that have no direct user impact.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants