Compare commits
4 Commits
v0.1.3
...
ea3d71fa93
| Author | SHA1 | Date | |
|---|---|---|---|
| ea3d71fa93 | |||
| e0bd3973ed | |||
| 547d168c12 | |||
| 9bc4436c79 |
@@ -33,6 +33,13 @@ type ClusterServiceSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
||||||
|
|
||||||
|
// ExternalTrafficPolicy for a LoadBalancer/NodePort Service. Local preserves
|
||||||
|
// client source IPs (required for source-IP ACLs on the DNS servers) but
|
||||||
|
// only routes to nodes running a pod. Defaults to Cluster.
|
||||||
|
// +kubebuilder:validation:Enum=Cluster;Local
|
||||||
|
// +optional
|
||||||
|
ExternalTrafficPolicy corev1.ServiceExternalTrafficPolicy `json:"externalTrafficPolicy,omitempty"`
|
||||||
|
|
||||||
// Annotations added to the client-facing Service (e.g. PureLB/MetalLB hints).
|
// Annotations added to the client-facing Service (e.g. PureLB/MetalLB hints).
|
||||||
// +optional
|
// +optional
|
||||||
Annotations map[string]string `json:"annotations,omitempty"`
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
|||||||
@@ -1094,6 +1094,15 @@ spec:
|
|||||||
description: Annotations added to the client-facing Service (e.g.
|
description: Annotations added to the client-facing Service (e.g.
|
||||||
PureLB/MetalLB hints).
|
PureLB/MetalLB hints).
|
||||||
type: object
|
type: object
|
||||||
|
externalTrafficPolicy:
|
||||||
|
description: |-
|
||||||
|
ExternalTrafficPolicy for a LoadBalancer/NodePort Service. Local preserves
|
||||||
|
client source IPs (required for source-IP ACLs on the DNS servers) but
|
||||||
|
only routes to nodes running a pod. Defaults to Cluster.
|
||||||
|
enum:
|
||||||
|
- Cluster
|
||||||
|
- Local
|
||||||
|
type: string
|
||||||
loadBalancerIP:
|
loadBalancerIP:
|
||||||
description: LoadBalancerIP requests a specific address when Type
|
description: LoadBalancerIP requests a specific address when Type
|
||||||
is LoadBalancer.
|
is LoadBalancer.
|
||||||
|
|||||||
@@ -1399,6 +1399,15 @@ spec:
|
|||||||
description: Annotations added to the client-facing Service (e.g.
|
description: Annotations added to the client-facing Service (e.g.
|
||||||
PureLB/MetalLB hints).
|
PureLB/MetalLB hints).
|
||||||
type: object
|
type: object
|
||||||
|
externalTrafficPolicy:
|
||||||
|
description: |-
|
||||||
|
ExternalTrafficPolicy for a LoadBalancer/NodePort Service. Local preserves
|
||||||
|
client source IPs (required for source-IP ACLs on the DNS servers) but
|
||||||
|
only routes to nodes running a pod. Defaults to Cluster.
|
||||||
|
enum:
|
||||||
|
- Cluster
|
||||||
|
- Local
|
||||||
|
type: string
|
||||||
loadBalancerIP:
|
loadBalancerIP:
|
||||||
description: LoadBalancerIP requests a specific address when Type
|
description: LoadBalancerIP requests a specific address when Type
|
||||||
is LoadBalancer.
|
is LoadBalancer.
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ type RenderInput struct {
|
|||||||
Policies []bindv1alpha1.BindPolicy
|
Policies []bindv1alpha1.BindPolicy
|
||||||
DNSSECPolicies []bindv1alpha1.BindDNSSECPolicy
|
DNSSECPolicies []bindv1alpha1.BindDNSSECPolicy
|
||||||
Catalog *bindv1alpha1.BindCatalogZone
|
Catalog *bindv1alpha1.BindCatalogZone
|
||||||
|
// Forwards are type:forward BindZones. They are pure configuration (no
|
||||||
|
// replicated data), so they are rendered into named.conf on every pod
|
||||||
|
// rather than added dynamically to the primary.
|
||||||
|
Forwards []bindv1alpha1.BindZone
|
||||||
// PrimaryAddress is the in-cluster address secondaries transfer from.
|
// PrimaryAddress is the in-cluster address secondaries transfer from.
|
||||||
PrimaryAddress string
|
PrimaryAddress string
|
||||||
}
|
}
|
||||||
@@ -90,6 +94,29 @@ func render(in RenderInput, isPrimary bool) string {
|
|||||||
b.WriteString(renderCatalogZoneDecl(in, isPrimary, ""))
|
b.WriteString(renderCatalogZoneDecl(in, isPrimary, ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Top-level forward zones (BIND only allows top-level zones when no views
|
||||||
|
// are defined; in-view forward zones are rendered inside renderView).
|
||||||
|
if len(in.Views) == 0 {
|
||||||
|
for _, z := range in.Forwards {
|
||||||
|
if z.Spec.ViewRef == "" {
|
||||||
|
b.WriteString(renderForwardZone(z, ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderForwardZone renders a type:forward zone clause.
|
||||||
|
func renderForwardZone(z bindv1alpha1.BindZone, indent string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString(fmt.Sprintf("%szone \"%s\" {\n", indent, z.Spec.ZoneName))
|
||||||
|
b.WriteString(indent + " type forward;\n")
|
||||||
|
b.WriteString(indent + " forward only;\n")
|
||||||
|
if len(z.Spec.Forwarders) > 0 {
|
||||||
|
b.WriteString(fmt.Sprintf("%s forwarders { %s };\n", indent, terminate(z.Spec.Forwarders)))
|
||||||
|
}
|
||||||
|
b.WriteString(indent + "};\n")
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +149,12 @@ func renderView(v bindv1alpha1.BindView, in RenderInput, isPrimary bool) string
|
|||||||
if in.Catalog != nil {
|
if in.Catalog != nil {
|
||||||
b.WriteString(renderCatalogZoneDecl(in, isPrimary, " "))
|
b.WriteString(renderCatalogZoneDecl(in, isPrimary, " "))
|
||||||
}
|
}
|
||||||
|
// Forward zones bound to this view.
|
||||||
|
for _, z := range in.Forwards {
|
||||||
|
if z.Spec.ViewRef == v.Name {
|
||||||
|
b.WriteString(renderForwardZone(z, " "))
|
||||||
|
}
|
||||||
|
}
|
||||||
b.WriteString("};\n\n")
|
b.WriteString("};\n\n")
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,29 @@ func TestRenderCatalogUsesPrimaryIP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenderForwardZoneInView(t *testing.T) {
|
||||||
|
rec := true
|
||||||
|
in := RenderInput{
|
||||||
|
Cluster: newCluster(bindv1alpha1.ModeResolver),
|
||||||
|
Views: []bindv1alpha1.BindView{{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "openforwarder"},
|
||||||
|
Spec: bindv1alpha1.BindViewSpec{ClusterRef: "auth", MatchClients: []string{"acl-main"}, Recursion: &rec},
|
||||||
|
}},
|
||||||
|
Forwards: []bindv1alpha1.BindZone{{
|
||||||
|
Spec: bindv1alpha1.BindZoneSpec{ClusterRef: "auth", ZoneName: "unkin.net", Type: bindv1alpha1.ZoneForward, ViewRef: "openforwarder", Forwarders: []string{"198.18.19.15"}},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
primary, secondary := RenderNamedConf(in)
|
||||||
|
for _, out := range []string{primary, secondary} {
|
||||||
|
if !strings.Contains(out, `view "openforwarder"`) {
|
||||||
|
t.Fatalf("view missing:\n%s", out)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, `zone "unkin.net" {`) || !strings.Contains(out, "type forward;") || !strings.Contains(out, "forwarders { 198.18.19.15; }") {
|
||||||
|
t.Fatalf("forward zone not rendered inside view (must be on all pods):\n%s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRenderACL(t *testing.T) {
|
func TestRenderACL(t *testing.T) {
|
||||||
in := RenderInput{
|
in := RenderInput{
|
||||||
Cluster: newCluster(bindv1alpha1.ModeAuthoritative),
|
Cluster: newCluster(bindv1alpha1.ModeAuthoritative),
|
||||||
|
|||||||
@@ -206,6 +206,17 @@ func (r *BindClusterReconciler) reconcileConfigMap(ctx context.Context, c *bindv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forward zones are configuration (no data), so they are rendered into
|
||||||
|
// named.conf on every pod rather than added dynamically to the primary.
|
||||||
|
var zones bindv1alpha1.BindZoneList
|
||||||
|
if err := r.List(ctx, &zones, client.InNamespace(c.Namespace)); err == nil {
|
||||||
|
for _, z := range zones.Items {
|
||||||
|
if z.Spec.ClusterRef == c.Name && z.Spec.Type == bindv1alpha1.ZoneForward {
|
||||||
|
in.Forwards = append(in.Forwards, z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var catalogs bindv1alpha1.BindCatalogZoneList
|
var catalogs bindv1alpha1.BindCatalogZoneList
|
||||||
if err := r.List(ctx, &catalogs, client.InNamespace(c.Namespace)); err == nil {
|
if err := r.List(ctx, &catalogs, client.InNamespace(c.Namespace)); err == nil {
|
||||||
for i := range catalogs.Items {
|
for i := range catalogs.Items {
|
||||||
@@ -262,6 +273,10 @@ func (r *BindClusterReconciler) reconcileServices(ctx context.Context, c *bindv1
|
|||||||
LoadBalancerIP: c.Spec.Service.LoadBalancerIP,
|
LoadBalancerIP: c.Spec.Service.LoadBalancerIP,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// externalTrafficPolicy is only valid for LoadBalancer/NodePort Services.
|
||||||
|
if svcType == corev1.ServiceTypeLoadBalancer || svcType == corev1.ServiceTypeNodePort {
|
||||||
|
client.Spec.ExternalTrafficPolicy = c.Spec.Service.ExternalTrafficPolicy
|
||||||
|
}
|
||||||
return r.upsertService(ctx, c, client)
|
return r.upsertService(ctx, c, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,6 +431,15 @@ func (r *BindClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||||||
Watches(&bindv1alpha1.BindDNSSECPolicy{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
|
Watches(&bindv1alpha1.BindDNSSECPolicy{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
|
||||||
return mapToCluster(o.(*bindv1alpha1.BindDNSSECPolicy).Spec.ClusterRef, o.GetNamespace())
|
return mapToCluster(o.(*bindv1alpha1.BindDNSSECPolicy).Spec.ClusterRef, o.GetNamespace())
|
||||||
})).
|
})).
|
||||||
|
Watches(&bindv1alpha1.BindZone{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
|
||||||
|
// Only forward zones affect named.conf; primary/secondary zones are
|
||||||
|
// managed dynamically by the BindZone controller.
|
||||||
|
z := o.(*bindv1alpha1.BindZone)
|
||||||
|
if z.Spec.Type != bindv1alpha1.ZoneForward {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return mapToCluster(z.Spec.ClusterRef, o.GetNamespace())
|
||||||
|
})).
|
||||||
Watches(&bindv1alpha1.BindCatalogZone{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
|
Watches(&bindv1alpha1.BindCatalogZone{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
|
||||||
return mapToCluster(o.(*bindv1alpha1.BindCatalogZone).Spec.ClusterRef, o.GetNamespace())
|
return mapToCluster(o.(*bindv1alpha1.BindCatalogZone).Spec.ClusterRef, o.GetNamespace())
|
||||||
})).
|
})).
|
||||||
|
|||||||
@@ -35,6 +35,19 @@ func (r *BindZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
|
|||||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forward zones are pure configuration rendered into named.conf by the
|
||||||
|
// BindCluster controller (on every pod), not added dynamically to the
|
||||||
|
// primary. Nothing to do here beyond reporting readiness.
|
||||||
|
if zone.Spec.Type == bindv1alpha1.ZoneForward {
|
||||||
|
zone.Status.Phase = "Ready"
|
||||||
|
zone.Status.ObservedGeneration = zone.Generation
|
||||||
|
setReady(&zone.Status.Conditions, zone.Generation, true, "Configured", "forward zone rendered into named.conf")
|
||||||
|
if err := r.Status().Update(ctx, &zone); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
cluster, err := getCluster(ctx, r.Client, zone.Namespace, zone.Spec.ClusterRef)
|
cluster, err := getCluster(ctx, r.Client, zone.Namespace, zone.Spec.ClusterRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return r.setPhase(ctx, &zone, "Error", "ClusterMissing", err.Error())
|
return r.setPhase(ctx, &zone, "Error", "ClusterMissing", err.Error())
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func (r *BindClusterReconciler) upsertService(ctx context.Context, c *bindv1alph
|
|||||||
existing.Spec.Selector = desired.Spec.Selector
|
existing.Spec.Selector = desired.Spec.Selector
|
||||||
existing.Spec.Type = desired.Spec.Type
|
existing.Spec.Type = desired.Spec.Type
|
||||||
existing.Spec.LoadBalancerIP = desired.Spec.LoadBalancerIP
|
existing.Spec.LoadBalancerIP = desired.Spec.LoadBalancerIP
|
||||||
|
existing.Spec.ExternalTrafficPolicy = desired.Spec.ExternalTrafficPolicy
|
||||||
if desired.Annotations != nil {
|
if desired.Annotations != nil {
|
||||||
if existing.Annotations == nil {
|
if existing.Annotations == nil {
|
||||||
existing.Annotations = map[string]string{}
|
existing.Annotations = map[string]string{}
|
||||||
|
|||||||
Reference in New Issue
Block a user