Repository navigation
actions: refactor to move template and manifest rendering to a helper - #1563
Conversation
|
Skipping CI for Draft Pull Request. |
|
/cc @lburgazzoli |
This PR can't be merged just yet 😢Please run For more info: https://github.com/opendatahub-io/opendatahub-operator/actions/runs/12953054333 |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1563 +/- ##
==========================================
+ Coverage 20.33% 20.50% +0.16%
==========================================
Files 163 165 +2
Lines 11164 11199 +35
==========================================
+ Hits 2270 2296 +26
- Misses 8657 8672 +15
+ Partials 237 231 -6 ☔ View full report in Codecov by Sentry. |
| "github.com/opendatahub-io/opendatahub-operator/v2/pkg/controller/types" | ||
| ) | ||
|
|
||
| type Renderer interface { |
There was a problem hiding this comment.
| type Renderer interface { | |
| // Renderer defines an interface for rendering resources. | |
| type Renderer interface { |
| Render(r Renderer, rr *types.ReconciliationRequest) (any, bool, error) | ||
| } | ||
|
|
||
| type CachingKeyFn func(rr *types.ReconciliationRequest) ([]byte, error) |
There was a problem hiding this comment.
| type CachingKeyFn func(rr *types.ReconciliationRequest) ([]byte, error) | |
| // CachingKeyFn is a function type used to generate a caching key based on the reconciliation request. | |
| type CachingKeyFn func(rr *types.ReconciliationRequest) ([]byte, error) |
|
|
||
| type CachingKeyFn func(rr *types.ReconciliationRequest) ([]byte, error) | ||
|
|
||
| type Cacher struct { |
There was a problem hiding this comment.
| type Cacher struct { | |
| // Cacher provides caching capabilities for rendered resources. | |
| type Cacher struct { |
|
|
||
| type CacherOpts func(*Cacher) | ||
|
|
||
| func (s *Cacher) SetKey(key CachingKeyFn) { |
There was a problem hiding this comment.
| func (s *Cacher) SetKey(key CachingKeyFn) { | |
| // SetKey sets the caching key function for the Cacher. | |
| func (s *Cacher) SetKey(key CachingKeyFn) { |
| resList := resources.UnstructuredList(resUnstructured) | ||
|
|
||
| if acted { | ||
| controllerName := strings.ToLower(rr.Instance.GetObjectKind().GroupVersionKind().Kind) |
There was a problem hiding this comment.
| controllerName := strings.ToLower(rr.Instance.GetObjectKind().GroupVersionKind().Kind) | |
| // Track the number of rendered resources for monitoring | |
| controllerName := strings.ToLower(rr.Instance.GetObjectKind().GroupVersionKind().Kind) |
| controllerName := strings.ToLower(rr.Instance.GetObjectKind().GroupVersionKind().Kind) | ||
| render.RenderedResourcesTotal.WithLabelValues(controllerName, s.Name()).Add(float64(len(resList))) | ||
|
|
||
| rr.Generated = true |
There was a problem hiding this comment.
| rr.Generated = true | |
| // Mark the reconciliation request as having generated resources | |
| rr.Generated = true |
| // deep copy object so changes done in the pipelines won't | ||
| // alter them |
There was a problem hiding this comment.
| // deep copy object so changes done in the pipelines won't | |
| // alter them | |
| // Append cloned resources to the reconciliation request, | |
| // ensuring a deep copy so changes in the pipeline don't alter the original objects. |
| return res, true, nil | ||
| } | ||
|
|
||
| func (a *Action) render(rr *types.ReconciliationRequest) ([]unstructured.Unstructured, error) { |
There was a problem hiding this comment.
| func (a *Action) render(rr *types.ReconciliationRequest) ([]unstructured.Unstructured, error) { | |
| // render handles the rendering of resources using the Kustomize engine. | |
| func (a *Action) render(rr *types.ReconciliationRequest) ([]unstructured.Unstructured, error) { |
| } | ||
|
|
||
| rr.Generated = true | ||
| func (a *Action) Render(_ cacher.Renderer, rr *types.ReconciliationRequest) (any, bool, error) { |
There was a problem hiding this comment.
| func (a *Action) Render(_ cacher.Renderer, rr *types.ReconciliationRequest) (any, bool, error) { | |
| // Render is a wrapper for rendering the reconciliation request using Kustomize. | |
| // It returns the rendered resources and indicates if any resources were acted upon. | |
| func (a *Action) Render(_ cacher.Renderer, rr *types.ReconciliationRequest) (any, bool, error) { |
|
@ykaliuta, I believe the same caching abstraction can be applied to the |
…flux/component-updates/odh-kueue-controller-v2-17 chore(deps): update odh-kueue-controller-v2-17 to 3f5e5f0
f316a77 to
c4f5a74
Compare
b3b34ca to
e9e2fed
Compare
2559e06 to
34bf0d9
Compare
|
/test all |
34bf0d9 to
b0b8909
Compare
|
/test opendatahub-operator-e2e |
|
/retest |
Makes sense! I tend to comment everything for readability, but sometimes it’s a bit too much! 😆 |
a139306 to
2f5c0cb
Compare
|
/unhold |
|
/test opendatahub-operator-e2e |
1 similar comment
|
/test opendatahub-operator-e2e |
|
/test all |
|
/retest |
|
/test all |
1 similar comment
|
/test all |
The caching code for all caching Actions is pretty much the same
1) hash the key
2) if the hash is the same as the stored one, return stored resource
3) otherwise render the resource
4) store the key and the resource
5) return the resource
Abstract this logic into Cacher[T] generic type, where T is the type
of the resource, which is supposed to be included as a composition
in on the higher levels.
The core engine is implemented as Render method which accepts actual
renderer (function) and return boolean value in addition to the
resources indicating if it performed rendering. It supposed to be
used in resource rendering actions for accounting.
The renderer should return (error, T).
Put renderer as the last argument to make more convenient to call
with a closure.
Copy CachingKeyFn type. In future it can be extented to accept
context for possible debug logging.
Define contract, CachingKeyFn should not return empty hash. (!!!)
The code originally is taken pretty much from the existing kustomize
rendering Action. But taking into account the contract, refactor the
code to avoid comparation of the result (and nil assignments) since
with generics to work with both nilable (maps, arrays, etc) and
non-nilable (ints, strings) types it requires some extra handling of
Zero values.
Zero values are still required for return values in error case, so
implement Zero[T] generic function.
With the assumption make "empty key" a error condition, it removes
need of extra comparation.
Move actual rendering and cache update into separate function, it
makes main Render function more clear without extra indentation.
Possible usage supposed to be
```
type Action struct {
cacher.Cacher[resources.UnstructuredList]
// action data
}
type ActionOpts func(*Action)
func WithCache() ActionOpts {
return func(a *Action) {
a.Cacher.SetKeyFn(types.Hash) // or own
}
}
func (s *Action) run(ctx context.Context, rr *types.ReconciliationRequest) error {
//...
res, acted, err := a.Cacher.Render(ctx, rr, s.render)
if acted { /* may be do something */ }
// use resources
}
func (s *Action) render(ctx context.Context, rr *types.ReconciliationRequest) (resources.UnstructuredList, error) {
result := make(resources.UnstructuredList)
// make some resources
return result, nil
}
func NewAction(opts ...ActionOpts) actions.Fn {
for _, opt := range opts {
opt(&action)
}
return action.run
}
```
with reconciler creation
```
func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error {
_, err := reconciler.ReconcilerFor(
mgr,
&componentApi.Type{},
).
// ...
WithAction(foobar.NewAction(
foobar.WithCache(),
))
}
```
Pay attention to signature of render() and call to Cacher.Render()
from run().
Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com>
Make an abstract wrapper on the raw cacher.Cacher[T] to work on
actions whose Renderer produces
resources (resources.UnstructuredList) to be recorded into
ReconciliationRequest.Manifests and later deployed (deleted) to the
cluster.
Performs common tasks:
- invalidate caching if devflags enabled;
- account resources if they were generated (and skip cached ones);
- add resources to the ReconciliationRequest.
Created constructor which accepts mandatory name. Since it is used
in prometheus, it's recommended to make it lower case.
At this moment this is the only part which interested in the origin
of resources (cached or not) and it's supposed to be used in actions
whose final goal is to add resources to the request (which is done
by this helper), so change signature of its Render()
method (comparing to cacher.Cacher) to return only error.
The code is basically copied from manifest/template rendering
actions run() method.
Usage (changed run() comparing to cacher.Cacher):
```
type Action struct {
resourcecacher.ResourceCacher
// action data
}
type ActionOpts func(*Action)
func WithCache() ActionOpts {
return func(a *Action) {
a.ResourceCacher.SetKeyFn(types.Hash) // or own
}
}
func (s *Action) run(ctx context.Context, rr *types.ReconciliationRequest) error {
//...
return a.ResourceCacher.Render(ctx, rr, s.render)
}
func (s *Action) render(ctx context.Context, rr *types.ReconciliationRequest) (resources.UnstructuredList, error) {
result := make(resources.UnstructuredList)
// make some resources
return result, nil
}
func NewAction(opts ...ActionOpts) actions.Fn {
action := Action{
ResourceCacher: resourcecacher.NewResourceCacher("mycoolaction"),
}
for _, opt := range opts {
opt(&action)
}
return action.run
}
```
Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com>
All the run() functionality now implemented by the ResourceCacher, so just call its Render() in run(). Amend WithCache() as required by ResourceCacher to set its CachingKeyFn and remove CachingKeyFn initialization from NewAction. Change render() signature to return resources.UnstructuredList RendererEngine is used as a field initializer now, so no need to export, rename to rendererEngine. Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com>
The same changes as to manifests rendering. Just keep `data` field initialization in NewAction, used in render(). Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com>
2f5c0cb to
fd89af3
Compare
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: lburgazzoli, zdtsw The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
…opendatahub-io#1563) * actions: abstract caching functionality into cacher The caching code for all caching Actions is pretty much the same 1) hash the key 2) if the hash is the same as the stored one, return stored resource 3) otherwise render the resource 4) store the key and the resource 5) return the resource Abstract this logic into Cacher[T] generic type, where T is the type of the resource, which is supposed to be included as a composition in on the higher levels. The core engine is implemented as Render method which accepts actual renderer (function) and return boolean value in addition to the resources indicating if it performed rendering. It supposed to be used in resource rendering actions for accounting. The renderer should return (error, T). Put renderer as the last argument to make more convenient to call with a closure. Copy CachingKeyFn type. In future it can be extented to accept context for possible debug logging. Define contract, CachingKeyFn should not return empty hash. (!!!) The code originally is taken pretty much from the existing kustomize rendering Action. But taking into account the contract, refactor the code to avoid comparation of the result (and nil assignments) since with generics to work with both nilable (maps, arrays, etc) and non-nilable (ints, strings) types it requires some extra handling of Zero values. Zero values are still required for return values in error case, so implement Zero[T] generic function. With the assumption make "empty key" a error condition, it removes need of extra comparation. Move actual rendering and cache update into separate function, it makes main Render function more clear without extra indentation. Possible usage supposed to be ``` type Action struct { cacher.Cacher[resources.UnstructuredList] // action data } type ActionOpts func(*Action) func WithCache() ActionOpts { return func(a *Action) { a.Cacher.SetKeyFn(types.Hash) // or own } } func (s *Action) run(ctx context.Context, rr *types.ReconciliationRequest) error { //... res, acted, err := a.Cacher.Render(ctx, rr, s.render) if acted { /* may be do something */ } // use resources } func (s *Action) render(ctx context.Context, rr *types.ReconciliationRequest) (resources.UnstructuredList, error) { result := make(resources.UnstructuredList) // make some resources return result, nil } func NewAction(opts ...ActionOpts) actions.Fn { for _, opt := range opts { opt(&action) } return action.run } ``` with reconciler creation ``` func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { _, err := reconciler.ReconcilerFor( mgr, &componentApi.Type{}, ). // ... WithAction(foobar.NewAction( foobar.WithCache(), )) } ``` Pay attention to signature of render() and call to Cacher.Render() from run(). Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> * actions: abstract resource caching into resourcecacher Make an abstract wrapper on the raw cacher.Cacher[T] to work on actions whose Renderer produces resources (resources.UnstructuredList) to be recorded into ReconciliationRequest.Manifests and later deployed (deleted) to the cluster. Performs common tasks: - invalidate caching if devflags enabled; - account resources if they were generated (and skip cached ones); - add resources to the ReconciliationRequest. Created constructor which accepts mandatory name. Since it is used in prometheus, it's recommended to make it lower case. At this moment this is the only part which interested in the origin of resources (cached or not) and it's supposed to be used in actions whose final goal is to add resources to the request (which is done by this helper), so change signature of its Render() method (comparing to cacher.Cacher) to return only error. The code is basically copied from manifest/template rendering actions run() method. Usage (changed run() comparing to cacher.Cacher): ``` type Action struct { resourcecacher.ResourceCacher // action data } type ActionOpts func(*Action) func WithCache() ActionOpts { return func(a *Action) { a.ResourceCacher.SetKeyFn(types.Hash) // or own } } func (s *Action) run(ctx context.Context, rr *types.ReconciliationRequest) error { //... return a.ResourceCacher.Render(ctx, rr, s.render) } func (s *Action) render(ctx context.Context, rr *types.ReconciliationRequest) (resources.UnstructuredList, error) { result := make(resources.UnstructuredList) // make some resources return result, nil } func NewAction(opts ...ActionOpts) actions.Fn { action := Action{ ResourceCacher: resourcecacher.NewResourceCacher("mycoolaction"), } for _, opt := range opts { opt(&action) } return action.run } ``` Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> * actions: use resourcecacher for manifests rendering All the run() functionality now implemented by the ResourceCacher, so just call its Render() in run(). Amend WithCache() as required by ResourceCacher to set its CachingKeyFn and remove CachingKeyFn initialization from NewAction. Change render() signature to return resources.UnstructuredList RendererEngine is used as a field initializer now, so no need to export, rename to rendererEngine. Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> * actions: use resourcecacher for template rendering The same changes as to manifests rendering. Just keep `data` field initialization in NewAction, used in render(). Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> --------- Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com>
…1666) * Improve gc service/action (#1656) * Improve gc service/action * Remove useless lock * Fix typos * Improve error handling * update: remove ondelete not used for cleanup steps (#1621) * update: remove ondelete not used for cleanup steps - we do not unpatch auth in smcp since we do not support unmanaged case --------- Signed-off-by: Wen Zhou <wenzhou@redhat.com> * actions: refactor to move template and manifest rendering to a helper (#1563) * actions: abstract caching functionality into cacher The caching code for all caching Actions is pretty much the same 1) hash the key 2) if the hash is the same as the stored one, return stored resource 3) otherwise render the resource 4) store the key and the resource 5) return the resource Abstract this logic into Cacher[T] generic type, where T is the type of the resource, which is supposed to be included as a composition in on the higher levels. The core engine is implemented as Render method which accepts actual renderer (function) and return boolean value in addition to the resources indicating if it performed rendering. It supposed to be used in resource rendering actions for accounting. The renderer should return (error, T). Put renderer as the last argument to make more convenient to call with a closure. Copy CachingKeyFn type. In future it can be extented to accept context for possible debug logging. Define contract, CachingKeyFn should not return empty hash. (!!!) The code originally is taken pretty much from the existing kustomize rendering Action. But taking into account the contract, refactor the code to avoid comparation of the result (and nil assignments) since with generics to work with both nilable (maps, arrays, etc) and non-nilable (ints, strings) types it requires some extra handling of Zero values. Zero values are still required for return values in error case, so implement Zero[T] generic function. With the assumption make "empty key" a error condition, it removes need of extra comparation. Move actual rendering and cache update into separate function, it makes main Render function more clear without extra indentation. Possible usage supposed to be ``` type Action struct { cacher.Cacher[resources.UnstructuredList] // action data } type ActionOpts func(*Action) func WithCache() ActionOpts { return func(a *Action) { a.Cacher.SetKeyFn(types.Hash) // or own } } func (s *Action) run(ctx context.Context, rr *types.ReconciliationRequest) error { //... res, acted, err := a.Cacher.Render(ctx, rr, s.render) if acted { /* may be do something */ } // use resources } func (s *Action) render(ctx context.Context, rr *types.ReconciliationRequest) (resources.UnstructuredList, error) { result := make(resources.UnstructuredList) // make some resources return result, nil } func NewAction(opts ...ActionOpts) actions.Fn { for _, opt := range opts { opt(&action) } return action.run } ``` with reconciler creation ``` func (s *componentHandler) NewComponentReconciler(ctx context.Context, mgr ctrl.Manager) error { _, err := reconciler.ReconcilerFor( mgr, &componentApi.Type{}, ). // ... WithAction(foobar.NewAction( foobar.WithCache(), )) } ``` Pay attention to signature of render() and call to Cacher.Render() from run(). Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> * actions: abstract resource caching into resourcecacher Make an abstract wrapper on the raw cacher.Cacher[T] to work on actions whose Renderer produces resources (resources.UnstructuredList) to be recorded into ReconciliationRequest.Manifests and later deployed (deleted) to the cluster. Performs common tasks: - invalidate caching if devflags enabled; - account resources if they were generated (and skip cached ones); - add resources to the ReconciliationRequest. Created constructor which accepts mandatory name. Since it is used in prometheus, it's recommended to make it lower case. At this moment this is the only part which interested in the origin of resources (cached or not) and it's supposed to be used in actions whose final goal is to add resources to the request (which is done by this helper), so change signature of its Render() method (comparing to cacher.Cacher) to return only error. The code is basically copied from manifest/template rendering actions run() method. Usage (changed run() comparing to cacher.Cacher): ``` type Action struct { resourcecacher.ResourceCacher // action data } type ActionOpts func(*Action) func WithCache() ActionOpts { return func(a *Action) { a.ResourceCacher.SetKeyFn(types.Hash) // or own } } func (s *Action) run(ctx context.Context, rr *types.ReconciliationRequest) error { //... return a.ResourceCacher.Render(ctx, rr, s.render) } func (s *Action) render(ctx context.Context, rr *types.ReconciliationRequest) (resources.UnstructuredList, error) { result := make(resources.UnstructuredList) // make some resources return result, nil } func NewAction(opts ...ActionOpts) actions.Fn { action := Action{ ResourceCacher: resourcecacher.NewResourceCacher("mycoolaction"), } for _, opt := range opts { opt(&action) } return action.run } ``` Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> * actions: use resourcecacher for manifests rendering All the run() functionality now implemented by the ResourceCacher, so just call its Render() in run(). Amend WithCache() as required by ResourceCacher to set its CachingKeyFn and remove CachingKeyFn initialization from NewAction. Change render() signature to return resources.UnstructuredList RendererEngine is used as a field initializer now, so no need to export, rename to rendererEngine. Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> * actions: use resourcecacher for template rendering The same changes as to manifests rendering. Just keep `data` field initialization in NewAction, used in render(). Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> --------- Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> * chore: move Release strcuts from pkg/cluster to apis/common (#1580) Structs that are part of the public APIs should be in the apis packages for consistency and to avoid cyclic import issues (cherry picked from commit 1760dee) * feat(test): support for Succeed and MatchError in testf framework (#1628) (cherry picked from commit a774767) * chore(kserve): wrap error returned by FromUnstructured (#1646) (cherry picked from commit 9402f6a) --------- Signed-off-by: Wen Zhou <wenzhou@redhat.com> Signed-off-by: Yauheni Kaliuta <ykaliuta@redhat.com> Co-authored-by: Luca Burgazzoli <lburgazzoli@users.noreply.github.com> Co-authored-by: Yauheni Kaliuta <ykaliuta@redhat.com>
Jira: https://issues.redhat.com/browse/RHOAIENG-15921
The PR adds two abstractions
The hashing key function and resource type are parameters.
Then two rendering actions, kustomize and templates, rewritten to use resource rendering abstraction.
See details in the commit messages.
Signed-off-by: Yauheni Kaliuta ykaliuta@redhat.com
Description
How Has This Been Tested?
Screenshot or short clip
Merge criteria