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

feat: Introduce the Features Factory #412

Merged
merged 2 commits into from
Apr 29, 2022

Conversation

clamoriniere
Copy link
Collaborator

What does this PR do?

This draft PR is here to illustrate how the Feature Factory can improve the generically of the DatadogAgent controller logic.

Motivation

  • Ease code evolution
  • Better separation of concern: each feature logic are implemented in a separated package that allows better code ownership detection
  • Ease unit-testing: since each feature should implement the same interface. it will ease the creation of a generic unit-test framework and allow to test each feature separately.

Additional Notes

Some code entry point:

Then the new Reconcile function is define here: https://github.com/DataDog/datadog-operator/blob/clamoriniere/generic-reconciler/controllers/datadogagent/reconcilerv2.go

Describe your test plan

Write there any instructions and details you may have to test your PR.

@clamoriniere clamoriniere added this to the v1.0.0 milestone Nov 30, 2021
@clamoriniere clamoriniere added the enhancement New feature or request label Nov 30, 2021
@clamoriniere clamoriniere requested a review from a team November 30, 2021 14:47
@codecov-commenter
Copy link

codecov-commenter commented Jan 4, 2022

Codecov Report

Merging #412 (4751c1b) into main (c5aa4c7) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #412   +/-   ##
=======================================
  Coverage   60.60%   60.60%           
=======================================
  Files           3        3           
  Lines         132      132           
=======================================
  Hits           80       80           
  Misses         40       40           
  Partials       12       12           
Flag Coverage Δ
unittests 60.60% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c5aa4c7...4751c1b. Read the comment docs.

@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch from 524c60f to d2579c9 Compare January 4, 2022 21:45
@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch 5 times, most recently from bb39d31 to fbc5410 Compare January 17, 2022 17:07
type Feature interface {
// Configure use to configure the internal of a Feature
// It should return `true` if the feature is enabled, else `false`.
Configure(dda *v2alpha1.DatadogAgent) bool
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this function both altering the DatadogAgent spec and checking if the feature is enabled?

Copy link
Contributor

Choose a reason for hiding this comment

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

From the ksmcore example it seems only the latter, in which case maybe we can call it IsConfigured or IsEnabled

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes the idea it to extra the information needed from the DatadogAgent. like this we don't need to access again the resource after when processing the feature

@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch from e68a192 to bb4ee42 Compare January 22, 2022 21:07
@clamoriniere clamoriniere marked this pull request as ready for review January 23, 2022 23:42
Makefile Outdated Show resolved Hide resolved
// OperatorStoreLabelKey used to identified which resource is managed by the store.
OperatorStoreLabelKey = "operator.datadoghq.com/managed-by-store"
// OperatorGenHashAnnotationKey annotation key used on a Resource in order to compare 2 resources without know the spec struct.
OperatorGenHashAnnotationKey = "operator.datadoghq.com/gen-hash"
Copy link
Contributor

Choose a reason for hiding this comment

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

Not used, could be easily implemented in the store, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

return false
}

func (f *dummyFeature) ConfigureV1(dda *v1alpha1.DatadogAgent) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should not be necessary with conversion in place

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree, But for now. I use it to validate the PR in #430

we can easily remove it later

// ManageDependencies allows a feature to manage its dependencies.
// Feature's dependencies should be added in the store.
func (f *dummyFeature) ManageDependencies(store feature.DependenciesStoreClient) error {
return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

With the store API, it means each deps need to do:

Get -> Check if nil -> Not nil: Update
                    -> Nil: Create

I think we should think about how to improve this experience before implementing features?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

IMO dependencies are not so often shared between features.

I was thinking to create a function GetOrCreate that return the instance or create a new instance automatically.
then the Store have a fuction AddOrUpdate so it doesn't need to know if the resource already exist in the API server.

I'm open to other idea

Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking about Roles for instance (or ConfigMap if we decide to move all configs to datadog.yaml). Having a GetOrCreate may not be useful as you will still need to check if the object is newly created or not to fill some fieds.

I think we way want to have Managers on top of the store. Like the RBACManager, offering a high level API like AddPermissions(serviceAccount, roleName, permissions) and when Apply is called on the store, it lazily generates the necessary objects.

About the comment below, your intention looks like a PodTemplateManager to me, with its helper functions to manage each piece.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@vboulineau
I have added a new commit with a RBACManager b0722e2

}

// PodFeatureConfiguration use to store a Pod Feature Configuration.
type PodFeatureConfiguration struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure to understand how this will be used, wasn't able to find any reference?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was a tentative to implement your proposition about not exposing the PodTemplate to feature function but only a subset of what can be configured.

But I haven't found a good implementation. It made me think that it was more clear to share the PodTemplate. and provide helper fonction to add|update|remove envvar, volume, volumemount.

@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch from 3e6a95e to 42c1ff4 Compare January 25, 2022 21:17
@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch from 42c1ff4 to 06ed743 Compare January 25, 2022 21:39
@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch 3 times, most recently from 39f3a0e to 523c55f Compare February 21, 2022 10:49
@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch 2 times, most recently from 89b4697 to 67e92b7 Compare February 21, 2022 12:59
@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch 4 times, most recently from 9814ca2 to 4751c1b Compare April 5, 2022 20:35
@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch 5 times, most recently from c4db9a3 to f0e8997 Compare April 6, 2022 10:53
// Manage dependencies
// -----------------------
depsStore := dependencies.NewStore(&dependencies.StoreOptions{SupportCilium: r.options.SupportCilium})
rbacManager := feature.NewResourcesManagers(depsStore)
Copy link
Contributor

Choose a reason for hiding this comment

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

should this be generalized to resourceManager instead of rbacManager ?

@clamoriniere clamoriniere force-pushed the clamoriniere/generic-reconciler branch from f0e8997 to c6adf6e Compare April 29, 2022 09:30
@clamoriniere clamoriniere requested a review from a team as a code owner April 29, 2022 09:30
@clamoriniere clamoriniere merged commit 88fe849 into main Apr 29, 2022
@clamoriniere clamoriniere deleted the clamoriniere/generic-reconciler branch April 29, 2022 09:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants