Skip to content

Commit 0439357

Browse files
authored
Better support for dual-stack Kubernetes clusters (#671)
1 parent b93bdea commit 0439357

18 files changed

+651
-2
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,11 @@ kind: ## Run a default KinD cluster
16501650
kind create cluster --name $(KIND_CLUSTER) --wait 10m --config $(SCRIPTS_DIR)/kind-config.yaml --image $(KIND_IMAGE)
16511651
$(SCRIPTS_DIR)/kind-label-node.sh
16521652

1653+
.PHONY: kind-dual
1654+
kind-dual: ## Run a KinD cluster configured for a dual stack IPv4 and IPv6 network
1655+
kind create cluster --name $(KIND_CLUSTER) --wait 10m --config $(SCRIPTS_DIR)/kind-config-dual.yaml --image $(KIND_IMAGE)
1656+
$(SCRIPTS_DIR)/kind-label-node.sh
1657+
16531658
# ----------------------------------------------------------------------------------------------------------------------
16541659
# Start a Kind cluster
16551660
# ----------------------------------------------------------------------------------------------------------------------

api/v1/coherence_types.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,14 +562,22 @@ func (in *CoherenceSpec) GetManagementPort() int32 {
562562
}
563563
}
564564

565-
// GetPersistenceSpec returns the Coherence persistence spcification.
565+
// GetPersistenceSpec returns the Coherence persistence specification.
566566
func (in *CoherenceSpec) GetPersistenceSpec() *PersistenceSpec {
567567
if in == nil {
568568
return nil
569569
}
570570
return in.Persistence
571571
}
572572

573+
// GetWkaIPFamily returns the IP Family of the headless Service used for Coherence WKA.
574+
func (in *CoherenceSpec) GetWkaIPFamily() corev1.IPFamily {
575+
if in == nil || in.WKA == nil || in.WKA.IPFamily == nil {
576+
return corev1.IPFamilyUnknown
577+
}
578+
return *in.WKA.IPFamily
579+
}
580+
573581
// ----- CoherenceWKASpec struct --------------------------------------------
574582

575583
// CoherenceWKASpec configures Coherence well-known-addressing to use an
@@ -599,6 +607,11 @@ type CoherenceWKASpec struct {
599607
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
600608
// +optional
601609
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
610+
611+
// IPFamily is the IP family to use for the WKA service (and also the StatefulSet headless service).
612+
// Valid values are "IPv4" or "IPv6".
613+
// +optional
614+
IPFamily *corev1.IPFamily `json:"ipFamily,omitempty"`
602615
}
603616

604617
// ----- CoherenceTracingSpec struct ----------------------------------------

api/v1/coherencejobresource_types.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,19 @@ func (in *CoherenceJob) IsBeforeVersion(version string) bool {
334334
return true
335335
}
336336

337+
// GetWkaIPFamily returns the IP Family of the headless Service used for Coherence WKA.
338+
func (in *CoherenceJob) GetWkaIPFamily() corev1.IPFamily {
339+
if in == nil {
340+
return corev1.IPFamilyUnknown
341+
}
342+
return in.Spec.GetWkaIPFamily()
343+
}
344+
345+
// GetHeadlessServiceIPFamily always returns an empty array as this is not applicable to Jobs.
346+
func (in *CoherenceJob) GetHeadlessServiceIPFamily() []corev1.IPFamily {
347+
return nil
348+
}
349+
337350
// ----- CoherenceJobList type ----------------------------------------------
338351

339352
// CoherenceJobResourceSpec defines the specification of a CoherenceJob resource.
@@ -487,6 +500,14 @@ func (in *CoherenceJobResourceSpec) GetReplicas() int32 {
487500
return *in.CoherenceResourceSpec.Replicas
488501
}
489502

503+
// GetWkaIPFamily returns the IP Family of the headless Service used for Coherence WKA.
504+
func (in *CoherenceJobResourceSpec) GetWkaIPFamily() corev1.IPFamily {
505+
if in == nil || in.Coherence == nil {
506+
return corev1.IPFamilyUnknown
507+
}
508+
return in.Coherence.GetWkaIPFamily()
509+
}
510+
490511
// UpdateJob updates a JobSpec from the fields in this spec
491512
func (in *CoherenceJobResourceSpec) UpdateJob(spec *batchv1.JobSpec) {
492513
if in == nil {

api/v1/coherenceresource.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ type CoherenceResource interface {
2121
GetCoherenceClusterName() string
2222
// GetWkaServiceName returns the name of the headless Service used for Coherence WKA.
2323
GetWkaServiceName() string
24+
// GetWkaIPFamily returns the IP Family of the headless Service used for Coherence WKA.
25+
GetWkaIPFamily() corev1.IPFamily
2426
// GetHeadlessServiceName returns the name of the headless Service used for the StatefulSet.
2527
GetHeadlessServiceName() string
28+
// GetHeadlessServiceIPFamily always returns an empty array as this is not applicable to Jobs.
29+
GetHeadlessServiceIPFamily() []corev1.IPFamily
2630
// GetReplicas returns the number of replicas required for a deployment.
2731
// The Replicas field is a pointer and may be nil so this method will
2832
// return either the actual Replicas value or the default (DefaultReplicas const)

api/v1/coherenceresource_types.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ func (in *Coherence) GetWkaServiceName() string {
150150
return in.Name + WKAServiceNameSuffix
151151
}
152152

153+
// GetWkaIPFamily returns the IP Family of the headless Service used for Coherence WKA.
154+
func (in *Coherence) GetWkaIPFamily() corev1.IPFamily {
155+
return in.Spec.GetWkaIPFamily()
156+
}
157+
153158
// GetHeadlessServiceName returns the name of the headless Service used for the StatefulSet.
154159
func (in *Coherence) GetHeadlessServiceName() string {
155160
if in == nil {
@@ -158,6 +163,14 @@ func (in *Coherence) GetHeadlessServiceName() string {
158163
return in.Name + HeadlessServiceNameSuffix
159164
}
160165

166+
// GetHeadlessServiceIPFamily returns the IP Family of the headless Service used for the StatefulSet.
167+
func (in *Coherence) GetHeadlessServiceIPFamily() []corev1.IPFamily {
168+
if in == nil {
169+
return nil
170+
}
171+
return in.Spec.HeadlessServiceIpFamilies
172+
}
173+
161174
// GetReplicas returns the number of replicas required for a deployment.
162175
// The Replicas field is a pointer and may be nil so this method will
163176
// return either the actual Replicas value or the default (DefaultReplicas const)
@@ -527,6 +540,10 @@ type CoherenceStatefulSetResourceSpec struct {
527540
// one of the node labels used to set the Coherence site or rack value.
528541
// +optional
529542
RollingUpdateLabel *string `json:"rollingUpdateLabel,omitempty"`
543+
// HeadlessServiceIpFamilies is the optional array of IP families that can be configured for
544+
// the headless service used for the StatefulSet.
545+
// +optional
546+
HeadlessServiceIpFamilies []corev1.IPFamily `json:"headlessServiceIpFamilies,omitempty"`
530547
}
531548

532549
// RollingUpdateStrategyType is a string enumeration type that enumerates

api/v1/coherenceresourcespec_types.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,14 @@ func (in *CoherenceResourceSpec) SetReplicas(replicas int32) {
331331
}
332332
}
333333

334+
// GetWkaIPFamily returns the IP Family of the headless Service used for Coherence WKA.
335+
func (in *CoherenceResourceSpec) GetWkaIPFamily() corev1.IPFamily {
336+
if in == nil || in.Coherence == nil {
337+
return corev1.IPFamilyUnknown
338+
}
339+
return in.Coherence.GetWkaIPFamily()
340+
}
341+
334342
// GetRestartPolicy returns the name of the application image to use
335343
func (in *CoherenceResourceSpec) GetRestartPolicy() *corev1.RestartPolicy {
336344
if in == nil {
@@ -553,6 +561,12 @@ func (in *CoherenceResourceSpec) CreateWKAService(deployment CoherenceResource)
553561
},
554562
}
555563

564+
ip := deployment.GetWkaIPFamily()
565+
if ip != corev1.IPFamilyUnknown {
566+
svc.Spec.IPFamilyPolicy = ptr.To(corev1.IPFamilyPolicySingleStack)
567+
svc.Spec.IPFamilies = []corev1.IPFamily{ip}
568+
}
569+
556570
return Resource{
557571
Kind: ResourceTypeService,
558572
Name: svc.GetName(),
@@ -592,6 +606,15 @@ func (in *CoherenceResourceSpec) CreateHeadlessService(deployment CoherenceResou
592606
},
593607
}
594608

609+
ipFamilies := deployment.GetHeadlessServiceIPFamily()
610+
if len(ipFamilies) == 1 {
611+
svc.Spec.IPFamilyPolicy = ptr.To(corev1.IPFamilyPolicySingleStack)
612+
svc.Spec.IPFamilies = ipFamilies
613+
} else if len(ipFamilies) > 1 {
614+
svc.Spec.IPFamilyPolicy = ptr.To(corev1.IPFamilyPolicyPreferDualStack)
615+
svc.Spec.IPFamilies = ipFamilies
616+
}
617+
595618
return Resource{
596619
Kind: ResourceTypeService,
597620
Name: svc.GetName(),

api/v1/create_job_wka_services_test.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ import (
1212
coh "github.com/oracle/coherence-operator/api/v1"
1313
corev1 "k8s.io/api/core/v1"
1414
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/utils/ptr"
1516
"testing"
1617
)
1718

18-
func TestCreateWKAServiceForMinimalJonDeployment(t *testing.T) {
19+
func TestCreateWKAServiceForMinimalJsonDeployment(t *testing.T) {
1920
// Create the test deployment
2021
deployment := &coh.CoherenceJob{
2122
ObjectMeta: metav1.ObjectMeta{
@@ -315,6 +316,60 @@ func TestCreateWKAServiceForJobWithAdditionalAnnotations(t *testing.T) {
315316
assertWKAServiceForJob(t, deployment, expected)
316317
}
317318

319+
func TestCreateJobWKAServiceWithIPFamily(t *testing.T) {
320+
// Create the test deployment
321+
deployment := &coh.CoherenceJob{
322+
ObjectMeta: metav1.ObjectMeta{
323+
Namespace: "test-ns",
324+
Name: "test",
325+
},
326+
Spec: coh.CoherenceJobResourceSpec{
327+
CoherenceResourceSpec: coh.CoherenceResourceSpec{
328+
Coherence: &coh.CoherenceSpec{
329+
WKA: &coh.CoherenceWKASpec{
330+
IPFamily: ptr.To(corev1.IPv4Protocol),
331+
},
332+
},
333+
},
334+
Cluster: "test-cluster",
335+
},
336+
}
337+
338+
// create the expected WKA service
339+
labels := deployment.CreateCommonLabels()
340+
labels[coh.LabelCoherenceCluster] = "test-cluster"
341+
labels[coh.LabelComponent] = coh.LabelComponentWKA
342+
343+
// The selector for the service (match all Pods with the same cluster label)
344+
selector := make(map[string]string)
345+
selector[coh.LabelCoherenceCluster] = "test-cluster"
346+
selector[coh.LabelComponent] = coh.LabelComponentCoherencePod
347+
selector[coh.LabelCoherenceWKAMember] = "true"
348+
349+
expected := &corev1.Service{
350+
ObjectMeta: metav1.ObjectMeta{
351+
Namespace: "test-ns",
352+
Name: "test-wka",
353+
Labels: labels,
354+
Annotations: map[string]string{
355+
"service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
356+
},
357+
},
358+
Spec: corev1.ServiceSpec{
359+
ClusterIP: corev1.ClusterIPNone,
360+
// Pods must be part of the WKA service even if not ready
361+
PublishNotReadyAddresses: true,
362+
Ports: getDefaultServicePorts(),
363+
Selector: selector,
364+
IPFamilyPolicy: ptr.To(corev1.IPFamilyPolicySingleStack),
365+
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
366+
},
367+
}
368+
369+
// assert that the Services are as expected
370+
assertWKAServiceForJob(t, deployment, expected)
371+
}
372+
318373
func assertWKAServiceForJob(t *testing.T, deployment *coh.CoherenceJob, expected *corev1.Service) {
319374
g := NewGomegaWithT(t)
320375

0 commit comments

Comments
 (0)