Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support gateway service name in meshNetworks #13392

Merged
merged 8 commits into from May 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions install/kubernetes/helm/istio/values.yaml
Expand Up @@ -443,8 +443,9 @@ global:
# The second network, `network2`, in this example is defined differently with all endpoints
# retrieved through the specified Multi-Cluster registry being mapped to network2. The
# gateway is also defined differently with the name of the gateway service on the remote
# cluster. The public IP for the gateway will be determined from that remote service (not
# supported yet).
# cluster. The public IP for the gateway will be determined from that remote service (only
# LoadBalancer gateway service type is currently supported, for a NodePort type gateway service,
# it still need to be configured manually).
#
# meshNetworks:
# network1:
Expand All @@ -457,7 +458,7 @@ global:
# endpoints:
# - fromRegistry: reg1
# gateways:
# - registryServiceName: istio-ingressgateway
# - registryServiceName: istio-ingressgateway.istio-system.svc.cluster.local
# port: 443
#
meshNetworks: {}
Expand Down
11 changes: 9 additions & 2 deletions pilot/pkg/model/service.go
Expand Up @@ -61,13 +61,12 @@ type Service struct {
// ClusterVIPs specifies the service address of the load balancer
// in each of the clusters where the service resides
ClusterVIPs map[string]string `json:"cluster-vips,omitempty"`

// Ports is the set of network ports where the service is listening for
// connections
Ports PortList `json:"ports,omitempty"`

// ServiceAccounts specifies the service accounts that run the service.
ServiceAccounts []string `json:"serviceaccounts,omitempty"`
ServiceAccounts []string `json:"serviceAccounts,omitempty"`

// MeshExternal (if true) indicates that the service is external to the mesh.
// These services are defined using Istio's ServiceEntry spec.
Expand Down Expand Up @@ -465,6 +464,14 @@ type ServiceAttributes struct {
// ExportTo defines the visibility of Service in
// a namespace when the namespace is imported.
ExportTo map[Visibility]bool

// For Kubernetes platform

// ClusterExternalAddresses is a mapping between a cluster name and the external
// address(es) to access the service from outside the cluster.
// Used by the aggregator to aggregate the Attributes.ClusterExternalAddresses
// for clusters where the service resides
ClusterExternalAddresses map[string][]string
}

// ServiceDiscovery enumerates Istio service instances.
Expand Down
84 changes: 58 additions & 26 deletions pilot/pkg/proxy/envoy/v2/ep_filters.go
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"
"github.com/gogo/protobuf/types"

"istio.io/api/mesh/v1alpha1"
"istio.io/istio/pilot/pkg/model"
"istio.io/istio/pilot/pkg/networking/util"
)
Expand All @@ -44,10 +45,10 @@ func EndpointsByNetworkFilter(endpoints []endpoint.LocalityLbEndpoints, conn *Xd
multiples := 1
for _, network := range env.MeshNetworks.Networks {
num := 0
registryName := getNetworkRegistry(network)
for _, gw := range network.Gateways {
if gwIP := net.ParseIP(gw.GetAddress()); gwIP != nil {
num++
}
addrs := getGatewayAddresses(gw, registryName, env)
num += len(addrs)
}
if num > 1 {
multiples *= num
Expand Down Expand Up @@ -85,44 +86,44 @@ func EndpointsByNetworkFilter(endpoints []endpoint.LocalityLbEndpoints, conn *Xd
// Iterate over all networks that have the cluster endpoint (weight>0) and
// for each one of those add a new endpoint that points to the network's
// gateway with the relevant weight
for n, w := range remoteEps {
networkConf, found := env.MeshNetworks.Networks[n]
for network, w := range remoteEps {
networkConf, found := env.MeshNetworks.Networks[network]
if !found {
adsLog.Debugf("the endpoints within network %s will be ignored for no network configured", n)
adsLog.Debugf("the endpoints within network %s will be ignored for no network configured", network)
continue
}
gws := networkConf.Gateways
if len(gws) == 0 {
adsLog.Debugf("the endpoints within network %s will be ignored for no gateways configured", n)
adsLog.Debugf("the endpoints within network %s will be ignored for no gateways configured", network)
continue
}

registryName := getNetworkRegistry(networkConf)
gwEps := []endpoint.LbEndpoint{}
// There may be multiples gateways for the network. Add an LbEndpoint for
// each one of them
for _, gw := range gws {
var gwEp *endpoint.LbEndpoint
//TODO add support for getting the gateway address from the service registry

// If the gateway address in the config was a hostname it got already resolved
// and replaced with an IP address when loading the config
if gwIP := net.ParseIP(gw.GetAddress()); gwIP != nil {
addr := util.BuildAddress(gw.GetAddress(), gw.Port)
gwEp = &endpoint.LbEndpoint{
HostIdentifier: &endpoint.LbEndpoint_Endpoint{
Endpoint: &endpoint.Endpoint{
Address: &addr,
gwAddresses := getGatewayAddresses(gw, registryName, env)
// If gateway addresses are found, create an endpoint for each one of them
if len(gwAddresses) > 0 {
for _, gwAddr := range gwAddresses {
epAddr := util.BuildAddress(gwAddr, gw.Port)
hzxuzhonghu marked this conversation as resolved.
Show resolved Hide resolved
gwEp := &endpoint.LbEndpoint{
HostIdentifier: &endpoint.LbEndpoint_Endpoint{
Endpoint: &endpoint.Endpoint{
Address: &epAddr,
},
},
LoadBalancingWeight: &types.UInt32Value{
Value: w,
},
},
LoadBalancingWeight: &types.UInt32Value{
Value: w,
},
}
gwEps = append(gwEps, *gwEp)
}
}

if gwEp != nil {
gwEps = append(gwEps, *gwEp)
}
}
if len(gwEps) == 0 {
continue
}
weight := w * uint32(multiples/len(gwEps))
for _, gwEp := range gwEps {
Expand Down Expand Up @@ -180,3 +181,34 @@ func createLocalityLbEndpoints(base *endpoint.LocalityLbEndpoints, lbEndpoints [
func LoadBalancingWeightNormalize(endpoints []endpoint.LocalityLbEndpoints) []endpoint.LocalityLbEndpoints {
return util.LocalityLbWeightNormalize(endpoints)
}

func getNetworkRegistry(network *v1alpha1.Network) string {
var registryName string
for _, eps := range network.Endpoints {
if eps != nil && len(eps.GetFromRegistry()) > 0 {
registryName = eps.GetFromRegistry()
break
}
}

return registryName
}

func getGatewayAddresses(gw *v1alpha1.Network_IstioNetworkGateway, registryName string, env *model.Environment) []string {
// First, if a gateway address is provided in the configuration use it. If the gateway address
// in the config was a hostname it got already resolved and replaced with an IP address
// when loading the config
if gwIP := net.ParseIP(gw.GetAddress()); gwIP != nil {
return []string{gw.GetAddress()}
}

// Second, try to find the gateway addresses by the provided service name
if gwSvcName := gw.GetRegistryServiceName(); len(gwSvcName) > 0 && len(registryName) > 0 {
svc, _ := env.GetService(model.Hostname(gwSvcName))
if svc != nil {
return svc.Attributes.ClusterExternalAddresses[registryName]
}
}

return nil
}
174 changes: 171 additions & 3 deletions pilot/pkg/proxy/envoy/v2/ep_filters_test.go
Expand Up @@ -42,7 +42,7 @@ func TestEndpointsByNetworkFilter(t *testing.T) {

// Environment defines the networks with:
// - 1 gateway for network1
// - 1 gateway for network2
// - 2 gateway for network2
// - 1 gateway for network3
// - 0 gateways for network4
env := environment()
Expand Down Expand Up @@ -179,6 +179,174 @@ func TestEndpointsByNetworkFilter(t *testing.T) {
}
}

func TestEndpointsByNetworkFilter_RegistryServiceName(t *testing.T) {

// - 1 gateway for network1
// - 1 gateway for network2
// - 1 gateway for network3
// - 0 gateways for network4
env := environment()
env.MeshNetworks.Networks["network2"] = &meshconfig.Network{
Endpoints: []*meshconfig.Network_NetworkEndpoints{
{
Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{
FromRegistry: "cluster2",
},
},
},
Gateways: []*meshconfig.Network_IstioNetworkGateway{
{
Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{
RegistryServiceName: "istio-ingressgateway.istio-system.svc.cluster.local",
},
Port: 80,
},
},
}

gwSvcName := model.Hostname("istio-ingressgateway.istio-system.svc.cluster.local")
serviceDiscovery := NewMemServiceDiscovery(map[model.Hostname]*model.Service{
gwSvcName: {
Hostname: gwSvcName,
Attributes: model.ServiceAttributes{
ClusterExternalAddresses: map[string][]string{
"cluster2": {"2.2.2.2"},
},
},
},
}, 0)

env.ServiceDiscovery = serviceDiscovery

// Test endpoints creates:
// - 2 endpoints in network1
// - 1 endpoints in network2
// - 0 endpoints in network3
// - 1 endpoints in network4
testEndpoints := testEndpoints()

// The tests below are calling the endpoints filter from each one of the
// networks and examines the returned filtered endpoints
tests := []struct {
name string
endpoints []endpoint.LocalityLbEndpoints
conn *XdsConnection
env *model.Environment
want []LocLbEpInfo
}{
{
name: "from_network1",
conn: xdsConnection("network1"),
env: env,
endpoints: testEndpoints,
want: []LocLbEpInfo{
{
lbEps: []LbEpInfo{
// 2 local endpoints
{address: "10.0.0.1", weight: 1},
{address: "10.0.0.2", weight: 1},
// 1 endpoint to gateway of network2 with weight 1 because it has 1 endpoint
{address: "2.2.2.2", weight: 1},
},
weight: 3,
},
},
},
{
name: "from_network2",
conn: xdsConnection("network2"),
env: env,
endpoints: testEndpoints,
want: []LocLbEpInfo{
{
lbEps: []LbEpInfo{
// 1 local endpoint
{address: "20.0.0.1", weight: 1},
// 1 endpoint to gateway of network1 with weight 2 because it has 2 endpoints
{address: "1.1.1.1", weight: 2},
},
weight: 3,
},
},
},
{
name: "from_network3",
conn: xdsConnection("network3"),
env: env,
endpoints: testEndpoints,
want: []LocLbEpInfo{
{
lbEps: []LbEpInfo{
// 1 endpoint to gateway of network1 with weight 2 because it has 2 endpoints
{address: "1.1.1.1", weight: 2},
// 1 endpoint to gateway of network2 with weight 1 because it has 1 endpoint
{address: "2.2.2.2", weight: 1},
},
weight: 3,
},
},
},
{
name: "from_network4",
conn: xdsConnection("network4"),
env: env,
endpoints: testEndpoints,
want: []LocLbEpInfo{
{
lbEps: []LbEpInfo{
// 1 local endpoint
{address: "40.0.0.1", weight: 1},
// 1 endpoint to gateway of network1 with weight 2 because it has 2 endpoints
{address: "1.1.1.1", weight: 2},
// 1 endpoint to gateway of network2 with weight 1 because it has 1 endpoint
{address: "2.2.2.2", weight: 1},
},
weight: 4,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filtered := EndpointsByNetworkFilter(tt.endpoints, tt.conn, tt.env)
if len(filtered) != len(tt.want) {
t.Errorf("Unexpected number of filtered endpoints: got %v, want %v", len(filtered), len(tt.want))
return
}

sort.Slice(filtered, func(i, j int) bool {
addrI := filtered[i].LbEndpoints[0].GetEndpoint().Address.GetSocketAddress().Address
addrJ := filtered[j].LbEndpoints[0].GetEndpoint().Address.GetSocketAddress().Address
return addrI < addrJ
})

for i, ep := range filtered {
if len(ep.LbEndpoints) != len(tt.want[i].lbEps) {
t.Errorf("Unexpected number of LB endpoints within endpoint %d: %v, want %v", i, len(ep.LbEndpoints), len(tt.want[i].lbEps))
}

if ep.LoadBalancingWeight.GetValue() != tt.want[i].weight {
t.Errorf("Unexpected weight for endpoint %d: got %v, want %v", i, ep.LoadBalancingWeight.GetValue(), tt.want[i].weight)
}

for _, lbEp := range ep.LbEndpoints {
addr := lbEp.GetEndpoint().Address.GetSocketAddress().Address
found := false
for _, wantLbEp := range tt.want[i].lbEps {
if addr == wantLbEp.address {
found = true
break
}
}
if !found {
t.Errorf("Unexpected address for endpoint %d: %v", i, addr)
}
}
}
})
}
}

func xdsConnection(network string) *XdsConnection {
var metadata map[string]string
if network != "" {
Expand All @@ -193,11 +361,12 @@ func xdsConnection(network string) *XdsConnection {

// environment creates an Environment object with the following MeshNetworks configurations:
// - 1 gateway for network1
// - 1 gateway for network2
// - 2 gateway for network2
// - 1 gateway for network3
// - 0 gateways for network4
func environment() *model.Environment {
return &model.Environment{
ServiceDiscovery: NewMemServiceDiscovery(nil, 0),
MeshNetworks: &meshconfig.MeshNetworks{
Networks: map[string]*meshconfig.Network{
"network1": {
Expand Down Expand Up @@ -264,7 +433,6 @@ func testEndpoints() []endpoint.LocalityLbEndpoints {
},
},
}

}

func createLbEndpoints(lbEpsInfo []LbEpInfo) []endpoint.LbEndpoint {
Expand Down