Skip to content

Commit 3b19d23

Browse files
authored
Forward porting Gloo service lookup by labels to 1.19.x (#10669)
1 parent 98c7bdd commit 3b19d23

File tree

6 files changed

+140
-84
lines changed

6 files changed

+140
-84
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
changelog:
2+
- type: FIX
3+
description: >-
4+
Discover the Gloo service using labels.
5+
In some environments, services must be renamed.
6+
This change allows the service to still be discovered when it's been renamed.
7+
In the event that multiple services in the namespace have the gloo=gloo label, an error will occur.
8+
issueLink: https://github.com/solo-io/solo-projects/issues/7646
9+
resolvesIssue: false

pkg/utils/kubeutils/names.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package kubeutils
22

33
const (
4-
GlooDeploymentName = "gloo"
5-
GlooServiceName = "gloo"
4+
GlooDeploymentName = "gloo"
5+
GlooServiceName = "gloo"
6+
GlooServiceAppLabel = "gloo"
7+
GlooServiceGlooLabel = "gloo"
68

79
// GlooXdsPortName is the name of the port in the Gloo Gateway control plane Kubernetes Service that serves xDS config.
810
// See: install/helm/gloo/templates/2-gloo-service.yaml
911
GlooXdsPortName = "grpc-xds"
1012

1113
DiscoveryDeploymentName = "discovery"
1214
)
15+
16+
var (
17+
GlooServiceLabels = map[string]string{
18+
"gloo": GlooServiceGlooLabel,
19+
}
20+
)

projects/gloo/pkg/syncer/setup/controlplane.go

+31-24
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ import (
77

88
"github.com/rotisserie/eris"
99
"github.com/solo-io/gloo/pkg/utils/kubeutils"
10-
"github.com/solo-io/gloo/pkg/utils/namespaces"
1110
"github.com/solo-io/solo-kit/pkg/api/v1/clients"
1211
skkube "github.com/solo-io/solo-kit/pkg/api/v1/resources/common/kubernetes"
13-
apierrors "k8s.io/apimachinery/pkg/api/errors"
1412
)
1513

1614
var (
@@ -19,42 +17,51 @@ var (
1917
return eris.Wrapf(NoXdsPortFoundError, "no port with the name %s found in service %s.%s", portName, svcNamespace, svcName)
2018
}
2119
NoGlooSvcFoundError = eris.New("failed to find Gloo service")
22-
noGlooSvcFoundError = func(err error, svcNamespace string, svcName string) error {
23-
wrapped := eris.Wrap(err, NoGlooSvcFoundError.Error())
24-
return eris.Wrapf(wrapped, "service %s.%s", svcNamespace, svcName)
20+
noGlooSvcFoundError = func(svcNamespace string) error {
21+
return eris.Wrapf(NoGlooSvcFoundError, "service in %s with gloo=gloo label", svcNamespace)
22+
}
23+
MultipleGlooSvcFoundError = eris.New("found multiple Gloo services")
24+
multipleGlooSvcFoundError = func(svcNamespace string) error {
25+
return eris.Wrapf(MultipleGlooSvcFoundError, "found multiple services in %s with gloo=glo label", svcNamespace)
2526
}
2627
)
2728

28-
// GetControlPlaneXdsPort gets the xDS port from the gloo Service.
29-
func GetControlPlaneXdsPort(ctx context.Context, svcClient skkube.ServiceClient) (int32, error) {
30-
// When this code is invoked from within the running Pod, this will contain the namespace where Gloo is running
31-
svcNamespace := namespaces.GetPodNamespace()
32-
return GetNamespacedControlPlaneXdsPort(ctx, svcNamespace, svcClient)
33-
}
34-
35-
// GetNamespacedControlPlaneXdsPort gets the xDS port from the Gloo Service, provided the namespace the Service is running in
36-
func GetNamespacedControlPlaneXdsPort(ctx context.Context, svcNamespace string, svcClient skkube.ServiceClient) (int32, error) {
37-
glooSvc, err := svcClient.Read(svcNamespace, kubeutils.GlooServiceName, clients.ReadOpts{Ctx: ctx})
29+
func GetControlPlaneService(ctx context.Context, svcNamespace string, svcClient skkube.ServiceClient) (*skkube.Service, error) {
30+
opts := clients.ListOpts{
31+
Ctx: ctx,
32+
Selector: kubeutils.GlooServiceLabels,
33+
}
34+
services, err := svcClient.List(svcNamespace, opts)
3835
if err != nil {
39-
if apierrors.IsNotFound(err) {
40-
return 0, noGlooSvcFoundError(err, svcNamespace, kubeutils.GlooServiceName)
41-
}
42-
return 0, err
36+
return nil, err
4337
}
4438

39+
if len(services) == 0 {
40+
return nil, noGlooSvcFoundError(svcNamespace)
41+
}
42+
43+
if len(services) > 1 {
44+
return nil, multipleGlooSvcFoundError(svcNamespace)
45+
}
46+
47+
return services[0], nil
48+
}
49+
50+
// GetControlPlaneXdsPort gets the xDS port from the Gloo Service
51+
func GetControlPlaneXdsPort(service *skkube.Service) (int32, error) {
4552
// find the xds port on the Gloo Service
46-
for _, port := range glooSvc.Spec.Ports {
53+
for _, port := range service.Spec.Ports {
4754
if port.Name == kubeutils.GlooXdsPortName {
4855
return port.Port, nil
4956
}
5057
}
51-
return 0, noXdsPortFoundError(kubeutils.GlooXdsPortName, svcNamespace, kubeutils.GlooServiceName)
58+
return 0, noXdsPortFoundError(kubeutils.GlooXdsPortName, service.Namespace, service.Name)
5259
}
5360

5461
// GetControlPlaneXdsHost gets the xDS address from the gloo Service.
55-
func GetControlPlaneXdsHost() string {
62+
func GetControlPlaneXdsHost(service *skkube.Service) string {
5663
return kubeutils.ServiceFQDN(metav1.ObjectMeta{
57-
Name: kubeutils.GlooServiceName,
58-
Namespace: namespaces.GetPodNamespace(),
64+
Name: service.Name,
65+
Namespace: service.Namespace,
5966
})
6067
}

projects/gloo/pkg/syncer/setup/controlplane_test.go

+72-53
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,23 @@ import (
77
. "github.com/onsi/ginkgo/v2"
88
. "github.com/onsi/gomega"
99
"github.com/solo-io/gloo/pkg/utils/kubeutils"
10+
"github.com/solo-io/gloo/projects/gloo/pkg/defaults"
1011
"github.com/solo-io/gloo/projects/gloo/pkg/syncer/setup"
1112
"github.com/solo-io/solo-kit/pkg/api/v1/clients"
1213
"github.com/solo-io/solo-kit/pkg/api/v1/clients/factory"
1314
"github.com/solo-io/solo-kit/pkg/api/v1/clients/memory"
1415
skkube "github.com/solo-io/solo-kit/pkg/api/v1/resources/common/kubernetes"
15-
skerrors "github.com/solo-io/solo-kit/pkg/errors"
1616
"github.com/solo-io/solo-kit/pkg/utils/statusutils"
1717
corev1 "k8s.io/api/core/v1"
1818
)
1919

20-
var _ = Describe("ControlPlane", func() {
21-
22-
Context("xds host", func() {
23-
24-
AfterEach(func() {
25-
err := os.Unsetenv(statusutils.PodNamespaceEnvName)
26-
Expect(err).NotTo(HaveOccurred())
27-
})
28-
29-
It("respects POD_NAMESPACE", func() {
30-
err := os.Setenv(statusutils.PodNamespaceEnvName, "other-ns")
31-
Expect(err).NotTo(HaveOccurred())
32-
xdsHost := setup.GetControlPlaneXdsHost()
33-
Expect(xdsHost).To(Equal("gloo.other-ns.svc.cluster.local"))
34-
})
35-
36-
It("uses default value when POD_NAMESPACE not set", func() {
37-
xdsHost := setup.GetControlPlaneXdsHost()
38-
Expect(xdsHost).To(Equal("gloo.gloo-system.svc.cluster.local"))
39-
})
40-
})
20+
const (
21+
otherNS = "other-ns"
22+
)
4123

42-
Context("xds port", func() {
24+
var _ = Describe("ControlPlane", func() {
4325

26+
Context("xds service", func() {
4427
var (
4528
ctx context.Context
4629
svcClient skkube.ServiceClient
@@ -58,7 +41,8 @@ var _ = Describe("ControlPlane", func() {
5841
svcClient, err = skkube.NewServiceClient(ctx, inMemoryFactory)
5942
Expect(err).NotTo(HaveOccurred())
6043

61-
svc1 = skkube.NewService("gloo-system", kubeutils.GlooServiceName)
44+
svc1 = skkube.NewService(defaults.GlooSystem, kubeutils.GlooServiceName)
45+
svc1.Labels = kubeutils.GlooServiceLabels
6246
svc1.Spec = corev1.ServiceSpec{
6347
Ports: []corev1.ServicePort{
6448
{
@@ -68,7 +52,8 @@ var _ = Describe("ControlPlane", func() {
6852
},
6953
}
7054

71-
svc2 = skkube.NewService("other-ns", kubeutils.GlooServiceName)
55+
svc2 = skkube.NewService(otherNS, kubeutils.GlooServiceName)
56+
svc2.Labels = kubeutils.GlooServiceLabels
7257
svc2.Spec = corev1.ServiceSpec{
7358
Ports: []corev1.ServicePort{
7459
{
@@ -84,58 +69,92 @@ var _ = Describe("ControlPlane", func() {
8469
Expect(err).NotTo(HaveOccurred())
8570
})
8671

87-
It("returns xds port from gloo service in default namespace", func() {
72+
It("returns xds service from gloo service in default namespace", func() {
8873
// write both services
8974
_, err = svcClient.Write(svc1, clients.WriteOpts{Ctx: ctx})
9075
Expect(err).NotTo(HaveOccurred())
9176
_, err = svcClient.Write(svc2, clients.WriteOpts{Ctx: ctx})
9277
Expect(err).NotTo(HaveOccurred())
9378

9479
// should return the port from gloo service in gloo-system
95-
port, err := setup.GetControlPlaneXdsPort(ctx, svcClient)
80+
service, err := setup.GetControlPlaneService(ctx, defaults.GlooSystem, svcClient)
9681
Expect(err).NotTo(HaveOccurred())
97-
Expect(port).To(Equal(int32(1111)))
82+
Expect(service).NotTo(BeNil())
83+
Expect(service.Name).To(Equal(svc1.Name))
84+
Expect(service.Namespace).To(Equal(svc1.Namespace))
9885
})
9986

100-
It("returns xds port from gloo service in POD_NAMESPACE namespace", func() {
101-
err := os.Setenv(statusutils.PodNamespaceEnvName, "other-ns")
102-
Expect(err).NotTo(HaveOccurred())
103-
104-
// write both services
87+
It("returns error when no gloo service exists in the namespace", func() {
88+
// only write svc1
10589
_, err = svcClient.Write(svc1, clients.WriteOpts{Ctx: ctx})
10690
Expect(err).NotTo(HaveOccurred())
107-
_, err = svcClient.Write(svc2, clients.WriteOpts{Ctx: ctx})
108-
Expect(err).NotTo(HaveOccurred())
10991

110-
// should return the port from gloo service in other-ns
111-
port, err := setup.GetControlPlaneXdsPort(ctx, svcClient)
112-
Expect(err).NotTo(HaveOccurred())
113-
Expect(port).To(Equal(int32(2222)))
92+
_, err = setup.GetControlPlaneService(ctx, "another-ns", svcClient)
93+
Expect(err).To(HaveOccurred())
94+
Expect(err).To(MatchError(setup.NoGlooSvcFoundError))
11495
})
11596

116-
It("returns error when no gloo service exists in the POD_NAMESPACE namespace", func() {
117-
err := os.Setenv(statusutils.PodNamespaceEnvName, "other-ns")
118-
Expect(err).NotTo(HaveOccurred())
97+
It("returns error when multiple gloo services exist in the namespace", func() {
98+
dupeSvc := skkube.NewService("other-ns", "duplicate-gloo-service")
99+
dupeSvc.Labels = kubeutils.GlooServiceLabels
100+
dupeSvc.Spec = corev1.ServiceSpec{
101+
Ports: []corev1.ServicePort{
102+
{
103+
Name: kubeutils.GlooXdsPortName,
104+
Port: 3333,
105+
},
106+
},
107+
}
119108

120-
// only write svc1
121-
_, err = svcClient.Write(svc1, clients.WriteOpts{Ctx: ctx})
109+
// write both services
110+
_, err = svcClient.Write(svc2, clients.WriteOpts{Ctx: ctx})
111+
Expect(err).NotTo(HaveOccurred())
112+
_, err = svcClient.Write(dupeSvc, clients.WriteOpts{Ctx: ctx})
122113
Expect(err).NotTo(HaveOccurred())
123114

124-
_, err = setup.GetControlPlaneXdsPort(ctx, svcClient)
115+
_, err = setup.GetControlPlaneService(ctx, otherNS, svcClient)
125116
Expect(err).To(HaveOccurred())
126-
Expect(skerrors.IsNotExist(err)).To(BeTrue())
117+
Expect(err).To(MatchError(setup.MultipleGlooSvcFoundError))
127118
})
119+
})
128120

129-
It("returns error when the expected port name is not found", func() {
130-
err := os.Setenv(statusutils.PodNamespaceEnvName, "other-ns")
131-
Expect(err).NotTo(HaveOccurred())
121+
Context("xds host", func() {
122+
It("get FQDN for service", func() {
123+
svc := skkube.NewService(defaults.GlooSystem, kubeutils.GlooServiceName)
124+
xdsHost := setup.GetControlPlaneXdsHost(svc)
125+
Expect(xdsHost).To(Equal("gloo.gloo-system.svc.cluster.local"))
126+
})
127+
})
132128

133-
// modify the port name
134-
svc2.Spec.Ports[0].Name = "test-name"
135-
_, err = svcClient.Write(svc2, clients.WriteOpts{Ctx: ctx})
129+
Context("xds port", func() {
130+
It("returns xds port", func() {
131+
svc := skkube.NewService(defaults.GlooSystem, kubeutils.GlooServiceName)
132+
svc.Spec = corev1.ServiceSpec{
133+
Ports: []corev1.ServicePort{
134+
{
135+
Name: kubeutils.GlooXdsPortName,
136+
Port: 1111,
137+
},
138+
},
139+
}
140+
141+
port, err := setup.GetControlPlaneXdsPort(svc)
136142
Expect(err).NotTo(HaveOccurred())
143+
Expect(port).To(Equal(int32(1111)))
144+
})
145+
146+
It("returns error when the expected port name is not found", func() {
147+
svc := skkube.NewService(defaults.GlooSystem, kubeutils.GlooServiceName)
148+
svc.Spec = corev1.ServiceSpec{
149+
Ports: []corev1.ServicePort{
150+
{
151+
Name: "not-the-right-name",
152+
Port: 1111,
153+
},
154+
},
155+
}
137156

138-
_, err = setup.GetControlPlaneXdsPort(ctx, svcClient)
157+
_, err := setup.GetControlPlaneXdsPort(svc)
139158
Expect(err).To(HaveOccurred())
140159
Expect(err).To(MatchError(setup.NoXdsPortFoundError))
141160
})

projects/gloo/pkg/syncer/setup/setup_syncer.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,19 @@ func (s *setupSyncer) Setup(ctx context.Context, kubeCache kube.SharedCache, mem
338338
var xdsPort int32
339339
switch settings.GetConfigSource().(type) {
340340
case *v1.Settings_KubernetesConfigSource:
341-
xdsHost = GetControlPlaneXdsHost()
342-
xdsPort, err = GetControlPlaneXdsPort(ctx, opts.KubeServiceClient)
341+
glooService, err := GetControlPlaneService(ctx, writeNamespace, opts.KubeServiceClient)
343342
if err != nil {
344343
return err
345344
}
345+
346+
xdsHost = GetControlPlaneXdsHost(glooService)
347+
xdsPort, err = GetControlPlaneXdsPort(glooService)
348+
if err != nil {
349+
return err
350+
}
351+
352+
logger := contextutils.LoggerFrom(ctx)
353+
logger.Infof("using xds host %v and xds port %v", xdsHost, xdsPort)
346354
}
347355

348356
// process grpcserver options to understand if any servers will need a restart

test/kubernetes/e2e/features/deployer/suite.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,12 @@ func xdsClusterAssertion(testInstallation *e2e.TestInstallation) func(ctx contex
331331
Namespace: testInstallation.Metadata.InstallNamespace,
332332
})), "xds socket address points to gloo service, in installation namespace")
333333

334-
xdsPort, err := setup.GetNamespacedControlPlaneXdsPort(ctx, testInstallation.Metadata.InstallNamespace, testInstallation.ResourceClients.ServiceClient())
334+
service, err := setup.GetControlPlaneService(ctx, testInstallation.Metadata.InstallNamespace,
335+
testInstallation.ResourceClients.ServiceClient())
336+
g.Expect(err).NotTo(gomega.HaveOccurred())
337+
g.Expect(service).NotTo(gomega.BeNil())
338+
339+
xdsPort, err := setup.GetControlPlaneXdsPort(service)
335340
g.Expect(err).NotTo(gomega.HaveOccurred())
336341
g.Expect(xdsSocketAddress.GetPortValue()).To(gomega.Equal(uint32(xdsPort)), "xds socket port points to gloo service, in installation namespace")
337342
}).
@@ -383,8 +388,8 @@ func awsStsClusterAssertion(testInstallation *e2e.TestInstallation) func(ctx con
383388
g.Expect(socketAddr.GetAddress()).To(gomega.Equal(expectedStsUri))
384389
}).
385390
WithContext(ctx).
386-
WithTimeout(time.Second * 10).
387-
WithPolling(time.Millisecond * 200).
391+
WithTimeout(time.Second * 30).
392+
WithPolling(time.Second * 1).
388393
Should(gomega.Succeed())
389394
}
390395
}

0 commit comments

Comments
 (0)