Skip to content

Commit 5a9f986

Browse files
Merge pull request #386 from beagles/discover-join
Improve designate-unbound configuration
2 parents 933871a + 04b3a2e commit 5a9f986

19 files changed

+993
-281
lines changed

api/v1beta1/designateunbound_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ type UnboundOverrideSpec struct {
7070
}
7171

7272
type StubZone struct {
73-
Name string `json:"name"`
73+
Name string `json:"name"`
7474
// +kubebuilder:validation:Optional
7575
Options map[string]string `json:"options,omitempty"`
7676
}

config/rbac/role.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ rules:
152152
verbs:
153153
- patch
154154
- update
155+
- apiGroups:
156+
- operator.openshift.io
157+
resources:
158+
- networks
159+
verbs:
160+
- get
161+
- list
162+
- watch
155163
- apiGroups:
156164
- rabbitmq.openstack.org
157165
resources:

controllers/designate_controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ func (r *DesignateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
253253
condition.UnknownCondition(designatev1beta1.DesignateCentralReadyCondition, condition.InitReason, designatev1beta1.DesignateCentralReadyInitMessage),
254254
condition.UnknownCondition(designatev1beta1.DesignateWorkerReadyCondition, condition.InitReason, designatev1beta1.DesignateWorkerReadyInitMessage),
255255
condition.UnknownCondition(designatev1beta1.DesignateMdnsReadyCondition, condition.InitReason, designatev1beta1.DesignateMdnsReadyInitMessage),
256+
condition.UnknownCondition(designatev1beta1.DesignateUnboundReadyCondition, condition.InitReason, designatev1beta1.DesignateUnboundReadyInitMessage),
256257
condition.UnknownCondition(designatev1beta1.DesignateProducerReadyCondition, condition.InitReason, designatev1beta1.DesignateProducerReadyInitMessage),
257258
condition.UnknownCondition(designatev1beta1.DesignateBackendbind9ReadyCondition, condition.InitReason, designatev1beta1.DesignateBackendbind9ReadyInitMessage),
258259
// service account, role, rolebinding conditions
@@ -339,6 +340,8 @@ func (r *DesignateReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Man
339340
return nil
340341
}
341342

343+
// TODO(beagles):
344+
// - Watch for changes to the redis PODs and resync the headless hostnames for the PODs if necessary.
342345
return ctrl.NewControllerManagedBy(mgr).
343346
For(&designatev1beta1.Designate{}).
344347
Owns(&mariadbv1.MariaDBDatabase{}).

controllers/designateunbound_controller.go

Lines changed: 122 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
"github.com/go-logr/logr"
2626
networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
27+
operatorv1 "github.com/openshift/api/operator/v1"
2728
designatev1 "github.com/openstack-k8s-operators/designate-operator/api/v1beta1"
2829
"github.com/openstack-k8s-operators/designate-operator/pkg/designate"
2930
"github.com/openstack-k8s-operators/designate-operator/pkg/designateunbound"
@@ -74,11 +75,24 @@ type StubZoneTmplRec struct {
7475
Servers []string
7576
}
7677

78+
func getCIDRsFromNADs(nadList []networkv1.NetworkAttachmentDefinition) ([]string, error) {
79+
cidrs := []string{}
80+
for _, nad := range nadList {
81+
nadConfig, err := designate.GetNADConfig(&nad)
82+
if err != nil {
83+
return nil, err
84+
}
85+
cidrs = append(cidrs, nadConfig.IPAM.CIDR.String())
86+
}
87+
return cidrs, nil
88+
}
89+
7790
//+kubebuilder:rbac:groups=designate.openstack.org,resources=designateunbounds,verbs=get;list;watch;create;update;patch;delete
7891
//+kubebuilder:rbac:groups=designate.openstack.org,resources=designateunbounds/status,verbs=get;update;patch
7992
//+kubebuilder:rbac:groups=designate.openstack.org,resources=designateunbounds/finalizers,verbs=update;patch
8093
//+kubebuilder:rbac:groups=k8s.cni.cncf.io,resources=network-attachment-definitions,verbs=get;list;watch
8194
//+kubebuilder:rbac:groups=topology.openstack.org,resources=topologies,verbs=get;list;watch;update
95+
//+kubebuilder:rbac:groups=operator.openshift.io,resources=networks,verbs=get;list;watch
8296

8397
// Reconcile implementation for designate's Unbound resolver
8498
func (r *UnboundReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, _err error) {
@@ -315,33 +329,8 @@ func (r *UnboundReconciler) reconcileNormal(ctx context.Context, instance *desig
315329
}
316330
instance.Status.Conditions.MarkTrue(condition.CreateServiceReadyCondition, condition.CreateServiceReadyMessage)
317331

318-
configMapVars := make(map[string]env.Setter)
319-
err := r.generateServiceConfigMaps(ctx, instance, helper, &configMapVars)
320-
if err != nil {
321-
instance.Status.Conditions.Set(condition.FalseCondition(
322-
condition.ServiceConfigReadyCondition,
323-
condition.ErrorReason,
324-
condition.SeverityWarning,
325-
condition.ServiceConfigReadyErrorMessage,
326-
err.Error()))
327-
return ctrl.Result{}, err
328-
}
329-
330-
//
331-
// create hash over all the different input resources to identify if any those changed
332-
// and a restart/recreate is required.
333-
//
334-
inputHash, hashChanged, err := r.createHashOfInputHashes(ctx, instance, configMapVars)
335-
if err != nil {
336-
return ctrl.Result{}, err
337-
} else if hashChanged {
338-
// Hash changed and instance status should be updated (which will be done by main defer func),
339-
// so we need to return and reconcile again
340-
return ctrl.Result{}, nil
341-
}
342-
343-
instance.Status.Conditions.MarkTrue(condition.ServiceConfigReadyCondition, condition.ServiceConfigReadyMessage)
344-
332+
// We do initial processing of the network attachments before the configs because it influences some of the
333+
// automatically configurations.
345334
nadList := []networkv1.NetworkAttachmentDefinition{}
346335
for _, networkAttachment := range instance.Spec.NetworkAttachments {
347336
nad, err := nad.GetNADWithName(ctx, helper, networkAttachment, instance.Namespace)
@@ -372,6 +361,33 @@ func (r *UnboundReconciler) reconcileNormal(ctx context.Context, instance *desig
372361
}
373362
}
374363

364+
configMapVars := make(map[string]env.Setter)
365+
err := r.generateServiceConfigMaps(ctx, instance, helper, &configMapVars, nadList)
366+
if err != nil {
367+
instance.Status.Conditions.Set(condition.FalseCondition(
368+
condition.ServiceConfigReadyCondition,
369+
condition.ErrorReason,
370+
condition.SeverityWarning,
371+
condition.ServiceConfigReadyErrorMessage,
372+
err.Error()))
373+
return ctrl.Result{}, err
374+
}
375+
376+
//
377+
// create hash over all the different input resources to identify if any those changed
378+
// and a restart/recreate is required.
379+
//
380+
inputHash, hashChanged, err := r.createHashOfInputHashes(ctx, instance, configMapVars)
381+
if err != nil {
382+
return ctrl.Result{}, err
383+
} else if hashChanged {
384+
// Hash changed and instance status should be updated (which will be done by main defer func),
385+
// so we need to return and reconcile again
386+
return ctrl.Result{}, nil
387+
}
388+
389+
instance.Status.Conditions.MarkTrue(condition.ServiceConfigReadyCondition, condition.ServiceConfigReadyMessage)
390+
375391
serviceAnnotations, err := nad.EnsureNetworksAnnotation(nadList)
376392
if err != nil {
377393
return ctrl.Result{}, fmt.Errorf("failed create network annotation from %s: %w",
@@ -518,6 +534,7 @@ func (r *UnboundReconciler) onIPChange() (ctrl.Result, error) {
518534
return ctrl.Result{}, nil
519535
}
520536

537+
// Set stub zone configuration defaults if they are net set by CR data.
521538
func stubZoneDefaults(values map[string]string) map[string]string {
522539

523540
if values == nil {
@@ -532,20 +549,84 @@ func stubZoneDefaults(values map[string]string) map[string]string {
532549
return values
533550
}
534551

552+
// getClusterJoinSubnets will return configured join subnets from the cluster network if available. If noit will
553+
// return the expected defaults defined in the build.
554+
// TODO(beagles): it might be a good idea to have this settable through an environment variable to bridge
555+
// unexpected compatibility issues.
556+
func (r *UnboundReconciler) getClusterJoinSubnets(ctx context.Context) []string {
557+
Log := r.GetLogger(ctx)
558+
559+
// Get the cluster network operator configuration
560+
network := &operatorv1.Network{}
561+
err := r.Get(ctx, types.NamespacedName{Name: "cluster"}, network)
562+
if err != nil {
563+
// If we can't get the network config, fall back to defaults.
564+
// TODO(beagles): while probably not necessary, we should revisit to see if this should be an error.
565+
Log.Info("Unable to get cluster network configuration, using defaults", "error", err)
566+
return []string{designateunbound.DefaultJoinSubnetV4, designateunbound.DefaultJoinSubnetV6}
567+
}
568+
569+
var joinSubnets []string
570+
571+
// NOTE(beagles): we are only handling OVN-Kubernetes configuration at this time. There is a
572+
// question of whether we should only set defaults for IP versions that are 'enabled'.
573+
// Unfortunately, I'm not sure we can know that for sure from the network operator configuration.
574+
// That is, absence of configuration might mean that it is enabled, but with all defaults.
575+
576+
// Extract join subnets from OVN-Kubernetes configuration
577+
if network.Spec.DefaultNetwork.OVNKubernetesConfig != nil {
578+
ovnConfig := network.Spec.DefaultNetwork.OVNKubernetesConfig
579+
580+
// IPv4 join subnet
581+
if ovnConfig.IPv4 != nil && ovnConfig.IPv4.InternalJoinSubnet != "" {
582+
joinSubnets = append(joinSubnets, ovnConfig.IPv4.InternalJoinSubnet)
583+
Log.Info("Found IPv4 join subnet from cluster config", "subnet", ovnConfig.IPv4.InternalJoinSubnet)
584+
} else {
585+
joinSubnets = append(joinSubnets, designateunbound.DefaultJoinSubnetV4)
586+
Log.Info("Using default IPv4 join subnet", "subnet", designateunbound.DefaultJoinSubnetV4)
587+
}
588+
589+
// IPv6 join subnet
590+
if ovnConfig.IPv6 != nil && ovnConfig.IPv6.InternalJoinSubnet != "" {
591+
joinSubnets = append(joinSubnets, ovnConfig.IPv6.InternalJoinSubnet)
592+
Log.Info("Found IPv6 join subnet from cluster config", "subnet", ovnConfig.IPv6.InternalJoinSubnet)
593+
} else {
594+
joinSubnets = append(joinSubnets, designateunbound.DefaultJoinSubnetV6)
595+
Log.Info("Using default IPv6 join subnet", "subnet", designateunbound.DefaultJoinSubnetV6)
596+
}
597+
} else {
598+
// No OVN-Kubernetes config found, use defaults
599+
Log.Info("No OVN-Kubernetes configuration found, using defaults")
600+
joinSubnets = []string{designateunbound.DefaultJoinSubnetV4, designateunbound.DefaultJoinSubnetV6}
601+
}
602+
603+
return joinSubnets
604+
}
605+
535606
func (r *UnboundReconciler) generateServiceConfigMaps(
536607
ctx context.Context,
537608
instance *designatev1.DesignateUnbound,
538609
h *helper.Helper,
539610
envVars *map[string]env.Setter,
611+
nadList []networkv1.NetworkAttachmentDefinition,
540612
) error {
541613
Log := r.GetLogger(ctx)
542614
Log.Info("Generating service config map")
543615
cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(designateunbound.Component), map[string]string{})
544616
customData := map[string]string{common.CustomServiceConfigFileName: instance.Spec.CustomServiceConfig}
617+
// NOTE(beagles): DefaultConfigOverwrite is bugged in unbound because it is
618+
// applied to conf.d when it really is meant to for overwriting stuff in
619+
// /etc/unbound. A reasonable fix would be to have special handling for the
620+
// "unbound.conf" and if any of the other files exist in /etc/unbound,
621+
// overwrite them. The ones that aren't can go into /etc/unbound/conf.d for
622+
// backwards compatibility.
545623
maps.Copy(customData, instance.Spec.DefaultConfigOverwrite)
546624

547625
templateParameters := make(map[string]any)
548626

627+
//
628+
// Create stub zone configuration data from predictable IP map.
629+
//
549630
stubZoneData := make([]StubZoneTmplRec, len(instance.Spec.StubZones))
550631
if len(instance.Spec.StubZones) > 0 {
551632
bindIPMap := &corev1.ConfigMap{}
@@ -572,6 +653,19 @@ func (r *UnboundReconciler) generateServiceConfigMaps(
572653
}
573654
templateParameters["StubZones"] = stubZoneData
574655

656+
// TODO(beagles): There are situations where the allowCidrs should be overriddable, do we want to support that at the API level or
657+
// customServiceConfig
658+
// Dynamically discover join subnets from the cluster network operator configuration
659+
allowCidrs := r.getClusterJoinSubnets(ctx)
660+
661+
// TODO(beagles): create entries for each network attachment.
662+
nadCIDRs, nadErr := getCIDRsFromNADs(nadList)
663+
if nadErr != nil {
664+
Log.Error(nadErr, "unable to get CIDRs from network attachment definitions")
665+
}
666+
allowCidrs = append(allowCidrs, nadCIDRs...)
667+
templateParameters["AllowCidrs"] = allowCidrs
668+
575669
cms := []util.Template{
576670
// ConfigMap
577671
{
@@ -587,7 +681,7 @@ func (r *UnboundReconciler) generateServiceConfigMaps(
587681
err := secret.EnsureSecrets(ctx, h, instance, cms, envVars)
588682

589683
if err != nil {
590-
Log.Error(err, "uanble to process config map")
684+
Log.Error(err, "unable to process config map")
591685
return err
592686
}
593687
Log.Info("Service config map generated")

0 commit comments

Comments
 (0)