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

Commit 5f54056

Browse files
authored
traffic-split: update root service selector & targetPort usage (#4902)
This change does the following: 1. Fixes the incorrect legacy behavior where traffic directed to a root service specified in a TrafficSplit resource can direct traffic to pods that do not match the root service's selector. Not only was this behavior confusing, it also significantly complicated code paths that required special handling of this scenario that is unintuitive. Going forward, the root service selector must match pods for traffic splitting to those pods to function. Existing e2e tests relying on this unsupported behavior have been updated to correctly configure selectors and labels on services and pods backing them. A redundant test explicitly testing the only supported scenario after this change has been removed. The automated demo has also been updated to correctly configure the selector and labels. 2. Fixes #4894, where the TargetPort on the root service was expected to match the ContainerPort on the pod backing the service. Per SMI's TrafficSplit API, the TargetPort on the root does not need to match the ContainerPort on the pod, thus allowing newer application versions to change the ports they listen on without needing to update the root service definition. Signed-off-by: Shashank Ram <[email protected]>
1 parent d5d3a25 commit 5f54056

14 files changed

+308
-640
lines changed

ci/cmd/maestro.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ const (
2727
// Pod labels
2828
bookBuyerLabel = "bookbuyer"
2929
bookThiefLabel = "bookthief"
30-
bookstoreV1Label = "bookstore-v1"
31-
bookstoreV2Label = "bookstore-v2"
30+
bookstoreLabel = "bookstore"
3231
bookWarehouseLabel = "bookwarehouse"
3332
mySQLLabel = "mysql"
3433
)
@@ -37,8 +36,8 @@ var (
3736
osmControllerPodSelector = fmt.Sprintf("%s=%s", constants.AppLabel, constants.OSMControllerName)
3837
bookThiefSelector = fmt.Sprintf("%s=%s", constants.AppLabel, bookThiefLabel)
3938
bookBuyerSelector = fmt.Sprintf("%s=%s", constants.AppLabel, bookBuyerLabel)
40-
bookstoreV1Selector = fmt.Sprintf("%s=%s", constants.AppLabel, bookstoreV1Label)
41-
bookstoreV2Selector = fmt.Sprintf("%s=%s", constants.AppLabel, bookstoreV2Label)
39+
bookstoreV1Selector = fmt.Sprintf("%s=%s,version=v1", constants.AppLabel, bookstoreLabel)
40+
bookstoreV2Selector = fmt.Sprintf("%s=%s,version=v2", constants.AppLabel, bookstoreLabel)
4241
bookWarehouseSelector = fmt.Sprintf("%s=%s", constants.AppLabel, bookWarehouseLabel)
4342
mySQLSelector = fmt.Sprintf("%s=%s", constants.AppLabel, mySQLLabel)
4443

@@ -61,7 +60,7 @@ var (
6160
)
6261

6362
func main() {
64-
log.Debug().Msgf("Looking for: %s/%s, %s/%s, %s/%s, %s/%s, %s/%s %s/%s", bookBuyerLabel, bookbuyerNS, bookThiefLabel, bookthiefNS, bookstoreV1Label, bookstoreNS, bookstoreV2Label, bookstoreNS, bookWarehouseLabel, bookWarehouseNS, mySQLLabel, bookWarehouseNS)
63+
log.Debug().Msgf("Looking for: %s/%s, %s/%s, %s/%s, %s/%s %s/%s", bookBuyerLabel, bookbuyerNS, bookThiefLabel, bookthiefNS, bookstoreLabel, bookstoreNS, bookWarehouseLabel, bookWarehouseNS, mySQLLabel, bookWarehouseNS)
6564

6665
kubeClient := maestro.GetKubernetesClient()
6766

demo/deploy-bookstore.sh

+7-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ KUBE_CONTEXT=$(kubectl config current-context)
1212

1313
kubectl delete deployment "$SVC" -n "$BOOKSTORE_NAMESPACE" --ignore-not-found
1414

15-
echo -e "Deploy bookstore Service"
15+
echo -e "Deploy root bookstore Service"
1616
kubectl apply -f - <<EOF
1717
apiVersion: v1
1818
kind: Service
@@ -53,14 +53,15 @@ metadata:
5353
name: $SVC
5454
namespace: $BOOKSTORE_NAMESPACE
5555
labels:
56-
app: $SVC
56+
app: bookstore
57+
version: $VERSION
5758
spec:
5859
ports:
5960
- port: 14001
6061
name: bookstore-port
61-
6262
selector:
63-
app: $SVC
63+
app: bookstore
64+
version: $VERSION
6465
EOF
6566

6667
echo -e "Deploy $SVC Deployment"
@@ -74,12 +75,12 @@ spec:
7475
replicas: 1
7576
selector:
7677
matchLabels:
77-
app: $SVC
78+
app: bookstore
7879
version: $VERSION
7980
template:
8081
metadata:
8182
labels:
82-
app: $SVC
83+
app: bookstore
8384
version: $VERSION
8485
spec:
8586
serviceAccountName: "$SVC"

pkg/catalog/fake/fake.go

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ func NewFakeMeshCatalog(kubeClient kubernetes.Interface, meshConfigClient config
111111
mockKubeController.EXPECT().ListServiceIdentitiesForService(tests.BookstoreV1Service).Return([]identity.K8sServiceAccount{tests.BookstoreServiceAccount}, nil).AnyTimes()
112112
mockKubeController.EXPECT().ListServiceIdentitiesForService(tests.BookstoreV2Service).Return([]identity.K8sServiceAccount{tests.BookstoreV2ServiceAccount}, nil).AnyTimes()
113113
mockKubeController.EXPECT().ListServiceIdentitiesForService(tests.BookbuyerService).Return([]identity.K8sServiceAccount{tests.BookbuyerServiceAccount}, nil).AnyTimes()
114+
mockKubeController.EXPECT().GetTargetPortForServicePort(
115+
gomock.Any(), gomock.Any()).Return(uint16(tests.ServicePort), nil).AnyTimes()
114116

115117
mockPolicyController.EXPECT().ListEgressPoliciesForSourceIdentity(gomock.Any()).Return(nil).AnyTimes()
116118
mockPolicyController.EXPECT().GetIngressBackendPolicy(gomock.Any()).Return(nil).AnyTimes()

pkg/catalog/outbound_traffic_policies.go

+13-64
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package catalog
22

33
import (
44
mapset "github.com/deckarep/golang-set"
5+
"k8s.io/apimachinery/pkg/types"
56

67
"github.com/openservicemesh/osm/pkg/constants"
78
"github.com/openservicemesh/osm/pkg/errcode"
@@ -34,7 +35,7 @@ func (mc *MeshCatalog) GetOutboundMeshTrafficPolicy(downstreamIdentity identity.
3435

3536
// For each service, build the traffic policies required to access it.
3637
// It is important to aggregate HTTP route configs by the service's port.
37-
for _, meshSvc := range mc.listAllowedUpstreamServicesIncludeApex(downstreamIdentity) {
38+
for _, meshSvc := range mc.ListOutboundServicesForIdentity(downstreamIdentity) {
3839
meshSvc := meshSvc // To prevent loop variable memory aliasing in for loop
3940

4041
// Retrieve the destination IP address from the endpoints for this service
@@ -72,12 +73,20 @@ func (mc *MeshCatalog) GetOutboundMeshTrafficPolicy(downstreamIdentity identity.
7273
if len(trafficSplits) != 0 {
7374
// Program routes to the backends specified in the traffic split
7475
split := trafficSplits[0] // TODO(#2759): support multiple traffic splits per apex service
76+
7577
for _, backend := range split.Spec.Backends {
7678
backendMeshSvc := service.MeshService{
77-
Namespace: meshSvc.Namespace, // Backends belong to the same namespace as the apex service
78-
Name: backend.Service,
79-
TargetPort: meshSvc.TargetPort,
79+
Namespace: meshSvc.Namespace, // Backends belong to the same namespace as the apex service
80+
Name: backend.Service,
81+
}
82+
targetPort, err := mc.kubeController.GetTargetPortForServicePort(
83+
types.NamespacedName{Namespace: backendMeshSvc.Namespace, Name: backendMeshSvc.Name}, meshSvc.Port)
84+
if err != nil {
85+
log.Error().Err(err).Msgf("Error fetching target port for leaf service %s, ignoring it", backendMeshSvc)
86+
continue
8087
}
88+
backendMeshSvc.TargetPort = targetPort
89+
8190
wc := service.WeightedCluster{
8291
ClusterName: service.ClusterName(backendMeshSvc.EnvoyClusterName()),
8392
Weight: backend.Weight,
@@ -166,63 +175,3 @@ func (mc *MeshCatalog) ListOutboundServicesForIdentity(serviceIdentity identity.
166175

167176
return allowedServices
168177
}
169-
170-
// listAllowedUpstreamServicesIncludeApex returns a list of services the given downstream service identity
171-
// is authorized to communicate with, including traffic split apex services that are not backed by
172-
// pods as well as other sibling pods from the same headless service.
173-
func (mc *MeshCatalog) listAllowedUpstreamServicesIncludeApex(downstreamIdentity identity.ServiceIdentity) []service.MeshService {
174-
upstreamServices := mc.ListOutboundServicesForIdentity(downstreamIdentity)
175-
if len(upstreamServices) == 0 {
176-
log.Debug().Msgf("Downstream identity %s does not have any allowed upstream services", downstreamIdentity)
177-
return nil
178-
}
179-
180-
dstServicesSet := make(map[service.MeshService]struct{}) // mapset to avoid duplicates
181-
for _, upstreamSvc := range upstreamServices {
182-
// All upstreams with an endpoint are expected to have TargetPort set.
183-
// Only a TrafficSplit apex service (virtual service) that does not have endpoints
184-
// will have an unset TargetPort. We will not include such a service in the initial
185-
// set because it will be correctly added to the set later on when each upstream
186-
// service is matched to a TrafficSplit object. This is important to avoid duplicate
187-
// TrafficSplit apex/virtual service from being computed with and without TargetPort set.
188-
if upstreamSvc.TargetPort != 0 {
189-
dstServicesSet[upstreamSvc] = struct{}{}
190-
}
191-
}
192-
193-
// Getting apex services referring to the outbound services
194-
// We get possible apexes which could traffic split to any of the possible
195-
// outbound services
196-
splitPolicy := mc.meshSpec.ListTrafficSplits()
197-
198-
for upstreamSvc := range dstServicesSet {
199-
for _, split := range splitPolicy {
200-
// Split policy must be in the same namespace as the upstream service that is a backend
201-
if split.Namespace != upstreamSvc.Namespace {
202-
continue
203-
}
204-
for _, backend := range split.Spec.Backends {
205-
if backend.Service == upstreamSvc.Name {
206-
rootServiceName := k8s.GetServiceFromHostname(mc.kubeController, split.Spec.Service)
207-
rootMeshService := service.MeshService{
208-
Namespace: split.Namespace,
209-
Name: rootServiceName,
210-
Port: upstreamSvc.Port,
211-
TargetPort: upstreamSvc.TargetPort,
212-
Protocol: upstreamSvc.Protocol,
213-
}
214-
215-
// Add this root service into the set
216-
dstServicesSet[rootMeshService] = struct{}{}
217-
}
218-
}
219-
}
220-
}
221-
222-
dstServices := make([]service.MeshService, 0, len(dstServicesSet))
223-
for svc := range dstServicesSet {
224-
dstServices = append(dstServices, svc)
225-
}
226-
227-
return dstServices
228-
}

0 commit comments

Comments
 (0)