Skip to content

Implement InjectRecorder #23

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

Merged
merged 2 commits into from
Jun 15, 2018
Merged

Conversation

lichuqiang
Copy link
Contributor

@lichuqiang lichuqiang commented Jun 12, 2018

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jun 12, 2018
"k8s.io/client-go/tools/record"
)

type RecorderGetFuncType func(string, *runtime.Scheme) record.EventRecorder
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think the interface should take the Scheme. This should be set by the manager.Manager so users don't need to do the wiring themselves.

Copy link
Contributor

Choose a reason for hiding this comment

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

If this is an interface instead of a function, we can provide the interface. Maybe RecoderProvider?


func GetEventRecorderFor(name string, scheme *runtime.Scheme) record.EventRecorder {
if eventBroadcaster == nil {
cs := config.GetKubernetesClientSetOrDie()
Copy link
Contributor

Choose a reason for hiding this comment

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

We should get the clientset from the manager.Manager, or have users set it on a struct. All components should use the same client.

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Jun 13, 2018
@lichuqiang lichuqiang force-pushed the recorder branch 2 times, most recently from 8bd21b3 to fb97e26 Compare June 13, 2018 08:29

cm.recorderProvider = recorder.NewProvider()
cm.recorderProvider.SetScheme(cm.scheme)
cm.recorderProvider.SetClientSet(clientconfig.GetKubernetesClientSetOrDieWithConfig(config))
Copy link
Contributor

@pwittrock pwittrock Jun 13, 2018

Choose a reason for hiding this comment

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

We shouldn't be dying. This function returns an error, we should return an error instead of panicing.

}

// NewProvider create a new Provider instance.
func NewProvider() Provider {
Copy link
Contributor

Choose a reason for hiding this comment

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

The New pattern is useful when step requires synchronous steps, or setup can fail. Since we don't have either in this case, I am not sure how much value this provides.

Copy link
Contributor

Choose a reason for hiding this comment

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

The eventbroadercaster setup could be done here.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should either take the deps in the New function or inject them with Inject functions. provider doesn't implement either inject.Scheme or inject.Config so it can't automatically get its deps from manager.SetFields(provider). If we are making the implementation internal, it doesn't really matter which we choose since we can change it without breaking consumers. New is probably the simpler approach for now, so I'd go with that.

e.g. either

// setup clientset and stuff here
New(config *rest.Config, s scheme *runtime.Scheme) { ... }

or

// Setup clientset and stuff here
(p *provider) InjectClient() {...}
(p *provider) InjectScheme() {...}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yah, I think we do inject for things that cross components, and here we just make it once, so a New function should be enough.

return nil, fmt.Errorf("must call SetScheme to specify Scheme before getting recorder")
}

if p.eventBroadcaster == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

This need to be threadsafe. Either wrap it in a sync.Once.Do call, or put it in "New"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch

@@ -131,5 +133,9 @@ func New(config *rest.Config, options Options) (Manager, error) {

cm.fieldIndexes = cm.cache
cm.client = client.DelegatingClient{ReadInterface: cm.cache, WriteInterface: writeObj}

cm.recorderProvider = recorder.NewProvider()
Copy link
Contributor

@pwittrock pwittrock Jun 13, 2018

Choose a reason for hiding this comment

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

Add a function to manager.go for calling the provider - e.g. add this to the Manager interface and implement it on manager.

type Manager interface {
...
  // GetRecorder returns a new EventRecorder for the provided name 
  GetRecorder(name string) (record.EventRecorder, error)
...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

what the function for? you mean we should not take the provider as a field of manager, and pass it into the recorder injection?

}

// NewProvider create a new Provider instance.
func NewProvider() Provider {
Copy link
Contributor

Choose a reason for hiding this comment

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

Lets move New under pkg/internal until we are sure we know users will need to get informers directly. They should be able to get them through the manager.Manager.

// Recorder is used by the ControllerManager to inject recorder into Sources, EventHandlers, Predicates, and
// Reconciles
type Recorder interface {
InjectRecorder(provider recorder.Provider) error
Copy link
Contributor

Choose a reason for hiding this comment

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

Document this.

package recorder_test

import (
"fmt"
Copy link
Contributor

Choose a reason for hiding this comment

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

Also update the pkg/controller/controller_integration_test.go with a case that uses an EventRecorder and creates and event from the Reconcile loop, then the test checks that the event was recorded.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure thing, just have not got time to make it today

@pwittrock
Copy link
Contributor

Headed in the right direction. Left a few comments on how we want to structure things. Please run test.sh and address the lint errors.

@lichuqiang
Copy link
Contributor Author

have not inject recorder into the components, and want to make clear its boundary:

  1. Whether we really need recorder in all of the components: Reconcilers, Sources, EventHandlers, and Predicates...
  2. Whether we need recorder in all the instances of certain component, or simply that of certain kind.
    eg: whether we should implement InjectRecorder in the source interface layer, or just implement it for Channel/Kind source.

@lichuqiang lichuqiang force-pushed the recorder branch 2 times, most recently from 1fcca7d to d748c8b Compare June 14, 2018 12:10
@pwittrock
Copy link
Contributor

SGTM

return cm, nil
}

// setOptionsDefaults set default values for Options fields
Copy link
Contributor

Choose a reason for hiding this comment

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

+1

@@ -442,3 +473,10 @@ func (i *injectable) InjectStopChannel(stop <-chan struct{}) error {
func (i *injectable) Start(<-chan struct{}) error {
return nil
}

func (i *injectable) InjectRecorder(provider recorder.Provider) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we aren't injecting this, we don't need it.

provider, err := recorder.NewProvider(cfg, scheme.Scheme)
Expect(err).NotTo(HaveOccurred())

recorder := provider.GetEventRecorderFor("test")
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you also test that the event recorder will publish events?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@lichuqiang lichuqiang force-pushed the recorder branch 2 times, most recently from 65fb07a to 2d0510c Compare June 15, 2018 09:46
@lichuqiang lichuqiang changed the title WIP: Implement InjectRecorder Implement InjectRecorder Jun 15, 2018
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 15, 2018
evtWatcher, err := clientset.CoreV1().Events("default").Watch(metav1.ListOptions{})
Expect(err).NotTo(HaveOccurred())

resultEvent := <-evtWatcher.ResultChan()
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an awesome way to both test the watching of these events and make the test run faster without polling!

@@ -122,6 +128,10 @@ func (cm *controllerManager) GetCache() cache.Cache {
return cm.cache
}

func (cm *controllerManager) GetRecorder(name string) record.EventRecorder {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a comment for this function

@pwittrock pwittrock added approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm "Looks good to me", indicates that a PR is ready to be merged. labels Jun 15, 2018
@k8s-ci-robot k8s-ci-robot merged commit b4748ac into kubernetes-sigs:master Jun 15, 2018
justinsb pushed a commit to justinsb/controller-runtime that referenced this pull request Dec 7, 2018
DirectXMan12 pushed a commit that referenced this pull request Jan 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants