Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

Commit

Permalink
Allow all headless services, not just those backed by Statefulsets wi…
Browse files Browse the repository at this point in the history
…th subdomains (#5250)

* Allow all headless services, not just those backed by Statefulsets with subdomains

Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
  • Loading branch information
keithmattix committed Dec 8, 2022
1 parent 6815b67 commit 25c8e53
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ jobs:
matrix:
k8s_version: [""]
focus: [""]
bucket: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
bucket: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
include:
- k8s_version: v1.22.9
focus: "Test traffic flowing from client to server with a Kubernetes Service for the Source: HTTP"
Expand Down
11 changes: 10 additions & 1 deletion pkg/cli/verifier/envoy_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import (

configv1alpha2 "github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
"github.com/openservicemesh/osm/pkg/envoy"
"github.com/openservicemesh/osm/pkg/k8s"
"github.com/openservicemesh/osm/pkg/trafficpolicy"

"github.com/openservicemesh/osm/pkg/constants"
"github.com/openservicemesh/osm/pkg/envoy/lds"
"github.com/openservicemesh/osm/pkg/envoy/rds/route"
envoySecrets "github.com/openservicemesh/osm/pkg/envoy/secrets"
"github.com/openservicemesh/osm/pkg/identity"
"github.com/openservicemesh/osm/pkg/k8s"
"github.com/openservicemesh/osm/pkg/service"
)

Expand Down Expand Up @@ -480,11 +480,15 @@ func (v *EnvoyConfigVerifier) getDstMeshServicesForSvcPod(svc corev1.Service, po
// us to retrieve the TargetPort for the MeshService.
meshSvc.TargetPort = k8s.GetTargetPortFromEndpoints(portSpec.Name, *endpoints)

// Even if the service is headless, add it so it can be targeted
if !k8s.IsHeadlessService(svc) {
meshServices = append(meshServices, meshSvc)
continue
}

// If there's not at least 1 subdomain-ed MeshService added,
// add the entire headless service
var added bool
for _, subset := range endpoints.Subsets {
for _, address := range subset.Addresses {
if address.Hostname == "" {
Expand All @@ -498,8 +502,13 @@ func (v *EnvoyConfigVerifier) getDstMeshServicesForSvcPod(svc corev1.Service, po
Protocol: meshSvc.Protocol,
}
meshServices = append(meshServices, mSvc)
added = true
}
}

if !added {
meshServices = append(meshServices, meshSvc)
}
}

return meshServices, nil
Expand Down
10 changes: 8 additions & 2 deletions pkg/k8s/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,9 @@ func ServiceToMeshServices(c Controller, svc corev1.Service) []service.MeshServi
meshServices = append(meshServices, meshSvc)
continue
}

// If there's not at least 1 subdomain-ed MeshService added,
// add the entire headless service
var added bool
for _, subset := range endpoints.Subsets {
for _, address := range subset.Addresses {
if address.Hostname == "" {
Expand All @@ -335,10 +337,14 @@ func ServiceToMeshServices(c Controller, svc corev1.Service) []service.MeshServi
TargetPort: meshSvc.TargetPort,
Protocol: meshSvc.Protocol,
})
added = true
}
}
}

if !added {
meshServices = append(meshServices, meshSvc)
}
}
return meshServices
}

Expand Down
55 changes: 55 additions & 0 deletions pkg/k8s/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,61 @@ func TestK8sServicesToMeshServices(t *testing.T) {
},
},
},
{
name: "k8s headless service with single port and endpoint (no hostname), no appProtocol set",
// Single port on the service maps to a single MeshService.
// Since no appProtocol is specified, MeshService.Protocol should default
// to http because Port.Protocol=TCP
svc: corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns1",
Name: "s1",
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "p1",
Port: 80,
Protocol: corev1.ProtocolTCP,
},
},
ClusterIP: corev1.ClusterIPNone,
},
},
svcEndpoints: []runtime.Object{
&corev1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
// Should match svc.Name and svc.Namespace
Namespace: "ns1",
Name: "s1",
},
Subsets: []corev1.EndpointSubset{
{
Addresses: []corev1.EndpointAddress{
{
IP: "10.1.0.1",
},
},
Ports: []corev1.EndpointPort{
{
// Must match the port of 'svc.Spec.Ports[0]'
Port: 8080, // TargetPort
},
},
},
},
},
},
expected: []service.MeshService{
{
Namespace: "ns1",
Name: "s1",
Port: 80,
TargetPort: 8080,
Protocol: "http",
},
},
},
{
name: "multiple ports on k8s service with appProtocol specified",
svc: corev1.Service{
Expand Down
19 changes: 14 additions & 5 deletions tests/e2e/e2e_client_server_connectivity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
"github.com/openservicemesh/osm/pkg/tests"

"github.com/openservicemesh/osm/pkg/constants"
. "github.com/openservicemesh/osm/tests/framework"
Expand All @@ -26,26 +27,34 @@ var _ = OSMDescribe("Test HTTP traffic from 1 pod client -> 1 pod server",
func() {
Context("Test traffic flowing from client to server with a Kubernetes Service for the Source: HTTP", func() {
withSourceKubernetesService := true
testTraffic(withSourceKubernetesService, PodCommandDefault)
testTraffic(withSourceKubernetesService, PodCommandDefault, false)
})

Context("Test traffic flowing from client to server without a Kubernetes Service for the Source: HTTP", func() {
// Prior iterations of OSM required that a source pod belong to a Kubernetes service
// for the Envoy proxy to be configured for outbound traffic to some remote server.
// This test ensures we test this scenario: client Pod is not associated w/ a service.
withSourceKubernetesService := false
testTraffic(withSourceKubernetesService, PodCommandDefault)
testTraffic(withSourceKubernetesService, PodCommandDefault, false)
})

Context("Test traffic flowing from client to a server with a podIP bind", func() {
// Prior iterations of OSM didn't allow mesh services to bind to the podIP
// This test ensures that that behavior is configurable via MeshConfig
withSourceKubernetesService := true
testTraffic(withSourceKubernetesService, []string{"gunicorn", "-b", "$(POD_IP):80", "httpbin:app", "-k", "gevent"}, WithLocalProxyMode(v1alpha2.LocalProxyModePodIP))
testTraffic(withSourceKubernetesService, []string{"gunicorn", "-b", "$(POD_IP):80", "httpbin:app", "-k", "gevent"}, false, WithLocalProxyMode(v1alpha2.LocalProxyModePodIP))
})

Context("Test traffic flowing from client to a headless service without a Kubernetes Service for the Source: HTTP", func() {
// Prior iterations of OSM required that a source pod belong to a Kubernetes service
// for the Envoy proxy to be configured for outbound traffic to some remote server.
// This test ensures we test this scenario: client Pod is not associated w/ a service.
withSourceKubernetesService := true
testTraffic(withSourceKubernetesService, PodCommandDefault, true)
})
})

func testTraffic(withSourceKubernetesService bool, destPodCommand []string, installOpts ...InstallOsmOpt) {
func testTraffic(withSourceKubernetesService bool, destPodCommand []string, destServiceHeadless bool, installOpts ...InstallOsmOpt) {
const sourceName = "client"
const destName = "server"
var ns = []string{sourceName, destName}
Expand All @@ -68,7 +77,7 @@ func testTraffic(withSourceKubernetesService bool, destPodCommand []string, inst
Expect(err).NotTo(HaveOccurred())
_, err = Td.CreatePod(destName, podDef)
Expect(err).NotTo(HaveOccurred())
dstSvc, err := Td.CreateService(destName, svcDef)
dstSvc, err := Td.CreateService(destName, *tests.HeadlessSvc(&svcDef))
Expect(err).NotTo(HaveOccurred())

// Expect it to be up and running in it's receiver namespace
Expand Down
Loading

0 comments on commit 25c8e53

Please sign in to comment.