-
Notifications
You must be signed in to change notification settings - Fork 33
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
Authorino CR reconcile moved to state of the world reconciler. #865
Changes from all commits
95f3a29
b8361be
169853f
f48e0c3
48b76d2
b4f4328
6a67fc7
9f0618d
1a820ed
6aa3c68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package controllers | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
"sync" | ||
|
||
v1beta2 "github.com/kuadrant/authorino-operator/api/v1beta1" | ||
"github.com/kuadrant/policy-machinery/controller" | ||
"github.com/kuadrant/policy-machinery/machinery" | ||
"github.com/samber/lo" | ||
"k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/dynamic" | ||
"k8s.io/utils/ptr" | ||
|
||
"github.com/kuadrant/kuadrant-operator/api/v1beta1" | ||
) | ||
|
||
type AuthorinoCrReconciler struct { | ||
Client *dynamic.DynamicClient | ||
} | ||
|
||
func NewAuthorinoCrReconciler(client *dynamic.DynamicClient) *AuthorinoCrReconciler { | ||
return &AuthorinoCrReconciler{Client: client} | ||
} | ||
|
||
func (r *AuthorinoCrReconciler) Subscription() *controller.Subscription { | ||
return &controller.Subscription{ | ||
ReconcileFunc: r.Reconcile, | ||
Events: []controller.ResourceEventMatcher{ | ||
{Kind: ptr.To(v1beta1.KuadrantKind), EventType: ptr.To(controller.CreateEvent)}, | ||
{Kind: ptr.To(v1beta1.AuthorinoKind), EventType: ptr.To(controller.DeleteEvent)}, | ||
}, | ||
} | ||
} | ||
|
||
func (r *AuthorinoCrReconciler) Reconcile(ctx context.Context, _ []controller.ResourceEvent, topology *machinery.Topology, _ error, _ *sync.Map) error { | ||
logger := controller.LoggerFromContext(ctx).WithName("AuthorinoCrReconciler") | ||
logger.Info("reconciling authorino resource", "status", "started") | ||
defer logger.Info("reconciling authorino resource", "status", "completed") | ||
|
||
kobjs := lo.FilterMap(topology.Objects().Roots(), func(item machinery.Object, _ int) (*v1beta1.Kuadrant, bool) { | ||
if item.GroupVersionKind().Kind == v1beta1.KuadrantKind.Kind { | ||
return item.(*v1beta1.Kuadrant), true | ||
} | ||
return nil, false | ||
}) | ||
|
||
kobj, err := GetOldestKuadrant(kobjs) | ||
if err != nil { | ||
if strings.Contains(err.Error(), "empty list passed") { | ||
logger.Info("kuadrant resource not found, ignoring", "status", "skipping") | ||
return err | ||
} | ||
logger.Error(err, "cannot find Kuadrant resource", "status", "error") | ||
return err | ||
} | ||
|
||
aobjs := lo.FilterMap(topology.Objects().Objects().Children(kobj), func(item machinery.Object, _ int) (machinery.Object, bool) { | ||
if item.GroupVersionKind().Kind == v1beta1.AuthorinoKind.Kind { | ||
return item, true | ||
} | ||
return nil, false | ||
}) | ||
|
||
if len(aobjs) > 0 { | ||
logger.Info("authorino resource already exists, no need to create", "status", "skipping") | ||
return nil | ||
} | ||
|
||
authorino := &v1beta2.Authorino{ | ||
TypeMeta: metav1.TypeMeta{ | ||
Kind: "Authorino", | ||
APIVersion: "operator.authorino.kuadrant.io/v1beta1", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "authorino", | ||
Namespace: kobj.Namespace, | ||
OwnerReferences: []metav1.OwnerReference{ | ||
{ | ||
APIVersion: kobj.GroupVersionKind().GroupVersion().String(), | ||
Kind: kobj.GroupVersionKind().Kind, | ||
Name: kobj.Name, | ||
UID: kobj.UID, | ||
BlockOwnerDeletion: ptr.To(true), | ||
Controller: ptr.To(true), | ||
}, | ||
}, | ||
}, | ||
Spec: v1beta2.AuthorinoSpec{ | ||
ClusterWide: true, | ||
SupersedingHostSubsets: true, | ||
Listener: v1beta2.Listener{ | ||
Tls: v1beta2.Tls{ | ||
Enabled: ptr.To(false), | ||
}, | ||
}, | ||
OIDCServer: v1beta2.OIDCServer{ | ||
Tls: v1beta2.Tls{ | ||
Enabled: ptr.To(false), | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
unstructuredAuthorino, err := controller.Destruct(authorino) | ||
if err != nil { | ||
logger.Error(err, "failed to destruct authorino", "status", "error") | ||
} | ||
logger.Info("creating authorino resource", "status", "processing") | ||
_, err = r.Client.Resource(v1beta1.AuthorinoResource).Namespace(authorino.Namespace).Create(ctx, unstructuredAuthorino, metav1.CreateOptions{}) | ||
if err != nil { | ||
if errors.IsAlreadyExists(err) { | ||
logger.Info("already created authorino resource", "status", "acceptable") | ||
} else { | ||
logger.Error(err, "failed to create authorino resource", "status", "error") | ||
} | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -10,13 +10,15 @@ import ( | |||||||
egv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1" | ||||||||
"github.com/go-logr/logr" | ||||||||
"github.com/google/go-cmp/cmp" | ||||||||
authorinov1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" | ||||||||
limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1" | ||||||||
"github.com/kuadrant/policy-machinery/controller" | ||||||||
"github.com/kuadrant/policy-machinery/machinery" | ||||||||
istioclientgoextensionv1alpha1 "istio.io/client-go/pkg/apis/extensions/v1alpha1" | ||||||||
istioclientnetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" | ||||||||
istioclientgosecurityv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1" | ||||||||
corev1 "k8s.io/api/core/v1" | ||||||||
"k8s.io/apimachinery/pkg/api/errors" | ||||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||||
"k8s.io/apimachinery/pkg/runtime/schema" | ||||||||
"k8s.io/client-go/dynamic" | ||||||||
|
@@ -35,7 +37,7 @@ import ( | |||||||
|
||||||||
var ( | ||||||||
ConfigMapGroupKind = schema.GroupKind{Group: corev1.GroupName, Kind: "ConfigMap"} | ||||||||
operatorNamespace = env.GetString("OPERATOR_NAMESPACE", "") | ||||||||
operatorNamespace = env.GetString("OPERATOR_NAMESPACE", "kuadrant-system") | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would make the panic at the following redundant, do we want to remove the panic now? kuadrant-operator/controllers/state_of_the_world.go Lines 152 to 154 in abc2e19
Or we can remove setting a default ns env value and set it across all the makefile integration test commands that expect this env value to be set if we want to keep the panic? |
||||||||
) | ||||||||
|
||||||||
//+kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gatewayclasses,verbs=list;watch | ||||||||
|
@@ -52,6 +54,7 @@ func NewPolicyMachineryController(manager ctrlruntime.Manager, client *dynamic.D | |||||||
controller.WithRunnable("ratelimitpolicy watcher", controller.Watch(&kuadrantv1beta2.RateLimitPolicy{}, kuadrantv1beta2.RateLimitPoliciesResource, metav1.NamespaceAll)), | ||||||||
controller.WithRunnable("topology configmap watcher", controller.Watch(&corev1.ConfigMap{}, controller.ConfigMapsResource, operatorNamespace, controller.FilterResourcesByLabel[*corev1.ConfigMap](fmt.Sprintf("%s=true", kuadrant.TopologyLabel)))), | ||||||||
controller.WithRunnable("limitador watcher", controller.Watch(&limitadorv1alpha1.Limitador{}, kuadrantv1beta1.LimitadorResource, metav1.NamespaceAll)), | ||||||||
controller.WithRunnable("authorino watcher", controller.Watch(&authorinov1beta1.Authorino{}, kuadrantv1beta1.AuthorinoResource, metav1.NamespaceAll)), | ||||||||
controller.WithPolicyKinds( | ||||||||
kuadrantv1alpha1.DNSPolicyKind, | ||||||||
kuadrantv1alpha1.TLSPolicyKind, | ||||||||
|
@@ -62,10 +65,12 @@ func NewPolicyMachineryController(manager ctrlruntime.Manager, client *dynamic.D | |||||||
kuadrantv1beta1.KuadrantKind, | ||||||||
ConfigMapGroupKind, | ||||||||
kuadrantv1beta1.LimitadorKind, | ||||||||
kuadrantv1beta1.AuthorinoKind, | ||||||||
), | ||||||||
controller.WithObjectLinks( | ||||||||
kuadrantv1beta1.LinkKuadrantToGatewayClasses, | ||||||||
kuadrantv1beta1.LinkKuadrantToLimitador, | ||||||||
kuadrantv1beta1.LinkKuadrantToAuthorino, | ||||||||
), | ||||||||
controller.WithReconcile(buildReconciler(client)), | ||||||||
} | ||||||||
|
@@ -140,9 +145,14 @@ func NewPolicyMachineryController(manager ctrlruntime.Manager, client *dynamic.D | |||||||
|
||||||||
func buildReconciler(client *dynamic.DynamicClient) controller.ReconcileFunc { | ||||||||
reconciler := &controller.Workflow{ | ||||||||
Precondition: NewEventLogger().Log, | ||||||||
Precondition: (&controller.Workflow{ | ||||||||
Precondition: NewEventLogger().Log, | ||||||||
Tasks: []controller.ReconcileFunc{ | ||||||||
NewTopologyFileReconciler(client, operatorNamespace).Reconcile, | ||||||||
}, | ||||||||
}).Run, | ||||||||
Tasks: []controller.ReconcileFunc{ | ||||||||
NewTopologyFileReconciler(client, operatorNamespace).Reconcile, | ||||||||
NewAuthorinoCrReconciler(client).Subscription().Reconcile, | ||||||||
}, | ||||||||
} | ||||||||
return reconciler.Run | ||||||||
|
@@ -184,8 +194,13 @@ func (r *TopologyFileReconciler) Reconcile(ctx context.Context, _ []controller.R | |||||||
}) | ||||||||
|
||||||||
if len(existingTopologyConfigMaps) == 0 { | ||||||||
_, err := r.Client.Resource(controller.ConfigMapsResource).Namespace(cm.Namespace).Create(ctx, unstructuredCM, metav1.CreateOptions{}) | ||||||||
_, err = r.Client.Resource(controller.ConfigMapsResource).Namespace(cm.Namespace).Create(ctx, unstructuredCM, metav1.CreateOptions{}) | ||||||||
if err != nil { | ||||||||
if errors.IsAlreadyExists(err) { | ||||||||
// This error can happen when the operator is starting, and the create event for the topology has not being processed. | ||||||||
logger.Info("already created topology configmap, must not be in topology yet") | ||||||||
return err | ||||||||
} | ||||||||
logger.Error(err, "failed to write topology configmap") | ||||||||
} | ||||||||
return err | ||||||||
|
@@ -198,7 +213,7 @@ func (r *TopologyFileReconciler) Reconcile(ctx context.Context, _ []controller.R | |||||||
cmTopology := existingTopologyConfigMap.Object.(*corev1.ConfigMap) | ||||||||
|
||||||||
if d, found := cmTopology.Data["topology"]; !found || strings.Compare(d, cm.Data["topology"]) != 0 { | ||||||||
_, err := r.Client.Resource(controller.ConfigMapsResource).Namespace(cm.Namespace).Update(ctx, unstructuredCM, metav1.UpdateOptions{}) | ||||||||
_, err = r.Client.Resource(controller.ConfigMapsResource).Namespace(cm.Namespace).Update(ctx, unstructuredCM, metav1.UpdateOptions{}) | ||||||||
if err != nil { | ||||||||
logger.Error(err, "failed to update topology configmap") | ||||||||
} | ||||||||
|
@@ -239,3 +254,30 @@ func (e *EventLogger) Log(ctx context.Context, resourceEvents []controller.Resou | |||||||
|
||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
// GetOldestKuadrant returns the oldest kuadrant resource from a list of kuadrant resources that is not marked for deletion. | ||||||||
func GetOldestKuadrant(kuadrants []*kuadrantv1beta1.Kuadrant) (*kuadrantv1beta1.Kuadrant, error) { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could probably pass the topology here. This is a pattern that likely will repeat a lot across tasks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good idea, but I am not going to do it in this PR. There was a topic I wish to discuss about unit testing where the input is a topology. I have a place holder issue, Kuadrant/policy-machinery#33. But for now I would like to leave this as is with its unit tests until there is a good way to generate the a topology for a unit test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we please add a TODO to refactor this ASAP. I do not want to risk people passing different lists of Kuadrant objects to this function. I want to make sure it is going to be always the same Kuadrant object that will be returned to all tasks that depend on it to navigate the topology from the common root. |
||||||||
if len(kuadrants) == 1 { | ||||||||
return kuadrants[0], nil | ||||||||
} | ||||||||
if len(kuadrants) == 0 { | ||||||||
return nil, fmt.Errorf("empty list passed") | ||||||||
} | ||||||||
oldest := kuadrants[0] | ||||||||
for _, k := range kuadrants[1:] { | ||||||||
if k == nil || k.DeletionTimestamp != nil { | ||||||||
continue | ||||||||
} | ||||||||
if oldest == nil { | ||||||||
oldest = k | ||||||||
continue | ||||||||
} | ||||||||
if k.CreationTimestamp.Before(&oldest.CreationTimestamp) { | ||||||||
oldest = k | ||||||||
} | ||||||||
} | ||||||||
if oldest == nil { | ||||||||
return nil, fmt.Errorf("only nil pointers in list") | ||||||||
} | ||||||||
return oldest, nil | ||||||||
} |
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.
nit: redundant consecutive
.Objects()
call? 🤔