Only use dualstack if the node and config supports it

This commit is contained in:
James Sturtevant
2021-04-12 17:42:07 -07:00
parent b765496650
commit d5d9327351
5 changed files with 215 additions and 79 deletions

View File

@@ -31,13 +31,14 @@ import (
"github.com/Microsoft/hcsshim"
"github.com/Microsoft/hcsshim/hcn"
discovery "k8s.io/api/discovery/v1"
"github.com/davecgh/go-spew/spew"
v1 "k8s.io/api/core/v1"
discovery "k8s.io/api/discovery/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
apiutil "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/tools/events"
@@ -137,6 +138,101 @@ type remoteSubnetInfo struct {
const NETWORK_TYPE_OVERLAY = "overlay"
func newHostNetworkService() (HostNetworkService, hcn.SupportedFeatures) {
var hns HostNetworkService
hns = hnsV1{}
supportedFeatures := hcn.GetSupportedFeatures()
if supportedFeatures.Api.V2 {
hns = hnsV2{}
}
return hns, supportedFeatures
}
func getNetworkName(hnsNetworkName string) (string, error) {
if len(hnsNetworkName) == 0 {
klog.V(3).InfoS("network-name flag not set. Checking environment variable")
hnsNetworkName = os.Getenv("KUBE_NETWORK")
if len(hnsNetworkName) == 0 {
return "", fmt.Errorf("Environment variable KUBE_NETWORK and network-flag not initialized")
}
}
return hnsNetworkName, nil
}
func getNetworkInfo(hns HostNetworkService, hnsNetworkName string) (*hnsNetworkInfo, error) {
hnsNetworkInfo, err := hns.getNetworkByName(hnsNetworkName)
for err != nil {
klog.ErrorS(err, "Unable to find HNS Network specified. Please check network name and CNI deployment", "hnsNetworkName", hnsNetworkName)
time.Sleep(1 * time.Second)
hnsNetworkInfo, err = hns.getNetworkByName(hnsNetworkName)
}
return hnsNetworkInfo, err
}
func isOverlay(hnsNetworkInfo *hnsNetworkInfo) bool {
return strings.EqualFold(hnsNetworkInfo.networkType, NETWORK_TYPE_OVERLAY)
}
// StackCompatTester tests whether the required kernel and network are dualstack capable
type StackCompatTester interface {
DualStackCompatible(networkName string) bool
}
type DualStackCompatTester struct{}
func (t DualStackCompatTester) DualStackCompatible(networkName string) bool {
dualStackFeatureEnabled := utilfeature.DefaultFeatureGate.Enabled(kubefeatures.IPv6DualStack)
if !dualStackFeatureEnabled {
return false
}
globals, err := hcn.GetGlobals()
if err != nil {
klog.ErrorS(err, "Unable to determine networking stack version. Falling back to single-stack")
return false
}
if !kernelSupportsDualstack(globals.Version) {
klog.InfoS("This version of Windows does not support dual-stack. Falling back to single-stack")
return false
}
// check if network is using overlay
hns, _ := newHostNetworkService()
networkName, err = getNetworkName(networkName)
if err != nil {
klog.ErrorS(err, "unable to determine dual-stack status %v. Falling back to single-stack")
return false
}
networkInfo, err := getNetworkInfo(hns, networkName)
if err != nil {
klog.ErrorS(err, "unable to determine dual-stack status %v. Falling back to single-stack")
return false
}
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.WinOverlay) && isOverlay(networkInfo) {
// Overlay (VXLAN) networks on Windows do not support dual-stack networking today
klog.InfoS("Winoverlay does not support dual-stack. Falling back to single-stack")
return false
}
return true
}
// The hcsshim version logic has a bug that did not calculate the versioning of DualStack correctly.
// DualStack is supported in WS 2004+ (10.0.19041+) where HCN component version is 11.10+
// https://github.com/microsoft/hcsshim/pull/1003#issuecomment-827930358
func kernelSupportsDualstack(currentVersion hcn.Version) bool {
hnsVersion := fmt.Sprintf("%d.%d.0", currentVersion.Major, currentVersion.Minor)
v, err := version.ParseSemantic(hnsVersion)
if err != nil {
return false
}
return v.AtLeast(version.MustParseSemantic("11.10.0"))
}
func Log(v interface{}, message string, level klog.Level) {
klog.V(level).InfoS("%s", message, "spewConfig", spewSdump(v))
}
@@ -546,36 +642,24 @@ func NewProxier(
}
serviceHealthServer := healthcheck.NewServiceHealthServer(hostname, recorder)
var hns HostNetworkService
hns = hnsV1{}
supportedFeatures := hcn.GetSupportedFeatures()
if supportedFeatures.Api.V2 {
hns = hnsV2{}
}
hnsNetworkName := config.NetworkName
if len(hnsNetworkName) == 0 {
klog.V(3).InfoS("network-name flag not set. Checking environment variable")
hnsNetworkName = os.Getenv("KUBE_NETWORK")
if len(hnsNetworkName) == 0 {
return nil, fmt.Errorf("Environment variable KUBE_NETWORK and network-flag not initialized")
}
hns, supportedFeatures := newHostNetworkService()
hnsNetworkName, err := getNetworkName(config.NetworkName)
if err != nil {
return nil, err
}
klog.V(3).InfoS("Cleaning up old HNS policy lists")
deleteAllHnsLoadBalancerPolicy()
// Get HNS network information
hnsNetworkInfo, err := hns.getNetworkByName(hnsNetworkName)
for err != nil {
klog.ErrorS(err, "Unable to find HNS Network specified. Please check network name and CNI deployment", "hnsNetworkName", hnsNetworkName)
time.Sleep(1 * time.Second)
hnsNetworkInfo, err = hns.getNetworkByName(hnsNetworkName)
hnsNetworkInfo, err := getNetworkInfo(hns, hnsNetworkName)
if err != nil {
return nil, err
}
// Network could have been detected before Remote Subnet Routes are applied or ManagementIP is updated
// Sleep and update the network to include new information
if strings.EqualFold(hnsNetworkInfo.networkType, NETWORK_TYPE_OVERLAY) {
if isOverlay(hnsNetworkInfo) {
time.Sleep(10 * time.Second)
hnsNetworkInfo, err = hns.getNetworkByName(hnsNetworkName)
if err != nil {
@@ -595,7 +679,7 @@ func NewProxier(
var sourceVip string
var hostMac string
if strings.EqualFold(hnsNetworkInfo.networkType, NETWORK_TYPE_OVERLAY) {
if isOverlay(hnsNetworkInfo) {
if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.WinOverlay) {
return nil, fmt.Errorf("WinOverlay feature gate not enabled")
}
@@ -608,6 +692,15 @@ func NewProxier(
return nil, fmt.Errorf("source-vip flag not set")
}
if nodeIP.IsUnspecified() {
// attempt to get the correct ip address
klog.V(2).InfoS("node ip was unspecified. Attempting to find node ip")
nodeIP, err = apiutil.ResolveBindAddress(nodeIP)
if err != nil {
klog.InfoS("failed to find an ip. You may need set the --bind-address flag", "err", err)
}
}
interfaces, _ := net.Interfaces() //TODO create interfaces
for _, inter := range interfaces {
addresses, _ := inter.Addrs()

View File

@@ -25,6 +25,7 @@ import (
"testing"
"time"
"github.com/Microsoft/hcsshim/hcn"
v1 "k8s.io/api/core/v1"
discovery "k8s.io/api/discovery/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -928,3 +929,54 @@ func makeTestEndpointSlice(namespace, name string, sliceNum int, epsFunc func(*d
epsFunc(eps)
return eps
}
func Test_kernelSupportsDualstack(t *testing.T) {
tests := []struct {
currentVersion hcn.Version
name string
want bool
}{
{
hcn.Version{Major: 10, Minor: 10},
"Less than minimal should not be supported",
false,
},
{
hcn.Version{Major: 9, Minor: 11},
"Less than minimal should not be supported",
false,
},
{
hcn.Version{Major: 11, Minor: 1},
"Less than minimal should not be supported",
false,
},
{
hcn.Version{Major: 11, Minor: 10},
"Current version should be supported",
true,
},
{
hcn.Version{Major: 11, Minor: 11},
"Greater than minimal version should be supported",
true,
},
{
hcn.Version{Major: 12, Minor: 1},
"Greater than minimal version should be supported",
true,
},
{
hcn.Version{Major: 12, Minor: 12},
"Greater than minimal should be supported",
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := kernelSupportsDualstack(tt.currentVersion); got != tt.want {
t.Errorf("kernelSupportsDualstack on version %v: got %v, want %v", tt.currentVersion, got, tt.want)
}
})
}
}