diff --git a/cluster/cluster.go b/cluster/cluster.go index 4f0d92ef..d92053a3 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -58,7 +58,7 @@ type Cluster struct { InactiveHosts []*hosts.Host K8sWrapTransport transport.WrapperFunc KubeClient *kubernetes.Clientset - KubernetesServiceIP net.IP + KubernetesServiceIP []net.IP LocalKubeConfigPath string LocalConnDialerFactory hosts.DialerFactory PrivateRegistriesMap map[string]v3.PrivateRegistry @@ -736,7 +736,7 @@ func InitClusterObject(ctx context.Context, rkeConfig *v3.RancherKubernetesEngin } // extract cluster network configuration if err = c.setNetworkOptions(); err != nil { - return nil, fmt.Errorf("failed set network options: %v", err) + return nil, fmt.Errorf("Failed to set network options: %v", err) } // Register cloud provider diff --git a/cluster/network.go b/cluster/network.go index 2612e0c8..d9e42372 100644 --- a/cluster/network.go +++ b/cluster/network.go @@ -289,6 +289,7 @@ var EtcdClientPortList = []string{ } var CalicoNetworkLabels = []string{CalicoNodeLabel, CalicoControllerLabel} +var IPv6CompatibleNetworkPlugins = []string{CalicoNetworkPlugin} func (c *Cluster) deployNetworkPlugin(ctx context.Context, data map[string]interface{}) error { log.Infof(ctx, "[network] Setting up network plugin: %s", c.Network.Plugin) diff --git a/cluster/validation.go b/cluster/validation.go index 6f07e97b..880e56d8 100644 --- a/cluster/validation.go +++ b/cluster/validation.go @@ -208,6 +208,31 @@ func validateNetworkOptions(c *Cluster) error { if c.Network.Plugin == FlannelNetworkPlugin && c.Network.MTU != 0 { return fmt.Errorf("Network plugin [%s] does not support configuring MTU", FlannelNetworkPlugin) } + dualStack := false + serviceClusterRanges := strings.Split(c.Services.KubeAPI.ServiceClusterIPRange, ",") + if len(serviceClusterRanges) > 1 { + logrus.Debugf("Found more than 1 service cluster IP range, assuming dual stack") + dualStack = true + } + clusterCIDRs := strings.Split(c.Services.KubeController.ClusterCIDR, ",") + if len(clusterCIDRs) > 1 { + logrus.Debugf("Found more than 1 cluster CIDR, assuming dual stack") + dualStack = true + } + if dualStack { + IPv6CompatibleNetworkPluginFound := false + for _, networkPlugin := range IPv6CompatibleNetworkPlugins { + if c.Network.Plugin == networkPlugin { + logrus.Debugf("Found IPv6 compatible network plugin [%s] == [%s]", c.Network.Plugin, networkPlugin) + IPv6CompatibleNetworkPluginFound = true + break + } + } + if !IPv6CompatibleNetworkPluginFound { + return fmt.Errorf("Network plugin [%s] does not support IPv6 (dualstack)", c.Network.Plugin) + } + } + if c.Network.Plugin == AciNetworkPlugin { //Skip cloud options and throw an error. cloudOptionsList := []string{AciEpRegistry, AciOpflexMode, AciUseHostNetnsVolume, AciUseOpflexServerVolume, diff --git a/pki/pki.go b/pki/pki.go index d3d2f50f..c042dbd3 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -83,7 +83,7 @@ func RegenerateEtcdCertificate( etcdHost *hosts.Host, etcdHosts []*hosts.Host, clusterDomain string, - KubernetesServiceIP net.IP) (map[string]CertificatePKI, error) { + KubernetesServiceIP []net.IP) (map[string]CertificatePKI, error) { etcdName := GetCrtNameForHost(etcdHost, EtcdCertName) log.Infof(ctx, "[certificates] Regenerating new %s certificate and key", etcdName) diff --git a/pki/pki_test.go b/pki/pki_test.go index d8637cc9..feabc0fc 100644 --- a/pki/pki_test.go +++ b/pki/pki_test.go @@ -86,8 +86,8 @@ func TestPKI(t *testing.T) { net.ParseIP("127.0.0.1"), net.ParseIP(rkeConfig.Nodes[0].InternalAddress), net.ParseIP(rkeConfig.Nodes[0].Address), - kubernetesServiceIP, } + kubeAPIAltIPs = append(kubeAPIAltIPs, kubernetesServiceIP...) for _, testIP := range kubeAPIAltIPs { found := false diff --git a/pki/util.go b/pki/util.go index 419be1c0..784cf325 100644 --- a/pki/util.go +++ b/pki/util.go @@ -163,7 +163,7 @@ func GetIPHostAltnamesForHost(host *hosts.Host) *cert.AltNames { } } -func GetAltNames(cpHosts []*hosts.Host, clusterDomain string, KubernetesServiceIP net.IP, SANs []string) *cert.AltNames { +func GetAltNames(cpHosts []*hosts.Host, clusterDomain string, KubernetesServiceIP []net.IP, SANs []string) *cert.AltNames { ips := []net.IP{} dnsNames := []string{} for _, host := range cpHosts { @@ -198,7 +198,7 @@ func GetAltNames(cpHosts []*hosts.Host, clusterDomain string, KubernetesServiceI } ips = append(ips, net.ParseIP("127.0.0.1")) - ips = append(ips, KubernetesServiceIP) + ips = append(ips, KubernetesServiceIP...) dnsNames = append(dnsNames, []string{ "localhost", "kubernetes", @@ -379,19 +379,24 @@ func getCertKeys(rkeNodes []v3.RKEConfigNode, nodeRole string, rkeConfig *v3.Ran return certList } -func GetKubernetesServiceIP(serviceClusterRange string) (net.IP, error) { - ip, ipnet, err := net.ParseCIDR(serviceClusterRange) - if err != nil { - return nil, fmt.Errorf("Failed to get kubernetes service IP from Kube API option [service_cluster_ip_range]: %v", err) - } - ip = ip.Mask(ipnet.Mask) - for j := len(ip) - 1; j >= 0; j-- { - ip[j]++ - if ip[j] > 0 { - break +func GetKubernetesServiceIP(serviceClusterRange string) ([]net.IP, error) { + var serviceIPs []net.IP + serviceClusterRanges := strings.Split(serviceClusterRange, ",") + for _, serviceClusterRange := range serviceClusterRanges { + ip, ipnet, err := net.ParseCIDR(serviceClusterRange) + if err != nil { + return nil, fmt.Errorf("Failed to get kubernetes service IP from Kube API option [service_cluster_ip_range]: %v", err) } + ip = ip.Mask(ipnet.Mask) + for j := len(ip) - 1; j >= 0; j-- { + ip[j]++ + if ip[j] > 0 { + break + } + } + serviceIPs = append(serviceIPs, ip) } - return ip, nil + return serviceIPs, nil } func GetLocalKubeConfig(configPath, configDir string) string {