Skip to content

Commit 5c6b00d

Browse files
authored
Importing Headless Services Support for A/AAAA records (#180)
1 parent c1f88e9 commit 5c6b00d

File tree

3 files changed

+137
-14
lines changed

3 files changed

+137
-14
lines changed

pkg/controllers/multicluster/cloudmap_controller.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func (r *CloudMapReconciler) reconcileService(ctx context.Context, svc *model.Se
138138
}
139139

140140
// create ServiceImport if it doesn't exist
141-
if svcImport, err = r.createAndGetServiceImport(ctx, svc.Namespace, svc.Name, importedSvcPorts); err != nil {
141+
if svcImport, err = r.createAndGetServiceImport(ctx, svc, importedSvcPorts); err != nil {
142142
return err
143143
}
144144
}
@@ -174,14 +174,14 @@ func (r *CloudMapReconciler) getServiceImport(ctx context.Context, namespace str
174174
return existingServiceImport, err
175175
}
176176

177-
func (r *CloudMapReconciler) createAndGetServiceImport(ctx context.Context, namespace string, name string, servicePorts []*model.Port) (*multiclusterv1alpha1.ServiceImport, error) {
178-
toCreate := CreateServiceImportStruct(namespace, name, servicePorts)
177+
func (r *CloudMapReconciler) createAndGetServiceImport(ctx context.Context, svc *model.Service, servicePorts []*model.Port) (*multiclusterv1alpha1.ServiceImport, error) {
178+
toCreate := CreateServiceImportStruct(svc, servicePorts)
179179
if err := r.Client.Create(ctx, toCreate); err != nil {
180180
return nil, err
181181
}
182-
r.Log.Info("created ServiceImport", "namespace", namespace, "name", name)
182+
r.Log.Info("created ServiceImport", "namespace", svc.Namespace, "name", svc.Name)
183183

184-
return r.getServiceImport(ctx, namespace, name)
184+
return r.getServiceImport(ctx, svc.Namespace, svc.Name)
185185
}
186186

187187
func (r *CloudMapReconciler) getDerivedService(ctx context.Context, namespace string, name string) (*v1.Service, error) {

pkg/controllers/multicluster/utils.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,21 +142,21 @@ func DerivedName(namespace string, name string) string {
142142
}
143143

144144
// CreateServiceImportStruct creates struct representation of a ServiceImport
145-
func CreateServiceImportStruct(namespace string, name string, servicePorts []*model.Port) *multiclusterv1alpha1.ServiceImport {
145+
func CreateServiceImportStruct(svc *model.Service, servicePorts []*model.Port) *multiclusterv1alpha1.ServiceImport {
146146
serviceImportPorts := make([]multiclusterv1alpha1.ServicePort, 0)
147147
for _, port := range servicePorts {
148148
serviceImportPorts = append(serviceImportPorts, PortToServiceImportPort(*port))
149149
}
150150

151151
return &multiclusterv1alpha1.ServiceImport{
152152
ObjectMeta: metav1.ObjectMeta{
153-
Namespace: namespace,
154-
Name: name,
155-
Annotations: map[string]string{DerivedServiceAnnotation: DerivedName(namespace, name)},
153+
Namespace: svc.Namespace,
154+
Name: svc.Name,
155+
Annotations: map[string]string{DerivedServiceAnnotation: DerivedName(svc.Namespace, svc.Name)},
156156
},
157157
Spec: multiclusterv1alpha1.ServiceImportSpec{
158158
IPs: []string{},
159-
Type: multiclusterv1alpha1.ClusterSetIP,
159+
Type: ServiceTypetoServiceImportType(svc.Endpoints[0].ServiceType), // assume each endpoint has the same serviceType
160160
Ports: serviceImportPorts,
161161
},
162162
}
@@ -174,7 +174,7 @@ func CreateDerivedServiceStruct(svcImport *multiclusterv1alpha1.ServiceImport, i
174174
svcPorts = append(svcPorts, PortToServicePort(*svcPort))
175175
}
176176

177-
return &v1.Service{
177+
svc := &v1.Service{
178178
ObjectMeta: metav1.ObjectMeta{
179179
Namespace: svcImport.Namespace,
180180
Name: svcImport.Annotations[DerivedServiceAnnotation],
@@ -185,6 +185,13 @@ func CreateDerivedServiceStruct(svcImport *multiclusterv1alpha1.ServiceImport, i
185185
Ports: svcPorts,
186186
},
187187
}
188+
189+
// if svcImport is Headless type, specify ClusterIP field to "None"
190+
if svcImport.Spec.Type == multiclusterv1alpha1.Headless {
191+
svc.Spec.ClusterIP = "None"
192+
}
193+
194+
return svc
188195
}
189196

190197
func CreateEndpointForSlice(svc *v1.Service, ip string) discovery.Endpoint {
@@ -235,3 +242,12 @@ func ExtractServiceType(svc *v1.Service) model.ServiceType {
235242

236243
return model.ClusterSetIPType
237244
}
245+
246+
// ServiceTypetoServiceImportType converts model service type to multicluster ServiceImport type
247+
func ServiceTypetoServiceImportType(serviceType model.ServiceType) multiclusterv1alpha1.ServiceImportType {
248+
if serviceType == model.HeadlessType {
249+
return multiclusterv1alpha1.Headless
250+
}
251+
252+
return multiclusterv1alpha1.ClusterSetIP
253+
}

pkg/controllers/multicluster/utils_test.go

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ func TestPortsEqualIgnoreOrder(t *testing.T) {
450450
func TestCreateServiceImportStruct(t *testing.T) {
451451
type args struct {
452452
servicePorts []*model.Port
453+
endpoints []*model.Endpoint
453454
}
454455
tests := []struct {
455456
name string
@@ -461,7 +462,12 @@ func TestCreateServiceImportStruct(t *testing.T) {
461462
args: args{
462463
servicePorts: []*model.Port{
463464
{Name: test.PortName1, Protocol: test.Protocol1, Port: test.Port1},
464-
{Name: test.PortName2, Protocol: test.Protocol1, Port: test.Port2},
465+
{Name: test.PortName2, Protocol: test.Protocol2, Port: test.Port2},
466+
},
467+
endpoints: []*model.Endpoint{
468+
{
469+
ServiceType: model.ClusterSetIPType,
470+
},
465471
},
466472
},
467473
want: multiclusterv1alpha1.ServiceImport{
@@ -475,15 +481,15 @@ func TestCreateServiceImportStruct(t *testing.T) {
475481
Type: multiclusterv1alpha1.ClusterSetIP,
476482
Ports: []multiclusterv1alpha1.ServicePort{
477483
{Name: test.PortName1, Protocol: v1.ProtocolTCP, Port: test.Port1},
478-
{Name: test.PortName2, Protocol: v1.ProtocolTCP, Port: test.Port2},
484+
{Name: test.PortName2, Protocol: v1.ProtocolUDP, Port: test.Port2},
479485
},
480486
},
481487
},
482488
},
483489
}
484490
for _, tt := range tests {
485491
t.Run(tt.name, func(t *testing.T) {
486-
if got := CreateServiceImportStruct(test.HttpNsName, test.SvcName, tt.args.servicePorts); !reflect.DeepEqual(*got, tt.want) {
492+
if got := CreateServiceImportStruct(test.GetTestServiceWithEndpoint(tt.args.endpoints), tt.args.servicePorts); !reflect.DeepEqual(*got, tt.want) {
487493
t.Errorf("CreateServiceImportStruct() = %v, want %v", got, tt.want)
488494
}
489495
})
@@ -547,3 +553,104 @@ func TestExtractServiceType(t *testing.T) {
547553
})
548554
}
549555
}
556+
557+
func TestServiceTypetoServiceImportType(t *testing.T) {
558+
tests := []struct {
559+
name string
560+
svcType model.ServiceType
561+
want multiclusterv1alpha1.ServiceImportType
562+
}{
563+
{
564+
name: "cluster ip type",
565+
svcType: model.ClusterSetIPType,
566+
want: multiclusterv1alpha1.ClusterSetIP,
567+
},
568+
{
569+
name: "headless type",
570+
svcType: model.HeadlessType,
571+
want: multiclusterv1alpha1.Headless,
572+
},
573+
}
574+
for _, tt := range tests {
575+
t.Run(tt.name, func(t *testing.T) {
576+
if got := ServiceTypetoServiceImportType(tt.svcType); got != tt.want {
577+
t.Errorf("ServiceTypetoServiceImportType() = %v, want %v", got, tt.want)
578+
}
579+
})
580+
}
581+
}
582+
583+
func TestCreateDerivedServiceStruct(t *testing.T) {
584+
type args struct {
585+
servicePorts []*model.Port
586+
svcImport *multiclusterv1alpha1.ServiceImport
587+
}
588+
tests := []struct {
589+
name string
590+
args args
591+
want *v1.ServiceSpec
592+
}{
593+
{
594+
name: "cluster ip case",
595+
args: args{
596+
servicePorts: []*model.Port{
597+
{Name: test.PortName1, Protocol: test.Protocol1, Port: test.Port1, TargetPort: "8080"},
598+
{Name: test.PortName2, Protocol: test.Protocol2, Port: test.Port2, TargetPort: "8080"},
599+
},
600+
svcImport: &multiclusterv1alpha1.ServiceImport{
601+
ObjectMeta: metav1.ObjectMeta{
602+
Namespace: test.HttpNsName,
603+
Name: test.SvcName,
604+
Annotations: map[string]string{DerivedServiceAnnotation: DerivedName(test.HttpNsName, test.SvcName)},
605+
},
606+
Spec: multiclusterv1alpha1.ServiceImportSpec{
607+
IPs: []string{},
608+
Type: multiclusterv1alpha1.ClusterSetIP,
609+
},
610+
},
611+
},
612+
want: &v1.ServiceSpec{
613+
Type: v1.ServiceTypeClusterIP,
614+
Ports: []v1.ServicePort{
615+
{Name: test.PortName1, Protocol: test.Protocol1, Port: test.Port1, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}},
616+
{Name: test.PortName2, Protocol: test.Protocol2, Port: test.Port2, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}},
617+
},
618+
},
619+
},
620+
{
621+
name: "headless case",
622+
args: args{
623+
servicePorts: []*model.Port{
624+
{Name: test.PortName1, Protocol: test.Protocol1, Port: test.Port1, TargetPort: "8080"},
625+
{Name: test.PortName2, Protocol: test.Protocol2, Port: test.Port2, TargetPort: "8080"},
626+
},
627+
svcImport: &multiclusterv1alpha1.ServiceImport{
628+
ObjectMeta: metav1.ObjectMeta{
629+
Namespace: test.HttpNsName,
630+
Name: test.SvcName,
631+
Annotations: map[string]string{DerivedServiceAnnotation: DerivedName(test.HttpNsName, test.SvcName)},
632+
},
633+
Spec: multiclusterv1alpha1.ServiceImportSpec{
634+
IPs: []string{},
635+
Type: multiclusterv1alpha1.Headless,
636+
},
637+
},
638+
},
639+
want: &v1.ServiceSpec{
640+
Type: v1.ServiceTypeClusterIP,
641+
Ports: []v1.ServicePort{
642+
{Name: test.PortName1, Protocol: test.Protocol1, Port: test.Port1, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}},
643+
{Name: test.PortName2, Protocol: test.Protocol2, Port: test.Port2, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}},
644+
},
645+
ClusterIP: "None",
646+
},
647+
},
648+
}
649+
for _, tt := range tests {
650+
t.Run(tt.name, func(t *testing.T) {
651+
if got := &CreateDerivedServiceStruct(tt.args.svcImport, tt.args.servicePorts).Spec; !reflect.DeepEqual(got, tt.want) {
652+
t.Errorf("CreateDerivedServiceStruct() = %v, want %v", got, tt.want)
653+
}
654+
})
655+
}
656+
}

0 commit comments

Comments
 (0)