-
Couldn't load subscription status.
- Fork 616
Enable Cert Manager #8509
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
Enable Cert Manager #8509
Conversation
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: matzew The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
| } | ||
|
|
||
| func deploymentName(sinkName string) string { | ||
| return kmeta.ChildName(sinkName, "-deployment") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think on the "generic" certificate factory we could just pass in the name of the deployment to the MakeCert function
| eventPolicyLister: eventPolicyInformer.Lister(), | ||
| //cmCertificateLister: cmCertificateInformer.Lister(), | ||
| //certManagerClient: cmclient.Get(ctx), | ||
| secretLister: secretInformer.Lister(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
knoob needed
|
This needs the knob for turning on/off the "cert manager" hooks. As discussed in #8385 (review) |
|
With the manual created lister, I seem to get error And yes, it gets once created, but I end up in the |
|
@matzew you need to label the certificate with that label |
| logging.FromContext(ctx).Errorw("Error reconciling Certificate", zap.Error(err)) | ||
| return err | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: delete certificate once the feature is turned.
| impl.EnqueueKey, | ||
| )) | ||
|
|
||
| cmCertificateInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Todo: better filtering.
|
TODO: delete certificate once the feature is turned off More unit tests for reconciler will come too |
Signed-off-by: Matthias Wessendorf <[email protected]>
Signed-off-by: Matthias Wessendorf <[email protected]>
Signed-off-by: Matthias Wessendorf <[email protected]>
Signed-off-by: Matthias Wessendorf <[email protected]>
Signed-off-by: Matthias Wessendorf <[email protected]>
Signed-off-by: Matthias Wessendorf <[email protected]>
Signed-off-by: Matthias Wessendorf <[email protected]>
Signed-off-by: Matthias Wessendorf <[email protected]>
| }() | ||
|
|
||
| } else { | ||
| cancelFunc, cancelExists := ctx.Value("cancelFunc").(context.CancelFunc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where is this set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah. yeah. right
| factory.Start(ctx.Done()) | ||
|
|
||
| if !cache.WaitForCacheSync(ctx.Done(), cmCertificateInformer.Informer().HasSynced) { | ||
| logging.FromContext(ctx).Error("Failed to sync Cert Manager Certificate informer") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this ctx will only complete when the pod exits, so this might leak informers watchers/connections, I think we need a "sub-context" for only this factory start/stop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, let me take a look
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
diff --git a/pkg/certificates/certificate.go b/pkg/certificates/certificate.go
index 67f7aa308..cb97e58be 100644
--- a/pkg/certificates/certificate.go
+++ b/pkg/certificates/certificate.go
@@ -17,16 +17,96 @@ limitations under the License.
package certificates
import (
+ "context"
"fmt"
+ "sync"
+ "sync/atomic"
"time"
cmv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
- "knative.dev/pkg/kmeta"
-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/tools/cache"
+ "knative.dev/eventing/pkg/apis/feature"
+ cmclient "knative.dev/eventing/pkg/client/certmanager/clientset/versioned"
+ cminformers "knative.dev/eventing/pkg/client/certmanager/informers/externalversions"
+ cmlistersv1 "knative.dev/eventing/pkg/client/certmanager/listers/certmanager/v1"
+ "knative.dev/pkg/controller"
+ "knative.dev/pkg/injection"
+ "knative.dev/pkg/kmeta"
+ "knative.dev/pkg/logging"
)
+type DynamicCertificatesInformer struct {
+ cancel atomic.Pointer[context.CancelFunc]
+ lister atomic.Pointer[cmlistersv1.CertificateLister]
+ mu sync.Mutex
+}
+
+func NewDynamicCertificatesInformer() *DynamicCertificatesInformer {
+ return &DynamicCertificatesInformer{
+ cancel: atomic.Pointer[context.CancelFunc]{},
+ lister: atomic.Pointer[cmlistersv1.CertificateLister]{},
+ mu: sync.Mutex{},
+ }
+}
+
+func (df *DynamicCertificatesInformer) Reconcile(ctx context.Context, features feature.Flags, handler cache.ResourceEventHandler) error {
+ df.mu.Lock()
+ defer df.mu.Unlock()
+ if !features.IsPermissiveTransportEncryption() && !features.IsStrictTransportEncryption() {
+ return df.stop(ctx)
+ }
+ if df.cancel.Load() != nil {
+ return nil
+ }
+ ctx, cancel := context.WithCancel(ctx)
+
+ client := cmclient.NewForConfigOrDie(injection.GetConfig(ctx))
+
+ factory := cminformers.NewSharedInformerFactoryWithOptions(
+ client,
+ controller.DefaultResyncPeriod,
+ cminformers.WithTweakListOptions(func(options *metav1.ListOptions) {
+ options.LabelSelector = "app.kubernetes.io/component=knative-eventing"
+ }),
+ )
+ informer := factory.Certmanager().V1().Certificates()
+ informer.Informer().AddEventHandler(handler)
+
+ factory.Start(ctx.Done())
+ if !cache.WaitForCacheSync(ctx.Done(), informer.Informer().HasSynced) {
+ defer cancel()
+ return fmt.Errorf("failed to sync cert-manager Certificate informer")
+ }
+
+ lister := informer.Lister()
+ df.lister.Store(&lister)
+ df.cancel.Store(&cancel) // Cancel is always set as last field since it's used as a "guard".
+
+ return nil
+}
+
+func (df *DynamicCertificatesInformer) stop(ctx context.Context) error {
+ cancel := df.cancel.Load()
+ if cancel == nil {
+ logging.FromContext(ctx).Debugw("Certificate informer has not been started, nothing to stop")
+ return nil
+ }
+
+ (*cancel)()
+ df.lister.Store(nil)
+ df.cancel.Store(nil) // Cancel is always set as last field since it's used as a "guard".
+ return nil
+}
+
+func (df *DynamicCertificatesInformer) Lister() *atomic.Pointer[cmlistersv1.CertificateLister] {
+ df.mu.Lock()
+ defer df.mu.Unlock()
+
+ return &df.lister
+}
+
func CertificateName(objName string) string {
return kmeta.ChildName(objName, "-server-tls")
}
diff --git a/pkg/reconciler/integration/sink/controller.go b/pkg/reconciler/integration/sink/controller.go
index dcc181adc..3cdbab854 100644
--- a/pkg/reconciler/integration/sink/controller.go
+++ b/pkg/reconciler/integration/sink/controller.go
@@ -19,19 +19,18 @@ package sink
import (
"context"
- v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "go.uber.org/zap"
"k8s.io/client-go/tools/cache"
- certmanagerclient "knative.dev/eventing/pkg/client/certmanager/clientset/versioned"
- certmanagerinformers "knative.dev/eventing/pkg/client/certmanager/informers/externalversions"
- "knative.dev/pkg/injection"
-
"knative.dev/eventing/pkg/apis/feature"
- v1alpha1 "knative.dev/eventing/pkg/apis/sinks/v1alpha1"
+ "knative.dev/eventing/pkg/apis/sinks/v1alpha1"
"knative.dev/eventing/pkg/auth"
+ "knative.dev/eventing/pkg/certificates"
+ cmclient "knative.dev/eventing/pkg/client/certmanager/clientset/versioned"
"knative.dev/eventing/pkg/client/injection/informers/eventing/v1alpha1/eventpolicy"
"knative.dev/eventing/pkg/client/injection/informers/sinks/v1alpha1/integrationsink"
deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment"
"knative.dev/pkg/client/injection/kube/informers/core/v1/service"
+ "knative.dev/pkg/injection"
pkgreconciler "knative.dev/pkg/reconciler"
integrationsinkreconciler "knative.dev/eventing/pkg/client/injection/reconciler/sinks/v1alpha1/integrationsink"
@@ -52,12 +51,15 @@ func NewController(
deploymentInformer := deploymentinformer.Get(ctx)
serviceInformer := service.Get(ctx)
+ dynamicCertificateInformer := certificates.NewDynamicCertificatesInformer()
r := &Reconciler{
- kubeClientSet: kubeclient.Get(ctx),
- deploymentLister: deploymentInformer.Lister(),
- serviceLister: serviceInformer.Lister(),
- secretLister: secretInformer.Lister(),
- eventPolicyLister: eventPolicyInformer.Lister(),
+ secretLister: secretInformer.Lister(),
+ eventPolicyLister: eventPolicyInformer.Lister(),
+ kubeClientSet: kubeclient.Get(ctx),
+ deploymentLister: deploymentInformer.Lister(),
+ serviceLister: serviceInformer.Lister(),
+ cmCertificateLister: *dynamicCertificateInformer.Lister(),
+ certManagerClient: cmclient.NewForConfigOrDie(injection.GetConfig(ctx)),
}
logging.FromContext(ctx).Info("Creating IntegrationSink controller")
@@ -66,40 +68,15 @@ func NewController(
featureStore := feature.NewStore(logging.FromContext(ctx).Named("feature-config-store"), func(name string, value interface{}) {
if globalResync != nil {
- globalResync(nil)
- }
- if features, ok := value.(feature.Flags); ok {
- // we assume that Cert-Manager is installed in the cluster if the feature flag is enabled
- if features.IsPermissiveTransportEncryption() || features.IsStrictTransportEncryption() {
-
- go func() { // Start and register informer
- certManagerClient := certmanagerclient.NewForConfigOrDie(injection.GetConfig(ctx))
- factory := certmanagerinformers.NewSharedInformerFactoryWithOptions(
- certManagerClient,
- controller.DefaultResyncPeriod,
- certmanagerinformers.WithTweakListOptions(func(options *v1.ListOptions) {
- options.LabelSelector = "app.kubernetes.io/component=knative-eventing"
- }),
- )
- cmCertificateInformer := factory.Certmanager().V1().Certificates()
- r.cmCertificateLister = cmCertificateInformer.Lister()
- r.certManagerClient = certManagerClient
-
- cmCertificateInformer.Informer().AddEventHandler(controller.HandleAll(globalResync))
- factory.Start(ctx.Done())
-
- if !cache.WaitForCacheSync(ctx.Done(), cmCertificateInformer.Informer().HasSynced) {
- logging.FromContext(ctx).Error("Failed to sync Cert Manager Certificate informer")
- }
- }()
-
- } else {
- cancelFunc, cancelExists := ctx.Value("cancelFunc").(context.CancelFunc)
- if cancelExists {
- go cancelFunc()
+ if features, ok := value.(feature.Flags); ok {
+ // we assume that Cert-Manager is installed in the cluster if the feature flag is enabled
+ if err := dynamicCertificateInformer.Reconcile(ctx, features, controller.HandleAll(globalResync)); err != nil {
+ logging.FromContext(ctx).Errorw("Failed to start certificates dynamic factory", zap.Error(err))
}
}
+
+ globalResync(nil)
}
})
featureStore.WatchConfigs(cmw)
diff --git a/pkg/reconciler/integration/sink/integrationsink.go b/pkg/reconciler/integration/sink/integrationsink.go
index 0f5a338bc..8e7f77b44 100644
--- a/pkg/reconciler/integration/sink/integrationsink.go
+++ b/pkg/reconciler/integration/sink/integrationsink.go
@@ -19,8 +19,10 @@ package sink
import (
"context"
"fmt"
+ "sync/atomic"
"knative.dev/eventing/pkg/certificates"
+ certmanagerlisters "knative.dev/eventing/pkg/client/certmanager/listers/certmanager/v1"
cmv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
@@ -47,8 +49,6 @@ import (
certmanagerclientset "knative.dev/eventing/pkg/client/certmanager/clientset/versioned"
- certmanagerlisters "knative.dev/eventing/pkg/client/certmanager/listers/certmanager/v1"
-
"knative.dev/eventing/pkg/eventingtls"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/controller"
@@ -74,7 +74,7 @@ type Reconciler struct {
deploymentLister appsv1listers.DeploymentLister
serviceLister corev1listers.ServiceLister
- cmCertificateLister certmanagerlisters.CertificateLister
+ cmCertificateLister atomic.Pointer[certmanagerlisters.CertificateLister]
certManagerClient certmanagerclientset.Interface
}
@@ -174,7 +174,12 @@ func (r *Reconciler) reconcileCMCertificate(ctx context.Context, sink *sinks.Int
expected := certificates.MakeCertificate(sink, sink.Name)
- cert, err := r.cmCertificateLister.Certificates(sink.Namespace).Get(expected.Name)
+ lister := r.cmCertificateLister.Load()
+ if lister == nil {
+ return nil, fmt.Errorf("no cer-manager certificate lister created yet, this should never happen")
+ }
+
+ cert, err := (*lister).Certificates(sink.Namespace).Get(expected.Name)
if apierrors.IsNotFound(err) {
cert, err := r.certManagerClient.CertmanagerV1().Certificates(sink.Namespace).Create(ctx, expected, metav1.CreateOptions{})
if err != nil {Something like this I guess
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hitting this:
logging.FromContext(ctx).Debugw("Certificate informer has not been started, nothing to stop")
than later it is stuck here:
return nil, fmt.Errorf("no cer-manager certificate lister created yet, this should never happen")
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #8509 +/- ##
==========================================
- Coverage 64.26% 64.19% -0.08%
==========================================
Files 397 397
Lines 24337 24354 +17
==========================================
- Hits 15641 15634 -7
- Misses 7858 7880 +22
- Partials 838 840 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
We could merge my current and work on proper fix in separate PR... as an option. |
|
/lgtm |
* Re-enable Cert Mgr Signed-off-by: Matthias Wessendorf <[email protected]> * Manual informer Signed-off-by: Matthias Wessendorf <[email protected]> * Update main Signed-off-by: Matthias Wessendorf <[email protected]> * update controller Signed-off-by: Matthias Wessendorf <[email protected]> * Adding label on cert Signed-off-by: Matthias Wessendorf <[email protected]> * add enqueue Signed-off-by: Matthias Wessendorf <[email protected]> * Create Cert Informer, on demand, and cancle when flag is disabled Signed-off-by: Matthias Wessendorf <[email protected]> * Update unit tests that broke during rrefactors Signed-off-by: Matthias Wessendorf <[email protected]> --------- Signed-off-by: Matthias Wessendorf <[email protected]>
* Re-enable Cert Mgr Signed-off-by: Matthias Wessendorf <[email protected]> * Manual informer Signed-off-by: Matthias Wessendorf <[email protected]> * Update main Signed-off-by: Matthias Wessendorf <[email protected]> * update controller Signed-off-by: Matthias Wessendorf <[email protected]> * Adding label on cert Signed-off-by: Matthias Wessendorf <[email protected]> * add enqueue Signed-off-by: Matthias Wessendorf <[email protected]> * Create Cert Informer, on demand, and cancle when flag is disabled Signed-off-by: Matthias Wessendorf <[email protected]> * Update unit tests that broke during rrefactors Signed-off-by: Matthias Wessendorf <[email protected]> --------- Signed-off-by: Matthias Wessendorf <[email protected]>
* Partial implementation of certmanager for integration sink (knative#8481) * Adding cert-mananger bits Basics for Cert-manager Signed-off-by: Matthias Wessendorf <[email protected]> * Adding create cert manifest function Signed-off-by: Matthias Wessendorf <[email protected]> * Use the make cert Signed-off-by: Matthias Wessendorf <[email protected]> * Reconcile Certificate manifest and add RBAC Signed-off-by: Matthias Wessendorf <[email protected]> * Add secret filter to controller Signed-off-by: Matthias Wessendorf <[email protected]> * Look up secrets/certs for sink Signed-off-by: Matthias Wessendorf <[email protected]> * Remove old/wrong Certificate manifest and reference Signed-off-by: Matthias Wessendorf <[email protected]> * Fix controller compile Signed-off-by: Matthias Wessendorf <[email protected]> * Formatting Signed-off-by: Matthias Wessendorf <[email protected]> * change the reconcile of cert manifest, and run it only w/ some form of TLS Signed-off-by: Matthias Wessendorf <[email protected]> * using filtered informer Signed-off-by: Matthias Wessendorf <[email protected]> * Adding Rekt test for IntegrationSink TLS support Signed-off-by: Matthias Wessendorf <[email protected]> * adding a bit of https port for deployment/service of the IntegrationSink Signed-off-by: Matthias Wessendorf <[email protected]> * Mounting secrets and setting quarkus env vars for TLS support Signed-off-by: Matthias Wessendorf <[email protected]> * Adding flags for different TLS levels resulting in diffeerent quarkus env vars Signed-off-by: Matthias Wessendorf <[email protected]> * Remove bad comment Signed-off-by: Matthias Wessendorf <[email protected]> * Adding label Signed-off-by: Matthias Wessendorf <[email protected]> * Update codegen Signed-off-by: Matthias Wessendorf <[email protected]> * Disable tmp. the cert manager feature Signed-off-by: Matthias Wessendorf <[email protected]> * Fixing header/year Signed-off-by: Matthias Wessendorf <[email protected]> * More test tweaking Signed-off-by: Matthias Wessendorf <[email protected]> * Remove unused and comment out the hooks for now Signed-off-by: Matthias Wessendorf <[email protected]> --------- Signed-off-by: Matthias Wessendorf <[email protected]> * Run make generate-release Signed-off-by: Matthias Wessendorf <[email protected]> * Remove unused code (knative#8485) Signed-off-by: Matthias Wessendorf <[email protected]> * IntegrationSink prefactors (knative#8486) Little refactors on IntegrationSink Signed-off-by: Matthias Wessendorf <[email protected]> * Add initial iteration of eventing-integration resources (knative#8493) * Generic code for Certificates (knative#8489) Make Certifacte factory more generic Signed-off-by: Matthias Wessendorf <[email protected]> * Jsonata Event transform e2e tests (knative#8499) * E2E tests Signed-off-by: Pierangelo Di Pilato <[email protected]> * EventTransform Jsonata e2e tests Signed-off-by: Pierangelo Di Pilato <[email protected]> * Use eventing-integrations images and fix tests Signed-off-by: Pierangelo Di Pilato <[email protected]> * Verify response status code received by source Signed-off-by: Pierangelo Di Pilato <[email protected]> * Update JSONata transformations Signed-off-by: Pierangelo Di Pilato <[email protected]> --------- Signed-off-by: Pierangelo Di Pilato <[email protected]> * Inject integration images from ConfigMap (knative#8500) Signed-off-by: Pierangelo Di Pilato <[email protected]> * Run make generate-release Signed-off-by: Matthias Wessendorf <[email protected]> * Update to the correct file (knative#8507) Signed-off-by: Matthias Wessendorf <[email protected]> * EventTransform: Inject tracing configurations (knative#8479) Signed-off-by: Pierangelo Di Pilato <[email protected]> * EventTransform: add reconciler unit tests (knative#8513) * EventTransform: add reconciler unit tests Signed-off-by: Pierangelo Di Pilato <[email protected]> * Update pkg/reconciler/testing/v1alpha1/eventtransform.go Co-authored-by: Marek Schmidt <[email protected]> --------- Signed-off-by: Pierangelo Di Pilato <[email protected]> Co-authored-by: Marek Schmidt <[email protected]> * Enable Cert Manager (knative#8509) * Re-enable Cert Mgr Signed-off-by: Matthias Wessendorf <[email protected]> * Manual informer Signed-off-by: Matthias Wessendorf <[email protected]> * Update main Signed-off-by: Matthias Wessendorf <[email protected]> * update controller Signed-off-by: Matthias Wessendorf <[email protected]> * Adding label on cert Signed-off-by: Matthias Wessendorf <[email protected]> * add enqueue Signed-off-by: Matthias Wessendorf <[email protected]> * Create Cert Informer, on demand, and cancle when flag is disabled Signed-off-by: Matthias Wessendorf <[email protected]> * Update unit tests that broke during rrefactors Signed-off-by: Matthias Wessendorf <[email protected]> --------- Signed-off-by: Matthias Wessendorf <[email protected]> * Event Transform JSONata TLS (knative#8515) * Event Transform JSONata TLS Signed-off-by: Pierangelo Di Pilato <[email protected]> * HA and security context Signed-off-by: Pierangelo Di Pilato <[email protected]> * Reconcile Certificate Signed-off-by: Pierangelo Di Pilato <[email protected]> * Additional unit tests cases Signed-off-by: Pierangelo Di Pilato <[email protected]> * Fix tests Signed-off-by: Pierangelo Di Pilato <[email protected]> * EventTransform trust bundle propagation and tests Signed-off-by: Pierangelo Di Pilato <[email protected]> * Fix tests and address comments Signed-off-by: Pierangelo Di Pilato <[email protected]> --------- Signed-off-by: Pierangelo Di Pilato <[email protected]> * Run make generate-release Signed-off-by: Matthias Wessendorf <[email protected]> * Add dynamic cert-manager certificates informer (knative#8517) * Add dynamic informer, part I Signed-off-by: Matthias Wessendorf <[email protected]> * Add Dynamic certificates informer for EventTransform Signed-off-by: Pierangelo Di Pilato <[email protected]> * Add Dynamic certificates Informer to IntegrationSink Signed-off-by: Pierangelo Di Pilato <[email protected]> * Fix error message Signed-off-by: Pierangelo Di Pilato <[email protected]> --------- Signed-off-by: Matthias Wessendorf <[email protected]> Signed-off-by: Pierangelo Di Pilato <[email protected]> Co-authored-by: Pierangelo Di Pilato <[email protected]> * Improve CM Cert reconcile and deletion if feature is turned off (knative#8519) refactor CM Cert reconciler and take care of delete if feature is disabled Signed-off-by: Matthias Wessendorf <[email protected]> * Remove transformations and integrations ConfigMaps (#1201) We don't use the ConfigMaps to inject the values, the operator will replace the value to the images directly. Signed-off-by: Pierangelo Di Pilato <[email protected]> Co-authored-by: Pierangelo Di Pilato <[email protected]> * Run make generate-release Signed-off-by: Matthias Wessendorf <[email protected]> * Add configmaps/finalizers permissions Signed-off-by: Pierangelo Di Pilato <[email protected]> * Add update finalizers permissions for EventTransform reconciled resources Signed-off-by: Pierangelo Di Pilato <[email protected]> * Volume mounts use direct Signed-off-by: Matthias Wessendorf <[email protected]> * Fix EventTransform with TLS Sink test Signed-off-by: Matthias Wessendorf <[email protected]> --------- Signed-off-by: Matthias Wessendorf <[email protected]> Signed-off-by: Pierangelo Di Pilato <[email protected]> Co-authored-by: David Simansky <[email protected]> Co-authored-by: Pierangelo Di Pilato <[email protected]> Co-authored-by: Marek Schmidt <[email protected]>
Fixes #
Proposed Changes
Pre-review Checklist
Release Note
Docs