Skip to content

Commit

Permalink
add support for alternate stat name for clusters (istio#16654)
Browse files Browse the repository at this point in the history
* initial implementation

Signed-off-by: Rama Chavali <[email protected]>

* add stat name support in mesh config

Signed-off-by: Rama Chavali <[email protected]>

* go lint fixes

Signed-off-by: Rama Chavali <[email protected]>

* fix test case

Signed-off-by: Rama Chavali <[email protected]>

* address review comments

Signed-off-by: Rama Chavali <[email protected]>
  • Loading branch information
ramaraochavali authored and istio-testing committed Aug 30, 2019
1 parent dc7a3e7 commit ee134b6
Show file tree
Hide file tree
Showing 13 changed files with 612 additions and 429 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ require (
gopkg.in/square/go-jose.v2 v2.3.1
gopkg.in/stack.v1 v1.7.0 // indirect
gopkg.in/yaml.v2 v2.2.2
istio.io/api v0.0.0-20190827203453-55bf99d69a1f
istio.io/api v0.0.0-20190829032130-adb6f9e24baf
istio.io/gogo-genproto v0.0.0-20190731221249-06e20ada0df2
istio.io/operator v0.0.0-20190827173952-af847b66d2f9
istio.io/pkg v0.0.0-20190813222952-67b4f108fe0e
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -757,8 +757,8 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
istio.io/api v0.0.0-20190405142752-6b8d1849e7f4/go.mod h1:hhLFQmpHia8zgaM37vb2ml9iS5NfNfqZGRt1pS9aVEo=
istio.io/api v0.0.0-20190515205759-982e5c3888c6/go.mod h1:hhLFQmpHia8zgaM37vb2ml9iS5NfNfqZGRt1pS9aVEo=
istio.io/api v0.0.0-20190827203453-55bf99d69a1f h1:hdbHcdj0Hmsjdqu8mNQFuNe/KY3EIVoTBmHZ0SghhU0=
istio.io/api v0.0.0-20190827203453-55bf99d69a1f/go.mod h1:42cBjnu/rTJcCaKi8nLdIvq0n71RcLrkgZ9IQSvDdSQ=
istio.io/api v0.0.0-20190829032130-adb6f9e24baf h1:S4yAxvS9U8a0wiQSJrsQ3O5+i5ct4WobXux6DR/+FZk=
istio.io/api v0.0.0-20190829032130-adb6f9e24baf/go.mod h1:42cBjnu/rTJcCaKi8nLdIvq0n71RcLrkgZ9IQSvDdSQ=
istio.io/gogo-genproto v0.0.0-20190731221249-06e20ada0df2 h1:AZ+aTgKSBmBc6KtZU+P+Wr2dOdPriJu09cU8wGMG+/M=
istio.io/gogo-genproto v0.0.0-20190731221249-06e20ada0df2/go.mod h1:IjvrbUlRbbw4JCpsgvgihcz9USUwEoNTL/uwMtyV5yk=
istio.io/operator v0.0.0-20190827173952-af847b66d2f9 h1:lXYXdziz/jPdLue45aGwStjKR1hxU1ufCVT4v7adJPk=
Expand Down
43 changes: 40 additions & 3 deletions pilot/pkg/networking/core/v1alpha3/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ const (

// ManagementClusterHostname indicates the hostname used for building inbound clusters for management ports
ManagementClusterHostname = "mgmtCluster"

// StatName patterns
serviceStatPattern = "%SERVICE%"
serviceFQDNStatPattern = "%SERVICE_FQDN%"
servicePortStatPattern = "%SERVICE_PORT%"
servicePortNameStatPattern = "%SERVICE_PORT_NAME%"
subsetNameStatPattern = "%SUBSET_NAME%"
)

var (
Expand Down Expand Up @@ -166,6 +173,10 @@ func (configgen *ConfigGeneratorImpl) buildOutboundClusters(env *model.Environme
clusterName := model.BuildSubsetKey(model.TrafficDirectionOutbound, "", service.Hostname, port.Port)
serviceAccounts := push.ServiceAccounts[service.Hostname][port.Port]
defaultCluster := buildDefaultCluster(env, clusterName, discoveryType, lbEndpoints, model.TrafficDirectionOutbound, proxy, port)
// If stat name is configured, build the alternate stats name.
if len(env.Mesh.OutboundClusterStatName) != 0 {
defaultCluster.AltStatName = altStatName(env.Mesh.OutboundClusterStatName, string(service.Hostname), "", proxy.DNSDomain, port)
}

setUpstreamProtocol(defaultCluster, port)
clusters = append(clusters, defaultCluster)
Expand Down Expand Up @@ -197,6 +208,9 @@ func (configgen *ConfigGeneratorImpl) buildOutboundClusters(env *model.Environme
lbEndpoints = buildLocalityLbEndpoints(env, networkView, service, port.Port, []labels.Instance{subset.Labels})
}
subsetCluster := buildDefaultCluster(env, subsetClusterName, discoveryType, lbEndpoints, model.TrafficDirectionOutbound, proxy, nil)
if len(env.Mesh.OutboundClusterStatName) != 0 {
subsetCluster.AltStatName = altStatName(env.Mesh.OutboundClusterStatName, string(service.Hostname), subset.Name, proxy.DNSDomain, port)
}
setUpstreamProtocol(subsetCluster, port)

opts := buildClusterOpts{
Expand Down Expand Up @@ -600,6 +614,11 @@ func (configgen *ConfigGeneratorImpl) buildInboundClusterForPortOrUDS(pluginPara
localityLbEndpoints := buildInboundLocalityLbEndpoints(pluginParams.Bind, instance.Endpoint.Port)
localCluster := buildDefaultCluster(pluginParams.Env, clusterName, apiv2.Cluster_STATIC, localityLbEndpoints,
model.TrafficDirectionInbound, pluginParams.Node, nil)
// If stat name is configured, build the alt statname.
if len(pluginParams.Env.Mesh.InboundClusterStatName) != 0 {
localCluster.AltStatName = altStatName(pluginParams.Env.Mesh.InboundClusterStatName,
string(instance.Service.Hostname), "", pluginParams.Node.DNSDomain, instance.Endpoint.ServicePort)
}
setUpstreamProtocol(localCluster, instance.Endpoint.ServicePort)
// call plugins
for _, p := range configgen.Plugins {
Expand Down Expand Up @@ -901,7 +920,7 @@ func applyLoadBalancer(cluster *apiv2.Cluster, lb *networking.LoadBalancerSettin
return
}

// The following order important. If cluster type has been identified as Original DST since Resolution is PassThrough,
// The following order is important. If cluster type has been identified as Original DST since Resolution is PassThrough,
// and port is named as redis-xxx we end up creating a cluster with type Original DST and LbPolicy as MAGLEV which would be
// rejected by Envoy.

Expand All @@ -917,6 +936,7 @@ func applyLoadBalancer(cluster *apiv2.Cluster, lb *networking.LoadBalancerSettin
return
}

// DO not do if else here. since lb.GetSimple returns a enum value (not pointer).
switch lb.GetSimple() {
case networking.LoadBalancerSettings_LEAST_CONN:
cluster.LbPolicy = apiv2.Cluster_LEAST_REQUEST
Expand All @@ -929,8 +949,6 @@ func applyLoadBalancer(cluster *apiv2.Cluster, lb *networking.LoadBalancerSettin
cluster.ClusterDiscoveryType = &apiv2.Cluster_Type{Type: apiv2.Cluster_ORIGINAL_DST}
}

// DO not do if else here. since lb.GetSimple returns a enum value (not pointer).

consistentHash := lb.GetConsistentHash()
if consistentHash != nil {
// TODO MinimumRingSize is an int, and zero could potentially be a valid value
Expand Down Expand Up @@ -1171,6 +1189,25 @@ func buildDefaultTrafficPolicy(env *model.Environment, discoveryType apiv2.Clust
}
}

func altStatName(statPattern string, host string, subset string, dnsDomain string, port *model.Port) string {
name := strings.ReplaceAll(statPattern, serviceStatPattern, shortHostName(host, dnsDomain))
name = strings.ReplaceAll(name, serviceFQDNStatPattern, host)
name = strings.ReplaceAll(name, subsetNameStatPattern, subset)
name = strings.ReplaceAll(name, servicePortStatPattern, strconv.Itoa(port.Port))
name = strings.ReplaceAll(name, servicePortNameStatPattern, port.Name)
return name
}

func shortHostName(host string, dnsDomain string) string {
shortHost := strings.TrimSuffix(host, dnsDomain)
if parts := strings.Split(dnsDomain, "."); len(parts) > 1 {
shortHost += parts[0] // k8s will have namespace.<domain>
} else {
shortHost = host
}
return shortHost
}

func lbPolicyClusterProvided(proxy *model.Proxy) apiv2.Cluster_LbPolicy {
if util.IsIstioVersionGE13(proxy) {
return apiv2.Cluster_CLUSTER_PROVIDED
Expand Down
143 changes: 143 additions & 0 deletions pilot/pkg/networking/core/v1alpha3/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,28 @@ func TestDisablePanicThresholdAsDefault(t *testing.T) {
}
}

func TestStatNamePattern(t *testing.T) {
g := NewGomegaWithT(t)

statConfigMesh := meshconfig.MeshConfig{
ConnectTimeout: &types.Duration{
Seconds: 10,
Nanos: 1,
},
InboundClusterStatName: "LocalService_%SERVICE%",
OutboundClusterStatName: "%SERVICE%_%SERVICE_PORT_NAME%_%SERVICE_PORT%",
}

clusters, err := buildTestClusters("*.example.org", model.DNSLB, model.SidecarProxy,
&core.Locality{}, statConfigMesh,
&networking.DestinationRule{
Host: "*.example.org",
})
g.Expect(err).NotTo(HaveOccurred())
g.Expect(clusters[0].AltStatName).To(Equal("*.example.org_default_8080"))
g.Expect(clusters[3].AltStatName).To(Equal("LocalService_*.example.org"))
}

func TestLocalityLB(t *testing.T) {
g := NewGomegaWithT(t)
// Distribute locality loadbalancing setting
Expand Down Expand Up @@ -1064,3 +1086,124 @@ func TestRedisProtocolCluster(t *testing.T) {
}
}
}

func TestAltStatName(t *testing.T) {
tests := []struct {
name string
statPattern string
host string
subsetName string
dnsDomain string
port *model.Port
want string
}{
{
"Service only pattern",
"%SERVICE%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default",
},
{
"Service FQDN only pattern",
"%SERVICE_FQDN%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default.svc.cluster.local",
},
{
"Service With Port pattern",
"%SERVICE%_%SERVICE_PORT%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default_7443",
},
{
"Service With Port Name pattern",
"%SERVICE%_%SERVICE_PORT_NAME%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default_grpc-svc",
},
{
"Service With Port and Port Name pattern",
"%SERVICE%_%SERVICE_PORT_NAME%_%SERVICE_PORT%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default_grpc-svc_7443",
},
{
"Service FQDN With Port pattern",
"%SERVICE_FQDN%_%SERVICE_PORT%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default.svc.cluster.local_7443",
},
{
"Service FQDN With Port Name pattern",
"%SERVICE_FQDN%_%SERVICE_PORT_NAME%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default.svc.cluster.local_grpc-svc",
},
{
"Service FQDN With Port and Port Name pattern",
"%SERVICE_FQDN%_%SERVICE_PORT_NAME%_%SERVICE_PORT%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default.svc.cluster.local_grpc-svc_7443",
},
{
"Service FQDN With Empty Subset, Port and Port Name pattern",
"%SERVICE_FQDN%%SUBSET_NAME%_%SERVICE_PORT_NAME%_%SERVICE_PORT%",
"reviews.default.svc.cluster.local",
"",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default.svc.cluster.local_grpc-svc_7443",
},
{
"Service FQDN With Subset, Port and Port Name pattern",
"%SERVICE_FQDN%.%SUBSET_NAME%.%SERVICE_PORT_NAME%_%SERVICE_PORT%",
"reviews.default.svc.cluster.local",
"v1",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default.svc.cluster.local.v1.grpc-svc_7443",
},
{
"Service FQDN With Unknown Pattern",
"%SERVICE_FQDN%.%DUMMY%",
"reviews.default.svc.cluster.local",
"v1",
"default.svc.cluster.local",
&model.Port{Name: "grpc-svc", Port: 7443, Protocol: "GRPC"},
"reviews.default.svc.cluster.local.%DUMMY%",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := altStatName(tt.statPattern, tt.host, tt.subsetName, tt.dnsDomain, tt.port)
if got != tt.want {
t.Errorf("Expected alt statname %s, but got %s", tt.want, got)
}
})
}
}
Loading

0 comments on commit ee134b6

Please sign in to comment.