Merge pull request #9490 from johngmyers/newcluster-4

Move more cluster creation code to NewCluster()
This commit is contained in:
Kubernetes Prow Robot 2020-07-06 16:23:57 -07:00 committed by GitHub
commit 222756b35d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 283 additions and 359 deletions

View File

@ -73,7 +73,6 @@ go_library(
"//pkg/instancegroups:go_default_library",
"//pkg/kopscodecs:go_default_library",
"//pkg/kubeconfig:go_default_library",
"//pkg/model/components:go_default_library",
"//pkg/pki:go_default_library",
"//pkg/pretty:go_default_library",
"//pkg/resources:go_default_library",
@ -85,8 +84,6 @@ go_library(
"//upup/pkg/fi:go_default_library",
"//upup/pkg/fi/cloudup:go_default_library",
"//upup/pkg/fi/cloudup/awsup:go_default_library",
"//upup/pkg/fi/cloudup/gce:go_default_library",
"//upup/pkg/fi/cloudup/openstack:go_default_library",
"//upup/pkg/fi/utils:go_default_library",
"//upup/pkg/kutil:go_default_library",
"//util/pkg/tables:go_default_library",

View File

@ -26,27 +26,20 @@ import (
"os"
"strings"
"github.com/blang/semver/v4"
"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/klog"
"k8s.io/kops/cmd/kops/util"
api "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/registry"
version "k8s.io/kops/pkg/apis/kops/util"
"k8s.io/kops/pkg/apis/kops/validation"
"k8s.io/kops/pkg/assets"
"k8s.io/kops/pkg/commands"
"k8s.io/kops/pkg/dns"
"k8s.io/kops/pkg/featureflag"
"k8s.io/kops/pkg/model/components"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
"k8s.io/kops/upup/pkg/fi/utils"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
@ -60,20 +53,16 @@ type CreateClusterOptions struct {
MasterSize string
MasterVolumeSize int32
NodeVolumeSize int32
Project string
KubernetesVersion string
ContainerRuntime string
OutDir string
Image string
NodeImage string
MasterImage string
UtilitySubnetIDs []string
DisableSubnetTags bool
NetworkCIDR string
DNSZone string
AdminAccess []string
SSHAccess []string
Networking string
NodeSecurityGroups []string
MasterSecurityGroups []string
AssociatePublicIP *bool
@ -84,15 +73,6 @@ type CreateClusterOptions struct {
// Overrides allows settings values direct in the spec
Overrides []string
// The network topology to use
Topology string
// The DNS type to use (public/private)
DNSType string
// Enable/Disable Bastion Host complete setup
Bastion bool
// Specify tags for AWS instance groups
CloudLabels string
@ -109,15 +89,8 @@ type CreateClusterOptions struct {
// Allow custom public master name
MasterPublicName string
// Spotinst options
SpotinstProduct string
SpotinstOrientation string
OpenstackNetworkID string
// GCEServiceAccount specifies the service account with which the GCE VM runs
GCEServiceAccount string
// DryRun mode output a cluster manifest of Output type.
DryRun bool
// Output type during a DryRun
@ -129,10 +102,6 @@ func (o *CreateClusterOptions) InitDefaults() {
o.Yes = false
o.Target = cloudup.TargetDirect
o.Networking = "kubenet"
o.Topology = api.TopologyPublic
o.DNSType = string(api.DNSTypePublic)
o.Bastion = false
// Default to open API & SSH access
o.AdminAccess = []string{"0.0.0.0/0"}
@ -508,265 +477,23 @@ func RunCreateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *Cr
cluster.Spec.DNSZone = c.DNSZone
}
// TODO: push more of the following logic into cloudup.NewCluster()
channel := clusterResult.Channel
allZones := clusterResult.AllZones
if c.CloudProvider != "" {
if featureflag.Spotinst.Enabled() {
if cluster.Spec.CloudConfig == nil {
cluster.Spec.CloudConfig = &api.CloudConfiguration{}
}
if c.SpotinstProduct != "" {
cluster.Spec.CloudConfig.SpotinstProduct = fi.String(c.SpotinstProduct)
}
if c.SpotinstOrientation != "" {
cluster.Spec.CloudConfig.SpotinstOrientation = fi.String(c.SpotinstOrientation)
}
}
}
// Populate project
if c.Project != "" {
cluster.Spec.Project = c.Project
}
if api.CloudProviderID(cluster.Spec.CloudProvider) == api.CloudProviderGCE {
if cluster.Spec.CloudConfig == nil {
cluster.Spec.CloudConfig = &api.CloudConfiguration{}
}
if cluster.Spec.Project == "" {
project, err := gce.DefaultProject()
if err != nil {
klog.Warningf("unable to get default google cloud project: %v", err)
} else if project == "" {
klog.Warningf("default google cloud project not set (try `gcloud config set project <name>`")
} else {
klog.Infof("using google cloud project: %s", project)
}
cluster.Spec.Project = project
}
if c.GCEServiceAccount != "" {
klog.Infof("VMs will be configured to use specified Service Account: %v", c.GCEServiceAccount)
cluster.Spec.CloudConfig.GCEServiceAccount = c.GCEServiceAccount
} else {
klog.Warning("VMs will be configured to use the GCE default compute Service Account! This is an anti-pattern")
klog.Warning("Use a pre-created Service Account with the flag: --gce-service-account=account@projectname.iam.gserviceaccount.com")
cluster.Spec.CloudConfig.GCEServiceAccount = "default"
}
}
if c.KubernetesVersion != "" {
cluster.Spec.KubernetesVersion = c.KubernetesVersion
}
if c.ContainerRuntime != "" {
cluster.Spec.ContainerRuntime = c.ContainerRuntime
}
cluster.Spec.Networking = &api.NetworkingSpec{}
switch c.Networking {
case "kubenet":
cluster.Spec.Networking.Kubenet = &api.KubenetNetworkingSpec{}
case "external":
cluster.Spec.Networking.External = &api.ExternalNetworkingSpec{}
case "cni":
cluster.Spec.Networking.CNI = &api.CNINetworkingSpec{}
case "kopeio-vxlan", "kopeio":
cluster.Spec.Networking.Kopeio = &api.KopeioNetworkingSpec{}
case "weave":
cluster.Spec.Networking.Weave = &api.WeaveNetworkingSpec{}
if cluster.Spec.CloudProvider == "aws" {
// AWS supports "jumbo frames" of 9001 bytes and weave adds up to 87 bytes overhead
// sets the default to the largest number that leaves enough overhead and is divisible by 4
jumboFrameMTUSize := int32(8912)
cluster.Spec.Networking.Weave.MTU = &jumboFrameMTUSize
}
case "flannel", "flannel-vxlan":
cluster.Spec.Networking.Flannel = &api.FlannelNetworkingSpec{
Backend: "vxlan",
}
case "flannel-udp":
klog.Warningf("flannel UDP mode is not recommended; consider flannel-vxlan instead")
cluster.Spec.Networking.Flannel = &api.FlannelNetworkingSpec{
Backend: "udp",
}
case "calico":
cluster.Spec.Networking.Calico = &api.CalicoNetworkingSpec{
MajorVersion: "v3",
}
// Validate to check if etcd clusters have an acceptable version
if errList := validation.ValidateEtcdVersionForCalicoV3(cluster.Spec.EtcdClusters[0], cluster.Spec.Networking.Calico.MajorVersion, field.NewPath("spec", "networking", "calico")); len(errList) != 0 {
// This is not a special version but simply of the 3 series
for _, etcd := range cluster.Spec.EtcdClusters {
etcd.Version = components.DefaultEtcd3Version_1_11
}
}
case "canal":
cluster.Spec.Networking.Canal = &api.CanalNetworkingSpec{}
case "kube-router":
cluster.Spec.Networking.Kuberouter = &api.KuberouterNetworkingSpec{}
if cluster.Spec.KubeProxy == nil {
cluster.Spec.KubeProxy = &api.KubeProxyConfig{}
}
enabled := false
cluster.Spec.KubeProxy.Enabled = &enabled
case "amazonvpc", "amazon-vpc-routed-eni":
cluster.Spec.Networking.AmazonVPC = &api.AmazonVPCNetworkingSpec{}
case "cilium":
cilium := &api.CiliumNetworkingSpec{}
cluster.Spec.Networking.Cilium = cilium
nodeport := false
if c.KubernetesVersion == "" {
nodeport = true
} else {
k8sVersion, err := semver.ParseTolerant(c.KubernetesVersion)
if err == nil {
if version.IsKubernetesGTE("1.12", k8sVersion) {
nodeport = true
}
}
}
if nodeport {
cilium.EnableNodePort = true
if cluster.Spec.KubeProxy == nil {
cluster.Spec.KubeProxy = &api.KubeProxyConfig{}
}
enabled := false
cluster.Spec.KubeProxy.Enabled = &enabled
}
case "lyftvpc":
cluster.Spec.Networking.LyftVPC = &api.LyftVPCNetworkingSpec{}
case "gce":
cluster.Spec.Networking.GCE = &api.GCENetworkingSpec{}
default:
return fmt.Errorf("unknown networking mode %q", c.Networking)
}
klog.V(4).Infof("networking mode=%s => %s", c.Networking, fi.DebugAsJsonString(cluster.Spec.Networking))
if c.NetworkCIDR != "" {
cluster.Spec.NetworkCIDR = c.NetworkCIDR
}
// Network Topology
if c.Topology == "" {
// The flag default should have set this, but we might be being called as a library
klog.Infof("Empty topology. Defaulting to public topology")
c.Topology = api.TopologyPublic
}
cluster.Spec.DisableSubnetTags = c.DisableSubnetTags
switch c.Topology {
case api.TopologyPublic:
cluster.Spec.Topology = &api.TopologySpec{
Masters: api.TopologyPublic,
Nodes: api.TopologyPublic,
//Bastion: &api.BastionSpec{Enable: c.Bastion},
}
if c.Bastion {
return fmt.Errorf("Bastion supports --topology='private' only.")
}
for i := range cluster.Spec.Subnets {
cluster.Spec.Subnets[i].Type = api.SubnetTypePublic
}
case api.TopologyPrivate:
if cluster.Spec.Networking.Kubenet != nil {
return fmt.Errorf("invalid networking option %s. Kubenet does not support private topology", c.Networking)
}
cluster.Spec.Topology = &api.TopologySpec{
Masters: api.TopologyPrivate,
Nodes: api.TopologyPrivate,
}
for i := range cluster.Spec.Subnets {
cluster.Spec.Subnets[i].Type = api.SubnetTypePrivate
}
var utilitySubnets []api.ClusterSubnetSpec
var zoneToSubnetProviderID map[string]string
if len(c.Zones) > 0 && len(c.UtilitySubnetIDs) > 0 {
if api.CloudProviderID(cluster.Spec.CloudProvider) == api.CloudProviderAWS {
zoneToSubnetProviderID, err = getZoneToSubnetProviderID(cluster.Spec.NetworkID, c.Zones[0][:len(c.Zones[0])-1], c.UtilitySubnetIDs)
if err != nil {
return err
}
} else if api.CloudProviderID(cluster.Spec.CloudProvider) == api.CloudProviderOpenstack {
tags := make(map[string]string)
tags[openstack.TagClusterName] = c.ClusterName
zoneToSubnetProviderID, err = getSubnetProviderID(&cluster.Spec, allZones.List(), c.UtilitySubnetIDs, tags)
if err != nil {
return err
}
}
}
for _, s := range cluster.Spec.Subnets {
if s.Type == api.SubnetTypeUtility {
continue
}
subnet := api.ClusterSubnetSpec{
Name: "utility-" + s.Name,
Zone: s.Zone,
Type: api.SubnetTypeUtility,
}
if subnetID, ok := zoneToSubnetProviderID[s.Zone]; ok {
subnet.ProviderID = subnetID
}
utilitySubnets = append(utilitySubnets, subnet)
}
cluster.Spec.Subnets = append(cluster.Spec.Subnets, utilitySubnets...)
if c.Bastion {
bastionGroup := &api.InstanceGroup{}
bastionGroup.Spec.Role = api.InstanceGroupRoleBastion
bastionGroup.ObjectMeta.Name = "bastions"
bastionGroup.Spec.Image = c.Image
instanceGroups = append(instanceGroups, bastionGroup)
if !dns.IsGossipHostname(cluster.Name) {
cluster.Spec.Topology.Bastion = &api.BastionSpec{
BastionPublicName: "bastion." + cluster.Name,
}
}
}
default:
return fmt.Errorf("Invalid topology %s.", c.Topology)
}
// DNS
if c.DNSType == "" {
// The flag default should have set this, but we might be being called as a library
klog.Infof("Empty DNS. Defaulting to public DNS")
c.DNSType = string(api.DNSTypePublic)
}
if cluster.Spec.Topology == nil {
cluster.Spec.Topology = &api.TopologySpec{}
}
if cluster.Spec.Topology.DNS == nil {
cluster.Spec.Topology.DNS = &api.DNSSpec{}
}
switch strings.ToLower(c.DNSType) {
case "public":
cluster.Spec.Topology.DNS.Type = api.DNSTypePublic
case "private":
cluster.Spec.Topology.DNS.Type = api.DNSTypePrivate
default:
return fmt.Errorf("unknown DNSType: %q", c.DNSType)
}
if c.MasterPublicName != "" {
cluster.Spec.MasterPublicName = c.MasterPublicName
}
// TODO: push more of the following logic into cloudup.NewCluster()
channel := clusterResult.Channel
// check if we should set anonymousAuth to false
{
if cluster.Spec.Kubelet == nil {
@ -1065,74 +792,6 @@ func initializeOpenstackAPI(c *CreateClusterOptions, cluster *api.Cluster) {
}
}
// TODO dedup or remove
func getZoneToSubnetProviderID(VPCID string, region string, subnetIDs []string) (map[string]string, error) {
res := make(map[string]string)
cloudTags := map[string]string{}
awsCloud, err := awsup.NewAWSCloud(region, cloudTags)
if err != nil {
return res, fmt.Errorf("error loading cloud: %v", err)
}
vpcInfo, err := awsCloud.FindVPCInfo(VPCID)
if err != nil {
return res, fmt.Errorf("error describing VPC: %v", err)
}
if vpcInfo == nil {
return res, fmt.Errorf("VPC %q not found", VPCID)
}
subnetByID := make(map[string]*fi.SubnetInfo)
for _, subnetInfo := range vpcInfo.Subnets {
subnetByID[subnetInfo.ID] = subnetInfo
}
for _, subnetID := range subnetIDs {
subnet, ok := subnetByID[subnetID]
if !ok {
return res, fmt.Errorf("subnet %s not found in VPC %s", subnetID, VPCID)
}
if res[subnet.Zone] != "" {
return res, fmt.Errorf("subnet %s and %s have the same zone", subnetID, res[subnet.Zone])
}
res[subnet.Zone] = subnetID
}
return res, nil
}
// TODO dedup or remove
func getSubnetProviderID(spec *api.ClusterSpec, zones []string, subnetIDs []string, tags map[string]string) (map[string]string, error) {
res := make(map[string]string)
osCloud, err := openstack.NewOpenstackCloud(tags, spec)
if err != nil {
return res, fmt.Errorf("error loading cloud: %v", err)
}
osCloud.UseZones(zones)
networkInfo, err := osCloud.FindVPCInfo(spec.NetworkID)
if err != nil {
return res, fmt.Errorf("error describing Network: %v", err)
}
if networkInfo == nil {
return res, fmt.Errorf("network %q not found", spec.NetworkID)
}
subnetByID := make(map[string]*fi.SubnetInfo)
for _, subnetInfo := range networkInfo.Subnets {
subnetByID[subnetInfo.ID] = subnetInfo
}
for _, subnetID := range subnetIDs {
subnet, ok := subnetByID[subnetID]
if !ok {
return res, fmt.Errorf("subnet %s not found in network %s", subnetID, spec.NetworkID)
}
if res[subnet.Zone] != "" {
return res, fmt.Errorf("subnet %s and %s have the same zone", subnetID, res[subnet.Zone])
}
res[subnet.Zone] = subnetID
}
return res, nil
}
func loadSSHPublicKeys(sshPublicKey string) (map[string][]byte, error) {
sshPublicKeys := make(map[string][]byte)
if sshPublicKey != "" {

View File

@ -84,6 +84,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)

View File

@ -23,14 +23,21 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/blang/semver/v4"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/klog"
"k8s.io/kops"
api "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/model"
version "k8s.io/kops/pkg/apis/kops/util"
"k8s.io/kops/pkg/apis/kops/validation"
"k8s.io/kops/pkg/client/simple"
"k8s.io/kops/pkg/dns"
"k8s.io/kops/pkg/featureflag"
"k8s.io/kops/pkg/model/components"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/aliup"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
@ -53,6 +60,8 @@ type NewClusterOptions struct {
Channel string
// ConfigBase is the location where we will store the configuration. It defaults to the state store.
ConfigBase string
// KubernetesVersion is the version of Kubernetes to deploy. It defaults to the version recommended by the channel.
KubernetesVersion string
// CloudProvider is the name of the cloud provider. The default is to guess based on the Zones name.
CloudProvider string
@ -61,12 +70,24 @@ type NewClusterOptions struct {
// MasterZones are the availability zones in which to run the masters. Defaults to the list in the Zones field.
MasterZones []string
// Project is the cluster's GCE project.
Project string
// GCEServiceAccount specifies the service account with which the GCE VM runs.
GCEServiceAccount string
// Spotinst options
SpotinstProduct string
SpotinstOrientation string
// NetworkID is the ID of the shared network (VPC).
// If empty, SubnetIDs are not empty, and on AWS or OpenStack, determines network ID from the first SubnetID.
// If empty otherwise, creates a new network/VPC to be owned by the cluster.
NetworkID string
// SubnetIDs are the IDs of the shared subnets. If empty, creates new subnets to be owned by the cluster.
// SubnetIDs are the IDs of the shared subnets.
// If empty, creates new subnets to be owned by the cluster.
SubnetIDs []string
// UtilitySubnetIDs are the IDs of the shared utility subnets. If empty and the topology is "private", creates new subnets to be owned by the cluster.
UtilitySubnetIDs []string
// Egress defines the method of traffic egress for subnets.
Egress string
@ -90,11 +111,23 @@ type NewClusterOptions struct {
// NodeCount is the number of nodes to create. Defaults to leaving the count unspecified
// on the InstanceGroup, which results in a count of 2.
NodeCount int32
// Bastion enables the creation of a Bastion instance.
Bastion bool
// Networking is the networking provider/node to use.
Networking string
// Topology is the network topology to use. Defaults to "public".
Topology string
// DNSType is the DNS type to use; "public" or "private". Defaults to "public".
DNSType string
}
func (o *NewClusterOptions) InitDefaults() {
o.Channel = api.DefaultChannel
o.Authorization = AuthorizationFlagRBAC
o.Networking = "kubenet"
o.Topology = api.TopologyPublic
o.DNSType = string(api.DNSTypePublic)
}
type NewClusterResult struct {
@ -104,8 +137,7 @@ type NewClusterResult struct {
InstanceGroups []*api.InstanceGroup
// TODO remove after more create_cluster logic refactored in
Channel *api.Channel
AllZones sets.String
Channel *api.Channel
}
// NewCluster initializes cluster and instance groups specifications as
@ -138,6 +170,9 @@ func NewCluster(opt *NewClusterOptions, clientset simple.Clientset) (*NewCluster
}
}
cluster.Spec.Channel = opt.Channel
if opt.KubernetesVersion != "" {
cluster.Spec.KubernetesVersion = opt.KubernetesVersion
}
cluster.Spec.ConfigBase = opt.ConfigBase
configBase, err := clientset.ConfigBaseFor(&cluster)
@ -197,14 +232,24 @@ func NewCluster(opt *NewClusterOptions, clientset simple.Clientset) (*NewCluster
return nil, err
}
err = setupNetworking(opt, &cluster)
if err != nil {
return nil, err
}
bastions, err := setupTopology(opt, &cluster, allZones)
if err != nil {
return nil, err
}
instanceGroups := append([]*api.InstanceGroup(nil), masters...)
instanceGroups = append(instanceGroups, nodes...)
instanceGroups = append(instanceGroups, bastions...)
result := NewClusterResult{
Cluster: &cluster,
InstanceGroups: instanceGroups,
Channel: channel,
AllZones: allZones,
}
return &result, nil
}
@ -232,6 +277,32 @@ func setupVPC(opt *NewClusterOptions, cluster *api.Cluster) error {
cluster.Spec.NetworkID = *res.Subnets[0].VpcId
}
case api.CloudProviderGCE:
if cluster.Spec.CloudConfig == nil {
cluster.Spec.CloudConfig = &api.CloudConfiguration{}
}
cluster.Spec.Project = opt.Project
if cluster.Spec.Project == "" {
project, err := gce.DefaultProject()
if err != nil {
klog.Warningf("unable to get default google cloud project: %v", err)
} else if project == "" {
klog.Warningf("default google cloud project not set (try `gcloud config set project <name>`")
} else {
klog.Infof("using google cloud project: %s", project)
}
cluster.Spec.Project = project
}
if opt.GCEServiceAccount != "" {
// TODO remove this logging?
klog.Infof("VMs will be configured to use specified Service Account: %v", opt.GCEServiceAccount)
cluster.Spec.CloudConfig.GCEServiceAccount = opt.GCEServiceAccount
} else {
klog.Warning("VMs will be configured to use the GCE default compute Service Account! This is an anti-pattern")
klog.Warning("Use a pre-created Service Account with the flag: --gce-service-account=account@projectname.iam.gserviceaccount.com")
cluster.Spec.CloudConfig.GCEServiceAccount = "default"
}
case api.CloudProviderOpenstack:
if cluster.Spec.CloudConfig == nil {
cluster.Spec.CloudConfig = &api.CloudConfiguration{}
@ -253,7 +324,7 @@ func setupVPC(opt *NewClusterOptions, cluster *api.Cluster) error {
if cluster.Spec.NetworkID == "" && len(opt.SubnetIDs) > 0 {
tags := make(map[string]string)
tags[openstack.TagClusterName] = opt.ClusterName
tags[openstack.TagClusterName] = cluster.Name
osCloud, err := openstack.NewOpenstackCloud(tags, &cluster.Spec)
if err != nil {
return fmt.Errorf("error loading cloud: %v", err)
@ -274,6 +345,18 @@ func setupVPC(opt *NewClusterOptions, cluster *api.Cluster) error {
}
}
if featureflag.Spotinst.Enabled() {
if cluster.Spec.CloudConfig == nil {
cluster.Spec.CloudConfig = &api.CloudConfiguration{}
}
if opt.SpotinstProduct != "" {
cluster.Spec.CloudConfig.SpotinstProduct = fi.String(opt.SpotinstProduct)
}
if opt.SpotinstOrientation != "" {
cluster.Spec.CloudConfig.SpotinstOrientation = fi.String(opt.SpotinstOrientation)
}
}
return nil
}
@ -345,7 +428,7 @@ func setupZones(opt *NewClusterOptions, cluster *api.Cluster, allZones sets.Stri
case api.CloudProviderAWS:
if len(opt.Zones) > 0 && len(opt.SubnetIDs) > 0 {
zoneToSubnetProviderID, err = getZoneToSubnetProviderID(cluster.Spec.NetworkID, opt.Zones[0][:len(opt.Zones[0])-1], opt.SubnetIDs)
zoneToSubnetProviderID, err = getAWSZoneToSubnetProviderID(cluster.Spec.NetworkID, opt.Zones[0][:len(opt.Zones[0])-1], opt.SubnetIDs)
if err != nil {
return nil, err
}
@ -354,8 +437,8 @@ func setupZones(opt *NewClusterOptions, cluster *api.Cluster, allZones sets.Stri
case api.CloudProviderOpenstack:
if len(opt.Zones) > 0 && len(opt.SubnetIDs) > 0 {
tags := make(map[string]string)
tags[openstack.TagClusterName] = opt.ClusterName
zoneToSubnetProviderID, err = getSubnetProviderID(&cluster.Spec, allZones.List(), opt.SubnetIDs, tags)
tags[openstack.TagClusterName] = cluster.Name
zoneToSubnetProviderID, err = getOpenstackZoneToSubnetProviderID(&cluster.Spec, allZones.List(), opt.SubnetIDs, tags)
if err != nil {
return nil, err
}
@ -384,8 +467,7 @@ func setupZones(opt *NewClusterOptions, cluster *api.Cluster, allZones sets.Stri
return zoneToSubnetMap, nil
}
// TODO rename to getAWSZoneToSubnetProviderID
func getZoneToSubnetProviderID(VPCID string, region string, subnetIDs []string) (map[string]string, error) {
func getAWSZoneToSubnetProviderID(VPCID string, region string, subnetIDs []string) (map[string]string, error) {
res := make(map[string]string)
cloudTags := map[string]string{}
awsCloud, err := awsup.NewAWSCloud(region, cloudTags)
@ -416,8 +498,7 @@ func getZoneToSubnetProviderID(VPCID string, region string, subnetIDs []string)
return res, nil
}
// TODO rename to getOpenstackZoneToSubnetProviderID
func getSubnetProviderID(spec *api.ClusterSpec, zones []string, subnetIDs []string, tags map[string]string) (map[string]string, error) {
func getOpenstackZoneToSubnetProviderID(spec *api.ClusterSpec, zones []string, subnetIDs []string, tags map[string]string) (map[string]string, error) {
res := make(map[string]string)
osCloud, err := openstack.NewOpenstackCloud(tags, spec)
if err != nil {
@ -644,3 +725,189 @@ func setupNodes(opt *NewClusterOptions, cluster *api.Cluster, zoneToSubnetMap ma
return nodes, nil
}
func setupNetworking(opt *NewClusterOptions, cluster *api.Cluster) error {
cluster.Spec.Networking = &api.NetworkingSpec{}
switch opt.Networking {
case "kubenet", "":
cluster.Spec.Networking.Kubenet = &api.KubenetNetworkingSpec{}
case "external":
cluster.Spec.Networking.External = &api.ExternalNetworkingSpec{}
case "cni":
cluster.Spec.Networking.CNI = &api.CNINetworkingSpec{}
case "kopeio-vxlan", "kopeio":
cluster.Spec.Networking.Kopeio = &api.KopeioNetworkingSpec{}
case "weave":
cluster.Spec.Networking.Weave = &api.WeaveNetworkingSpec{}
if cluster.Spec.CloudProvider == "aws" {
// AWS supports "jumbo frames" of 9001 bytes and weave adds up to 87 bytes overhead
// sets the default to the largest number that leaves enough overhead and is divisible by 4
jumboFrameMTUSize := int32(8912)
cluster.Spec.Networking.Weave.MTU = &jumboFrameMTUSize
}
case "flannel", "flannel-vxlan":
cluster.Spec.Networking.Flannel = &api.FlannelNetworkingSpec{
Backend: "vxlan",
}
case "flannel-udp":
klog.Warningf("flannel UDP mode is not recommended; consider flannel-vxlan instead")
cluster.Spec.Networking.Flannel = &api.FlannelNetworkingSpec{
Backend: "udp",
}
case "calico":
cluster.Spec.Networking.Calico = &api.CalicoNetworkingSpec{
MajorVersion: "v3",
}
// Validate to check if etcd clusters have an acceptable version
if errList := validation.ValidateEtcdVersionForCalicoV3(cluster.Spec.EtcdClusters[0], cluster.Spec.Networking.Calico.MajorVersion, field.NewPath("spec", "networking", "calico")); len(errList) != 0 {
// This is not a special version but simply of the 3 series
for _, etcd := range cluster.Spec.EtcdClusters {
etcd.Version = components.DefaultEtcd3Version_1_11
}
}
case "canal":
cluster.Spec.Networking.Canal = &api.CanalNetworkingSpec{}
case "kube-router":
cluster.Spec.Networking.Kuberouter = &api.KuberouterNetworkingSpec{}
if cluster.Spec.KubeProxy == nil {
cluster.Spec.KubeProxy = &api.KubeProxyConfig{}
}
enabled := false
cluster.Spec.KubeProxy.Enabled = &enabled
case "amazonvpc", "amazon-vpc-routed-eni":
cluster.Spec.Networking.AmazonVPC = &api.AmazonVPCNetworkingSpec{}
case "cilium":
cilium := &api.CiliumNetworkingSpec{}
cluster.Spec.Networking.Cilium = cilium
nodeport := false
if cluster.Spec.KubernetesVersion == "" {
nodeport = true
} else {
k8sVersion, err := semver.ParseTolerant(cluster.Spec.KubernetesVersion)
if err == nil {
if version.IsKubernetesGTE("1.12", k8sVersion) {
nodeport = true
}
}
}
if nodeport {
cilium.EnableNodePort = true
if cluster.Spec.KubeProxy == nil {
cluster.Spec.KubeProxy = &api.KubeProxyConfig{}
}
enabled := false
cluster.Spec.KubeProxy.Enabled = &enabled
}
case "lyftvpc":
cluster.Spec.Networking.LyftVPC = &api.LyftVPCNetworkingSpec{}
case "gce":
cluster.Spec.Networking.GCE = &api.GCENetworkingSpec{}
default:
return fmt.Errorf("unknown networking mode %q", opt.Networking)
}
klog.V(4).Infof("networking mode=%s => %s", opt.Networking, fi.DebugAsJsonString(cluster.Spec.Networking))
return nil
}
func setupTopology(opt *NewClusterOptions, cluster *api.Cluster, allZones sets.String) ([]*api.InstanceGroup, error) {
var bastions []*api.InstanceGroup
switch opt.Topology {
case api.TopologyPublic, "":
cluster.Spec.Topology = &api.TopologySpec{
Masters: api.TopologyPublic,
Nodes: api.TopologyPublic,
//Bastion: &api.BastionSpec{Enable: c.Bastion},
}
if opt.Bastion {
return nil, fmt.Errorf("bastion supports --topology='private' only")
}
for i := range cluster.Spec.Subnets {
cluster.Spec.Subnets[i].Type = api.SubnetTypePublic
}
case api.TopologyPrivate:
if cluster.Spec.Networking.Kubenet != nil {
return nil, fmt.Errorf("invalid networking option %s. Kubenet does not support private topology", opt.Networking)
}
cluster.Spec.Topology = &api.TopologySpec{
Masters: api.TopologyPrivate,
Nodes: api.TopologyPrivate,
}
for i := range cluster.Spec.Subnets {
cluster.Spec.Subnets[i].Type = api.SubnetTypePrivate
}
var utilitySubnets []api.ClusterSubnetSpec
var zoneToSubnetProviderID map[string]string
var err error
if len(opt.Zones) > 0 && len(opt.UtilitySubnetIDs) > 0 {
switch api.CloudProviderID(cluster.Spec.CloudProvider) {
case api.CloudProviderAWS:
zoneToSubnetProviderID, err = getAWSZoneToSubnetProviderID(cluster.Spec.NetworkID, opt.Zones[0][:len(opt.Zones[0])-1], opt.UtilitySubnetIDs)
if err != nil {
return nil, err
}
case api.CloudProviderOpenstack:
tags := make(map[string]string)
tags[openstack.TagClusterName] = cluster.Name
zoneToSubnetProviderID, err = getOpenstackZoneToSubnetProviderID(&cluster.Spec, allZones.List(), opt.UtilitySubnetIDs, tags)
if err != nil {
return nil, err
}
}
}
for _, s := range cluster.Spec.Subnets {
if s.Type == api.SubnetTypeUtility {
continue
}
subnet := api.ClusterSubnetSpec{
Name: "utility-" + s.Name,
Zone: s.Zone,
Type: api.SubnetTypeUtility,
}
if subnetID, ok := zoneToSubnetProviderID[s.Zone]; ok {
subnet.ProviderID = subnetID
}
utilitySubnets = append(utilitySubnets, subnet)
}
cluster.Spec.Subnets = append(cluster.Spec.Subnets, utilitySubnets...)
if opt.Bastion {
bastionGroup := &api.InstanceGroup{}
bastionGroup.Spec.Role = api.InstanceGroupRoleBastion
bastionGroup.ObjectMeta.Name = "bastions"
bastions = append(bastions, bastionGroup)
if !dns.IsGossipHostname(cluster.Name) {
cluster.Spec.Topology.Bastion = &api.BastionSpec{
BastionPublicName: "bastion." + cluster.Name,
}
}
}
default:
return nil, fmt.Errorf("invalid topology %s", opt.Topology)
}
cluster.Spec.Topology.DNS = &api.DNSSpec{}
switch strings.ToLower(opt.DNSType) {
case "public", "":
cluster.Spec.Topology.DNS.Type = api.DNSTypePublic
case "private":
cluster.Spec.Topology.DNS.Type = api.DNSTypePrivate
default:
return nil, fmt.Errorf("unknown DNSType: %q", opt.DNSType)
}
return bastions, nil
}