Merge pull request 'Add a primary (write) Service routing to pod-0' (#7) from benvin/primary-write-service into main
ci/woodpecker/tag/docker Pipeline was successful
ci/woodpecker/tag/docker Pipeline was successful
Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
@@ -105,10 +105,18 @@ type BindClusterSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
||||||
|
|
||||||
// Service controls how the cluster is exposed.
|
// Service controls how the cluster is exposed for reads (all pods).
|
||||||
// +optional
|
// +optional
|
||||||
Service ClusterServiceSpec `json:"service,omitempty"`
|
Service ClusterServiceSpec `json:"service,omitempty"`
|
||||||
|
|
||||||
|
// PrimaryService, when set, creates an additional Service routing only to the
|
||||||
|
// primary pod (ordinal 0) — the write endpoint for RFC2136/nsupdate, since
|
||||||
|
// secondaries reject updates. Reads still use Service (all pods). Use
|
||||||
|
// ClusterIP for in-cluster writers (e.g. external-dns) or LoadBalancer for
|
||||||
|
// external writers.
|
||||||
|
// +optional
|
||||||
|
PrimaryService *ClusterServiceSpec `json:"primaryService,omitempty"`
|
||||||
|
|
||||||
// NodeSelector for the BIND pods.
|
// NodeSelector for the BIND pods.
|
||||||
// +optional
|
// +optional
|
||||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||||
|
|||||||
@@ -301,6 +301,11 @@ func (in *BindClusterSpec) DeepCopyInto(out *BindClusterSpec) {
|
|||||||
}
|
}
|
||||||
in.Resources.DeepCopyInto(&out.Resources)
|
in.Resources.DeepCopyInto(&out.Resources)
|
||||||
in.Service.DeepCopyInto(&out.Service)
|
in.Service.DeepCopyInto(&out.Service)
|
||||||
|
if in.PrimaryService != nil {
|
||||||
|
in, out := &in.PrimaryService, &out.PrimaryService
|
||||||
|
*out = new(ClusterServiceSpec)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
if in.NodeSelector != nil {
|
if in.NodeSelector != nil {
|
||||||
in, out := &in.NodeSelector, &out.NodeSelector
|
in, out := &in.NodeSelector, &out.NodeSelector
|
||||||
*out = make(map[string]string, len(*in))
|
*out = make(map[string]string, len(*in))
|
||||||
|
|||||||
@@ -1013,6 +1013,41 @@ spec:
|
|||||||
type: string
|
type: string
|
||||||
description: NodeSelector for the BIND pods.
|
description: NodeSelector for the BIND pods.
|
||||||
type: object
|
type: object
|
||||||
|
primaryService:
|
||||||
|
description: |-
|
||||||
|
PrimaryService, when set, creates an additional Service routing only to the
|
||||||
|
primary pod (ordinal 0) — the write endpoint for RFC2136/nsupdate, since
|
||||||
|
secondaries reject updates. Reads still use Service (all pods). Use
|
||||||
|
ClusterIP for in-cluster writers (e.g. external-dns) or LoadBalancer for
|
||||||
|
external writers.
|
||||||
|
properties:
|
||||||
|
annotations:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: Annotations added to the client-facing Service (e.g.
|
||||||
|
PureLB/MetalLB hints).
|
||||||
|
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:
|
||||||
|
description: LoadBalancerIP requests a specific address when Type
|
||||||
|
is LoadBalancer.
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: Type of the client-facing Service. Defaults to ClusterIP.
|
||||||
|
enum:
|
||||||
|
- ClusterIP
|
||||||
|
- LoadBalancer
|
||||||
|
- NodePort
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
recursion:
|
recursion:
|
||||||
description: |-
|
description: |-
|
||||||
Recursion overrides the default per-mode recursion setting. When nil,
|
Recursion overrides the default per-mode recursion setting. When nil,
|
||||||
@@ -1086,7 +1121,8 @@ spec:
|
|||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
service:
|
service:
|
||||||
description: Service controls how the cluster is exposed.
|
description: Service controls how the cluster is exposed for reads
|
||||||
|
(all pods).
|
||||||
properties:
|
properties:
|
||||||
annotations:
|
annotations:
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
|
|||||||
+37
-1
@@ -1318,6 +1318,41 @@ spec:
|
|||||||
type: string
|
type: string
|
||||||
description: NodeSelector for the BIND pods.
|
description: NodeSelector for the BIND pods.
|
||||||
type: object
|
type: object
|
||||||
|
primaryService:
|
||||||
|
description: |-
|
||||||
|
PrimaryService, when set, creates an additional Service routing only to the
|
||||||
|
primary pod (ordinal 0) — the write endpoint for RFC2136/nsupdate, since
|
||||||
|
secondaries reject updates. Reads still use Service (all pods). Use
|
||||||
|
ClusterIP for in-cluster writers (e.g. external-dns) or LoadBalancer for
|
||||||
|
external writers.
|
||||||
|
properties:
|
||||||
|
annotations:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: Annotations added to the client-facing Service (e.g.
|
||||||
|
PureLB/MetalLB hints).
|
||||||
|
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:
|
||||||
|
description: LoadBalancerIP requests a specific address when Type
|
||||||
|
is LoadBalancer.
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: Type of the client-facing Service. Defaults to ClusterIP.
|
||||||
|
enum:
|
||||||
|
- ClusterIP
|
||||||
|
- LoadBalancer
|
||||||
|
- NodePort
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
recursion:
|
recursion:
|
||||||
description: |-
|
description: |-
|
||||||
Recursion overrides the default per-mode recursion setting. When nil,
|
Recursion overrides the default per-mode recursion setting. When nil,
|
||||||
@@ -1391,7 +1426,8 @@ spec:
|
|||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
service:
|
service:
|
||||||
description: Service controls how the cluster is exposed.
|
description: Service controls how the cluster is exposed for reads
|
||||||
|
(all pods).
|
||||||
properties:
|
properties:
|
||||||
annotations:
|
annotations:
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
|
|||||||
@@ -277,7 +277,52 @@ func (r *BindClusterReconciler) reconcileServices(ctx context.Context, c *bindv1
|
|||||||
if svcType == corev1.ServiceTypeLoadBalancer || svcType == corev1.ServiceTypeNodePort {
|
if svcType == corev1.ServiceTypeLoadBalancer || svcType == corev1.ServiceTypeNodePort {
|
||||||
client.Spec.ExternalTrafficPolicy = c.Spec.Service.ExternalTrafficPolicy
|
client.Spec.ExternalTrafficPolicy = c.Spec.Service.ExternalTrafficPolicy
|
||||||
}
|
}
|
||||||
return r.upsertService(ctx, c, client)
|
if err := r.upsertService(ctx, c, client); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Primary (write) Service: routes only to pod-0. Created when configured,
|
||||||
|
// deleted when removed.
|
||||||
|
return r.reconcilePrimaryService(ctx, c, dnsPorts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BindClusterReconciler) reconcilePrimaryService(ctx context.Context, c *bindv1alpha1.BindCluster, dnsPorts []corev1.ServicePort) error {
|
||||||
|
name := primaryServiceName(c.Name)
|
||||||
|
if c.Spec.PrimaryService == nil {
|
||||||
|
var existing corev1.Service
|
||||||
|
err := r.Get(ctx, types.NamespacedName{Namespace: c.Namespace, Name: name}, &existing)
|
||||||
|
if apierrors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return client.IgnoreNotFound(r.Delete(ctx, &existing))
|
||||||
|
}
|
||||||
|
|
||||||
|
ps := c.Spec.PrimaryService
|
||||||
|
psType := ps.Type
|
||||||
|
if psType == "" {
|
||||||
|
psType = corev1.ServiceTypeClusterIP
|
||||||
|
}
|
||||||
|
svc := &corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: c.Namespace,
|
||||||
|
Labels: commonLabels(c.Name),
|
||||||
|
Annotations: ps.Annotations,
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
Type: psType,
|
||||||
|
Selector: primaryPodSelector(c.Name),
|
||||||
|
Ports: dnsPorts,
|
||||||
|
LoadBalancerIP: ps.LoadBalancerIP,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if psType == corev1.ServiceTypeLoadBalancer || psType == corev1.ServiceTypeNodePort {
|
||||||
|
svc.Spec.ExternalTrafficPolicy = ps.ExternalTrafficPolicy
|
||||||
|
}
|
||||||
|
return r.upsertService(ctx, c, svc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *BindClusterReconciler) reconcileStatefulSet(ctx context.Context, c *bindv1alpha1.BindCluster) (*appsv1.StatefulSet, error) {
|
func (r *BindClusterReconciler) reconcileStatefulSet(ctx context.Context, c *bindv1alpha1.BindCluster) (*appsv1.StatefulSet, error) {
|
||||||
|
|||||||
@@ -29,11 +29,20 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func headlessServiceName(cluster string) string { return cluster + "-headless" }
|
func headlessServiceName(cluster string) string { return cluster + "-headless" }
|
||||||
func clientServiceName(cluster string) string { return cluster }
|
func primaryServiceName(cluster string) string { return cluster + "-primary" }
|
||||||
func primaryPodName(cluster string) string { return cluster + "-0" }
|
|
||||||
func configMapName(cluster string) string { return cluster + "-config" }
|
// primaryPodSelector selects only the primary pod (ordinal 0) via the stable
|
||||||
func keysSecretName(cluster string) string { return cluster + "-keys" }
|
// StatefulSet pod-name label, for the write Service.
|
||||||
func rndcSecretName(cluster string) string { return cluster + "-rndc" }
|
func primaryPodSelector(cluster string) map[string]string {
|
||||||
|
s := commonLabels(cluster)
|
||||||
|
s["statefulset.kubernetes.io/pod-name"] = primaryPodName(cluster)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func clientServiceName(cluster string) string { return cluster }
|
||||||
|
func primaryPodName(cluster string) string { return cluster + "-0" }
|
||||||
|
func configMapName(cluster string) string { return cluster + "-config" }
|
||||||
|
func keysSecretName(cluster string) string { return cluster + "-keys" }
|
||||||
|
func rndcSecretName(cluster string) string { return cluster + "-rndc" }
|
||||||
|
|
||||||
// primaryAddress is the in-cluster DNS name of the primary pod (ordinal 0).
|
// primaryAddress is the in-cluster DNS name of the primary pod (ordinal 0).
|
||||||
func primaryAddress(cluster, namespace string) string {
|
func primaryAddress(cluster, namespace string) string {
|
||||||
|
|||||||
Reference in New Issue
Block a user