Skip to content

Commit

Permalink
synthesise k8s service network from service IPs
Browse files Browse the repository at this point in the history
This prevents cluttering host.LocalNetworks with lots of /32
addresses. These were unsightly and rather distracting in the UI. They
also bloated the report and slowed down server-side rendering.

Fixes #2748.
  • Loading branch information
rade committed Aug 1, 2017
1 parent d7c1bfb commit 12add72
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 14 deletions.
38 changes: 24 additions & 14 deletions probe/kubernetes/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kubernetes

import (
"fmt"
"net"
"strings"

"k8s.io/apimachinery/pkg/labels"
Expand Down Expand Up @@ -294,23 +295,32 @@ func (r *Reporter) serviceTopology() (report.Topology, []Service, error) {
return result, services, err
}

// FIXME: Hideous hack to remove persistent-connection edges to virtual service
// IPs attributed to the internet. We add each service IP as a /32 network
// (the global service-cluster-ip-range is not exposed by the API
// server so we treat each IP as a /32 network see
// https://github.com/kubernetes/kubernetes/issues/25533).
// The right way of fixing this is performing DNAT mapping on persistent
// connections for which we don't have a robust solution
// (see https://github.com/weaveworks/scope/issues/1491)
// FIXME: Hideous hack to remove persistent-connection edges to
// virtual service IPs attributed to the internet. The global
// service-cluster-ip-range is not exposed by the API server (see
// https://github.com/kubernetes/kubernetes/issues/25533), so instead
// we synthesise it by computing the smallest network that contains
// all service IPs. That network may be smaller than the actual range
// but that is ok, since in the end all we care about is that it
// contains all the service IPs.
//
// The right way of fixing this is performing DNAT mapping on
// persistent connections for which we don't have a robust solution
// (see https://github.com/weaveworks/scope/issues/1491).
func (r *Reporter) hostTopology(services []Service) report.Topology {
localNetworks := report.MakeStringSet()
serviceIPs := make([]net.IP, 0, len(services))
for _, service := range services {
localNetworks = localNetworks.Add(service.ClusterIP() + "/32")
if ip := net.ParseIP(service.ClusterIP()).To4(); ip != nil {
serviceIPs = append(serviceIPs, ip)
}
}
serviceNetwork := report.ContainingIPv4Network(serviceIPs)
if serviceNetwork == nil {
return report.MakeTopology()
}
node := report.MakeNode(report.MakeHostNodeID(r.hostID))
node = node.WithSets(report.MakeSets().
Add(host.LocalNetworks, localNetworks))
return report.MakeTopology().AddNode(node)
return report.MakeTopology().AddNode(
report.MakeNode(report.MakeHostNodeID(r.hostID)).
WithSets(report.MakeSets().Add(host.LocalNetworks, report.MakeStringSet(serviceNetwork.String()))))
}

func (r *Reporter) deploymentTopology(probeID string) (report.Topology, []Deployment, error) {
Expand Down
29 changes: 29 additions & 0 deletions report/networks.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package report

import (
"encoding/binary"
"net"
"strings"

Expand Down Expand Up @@ -105,3 +106,31 @@ func ipv4Nets(addrs []net.Addr) []*net.IPNet {
}
return nets
}

// ContainingIPv4Network determines the smallest network containing
// the given IPv4 addresses. When no addresses are specified, nil is
// returned.
func ContainingIPv4Network(ips []net.IP) *net.IPNet {
if len(ips) == 0 {
return nil
}
network := net.IPNet{
IP: ips[0],
Mask: net.CIDRMask(net.IPv4len*8, net.IPv4len*8),
}
for _, ip := range ips[1:] {
network.Mask = net.CIDRMask(commonIPv4PrefixLen(network.IP, ip), net.IPv4len*8)
network.IP = network.IP.Mask(network.Mask)
}
return &network
}

func commonIPv4PrefixLen(a, b net.IP) (cpl int) {
x := binary.BigEndian.Uint32(a)
y := binary.BigEndian.Uint32(b)
for cpl = 32; x != y; cpl-- {
x >>= 1
y >>= 1
}
return
}
16 changes: 16 additions & 0 deletions report/networks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net"
"testing"

"github.com/stretchr/testify/assert"
"github.com/weaveworks/scope/report"
)

Expand All @@ -23,3 +24,18 @@ func TestContains(t *testing.T) {
t.Errorf("10.0.0.1 in %v", networks)
}
}

func TestContainingIPv4Network(t *testing.T) {
assert.Nil(t, containingIPv4Networks([]string{}))
assert.Equal(t, "10.0.0.1/32", containingIPv4Networks([]string{"10.0.0.1"}).String())
assert.Equal(t, "10.0.0.0/17", containingIPv4Networks([]string{"10.0.0.1", "10.0.2.55", "10.0.106.48"}).String())
assert.Equal(t, "0.0.0.0/0", containingIPv4Networks([]string{"10.0.0.1", "192.168.0.1"}).String())
}

func containingIPv4Networks(ipstrings []string) *net.IPNet {
ips := make([]net.IP, len(ipstrings))
for i, ip := range ipstrings {
ips[i] = net.ParseIP(ip).To4()
}
return report.ContainingIPv4Network(ips)
}

0 comments on commit 12add72

Please sign in to comment.