mirror of https://github.com/kubernetes/kops.git
				
				
				
			
		
			
				
	
	
		
			249 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
/*
 | 
						|
Copyright 2016 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package cloudup
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
 | 
						|
	"github.com/blang/semver"
 | 
						|
	"github.com/golang/glog"
 | 
						|
	api "k8s.io/kops/pkg/apis/kops"
 | 
						|
	"k8s.io/kops/pkg/apis/kops/util"
 | 
						|
	"k8s.io/kops/pkg/apis/kops/validation"
 | 
						|
	"k8s.io/kops/upup/pkg/fi"
 | 
						|
	"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
 | 
						|
	"k8s.io/kops/upup/pkg/fi/utils"
 | 
						|
)
 | 
						|
 | 
						|
// Default Machine types for various types of instance group machine
 | 
						|
const (
 | 
						|
	defaultNodeMachineTypeAWS = "t2.medium"
 | 
						|
	defaultNodeMachineTypeGCE = "n1-standard-2"
 | 
						|
 | 
						|
	defaultBastionMachineTypeAWS = "t2.micro"
 | 
						|
	defaultBastionMachineTypeGCE = "f1-micro"
 | 
						|
 | 
						|
	defaultMasterMachineTypeGCE = "n1-standard-1"
 | 
						|
	defaultMasterMachineTypeAWS = "m3.medium"
 | 
						|
)
 | 
						|
 | 
						|
var masterMachineTypeExceptions = map[string]string{
 | 
						|
	// Some regions do not (currently) support the m3 family; the c4 large is the cheapest non-burstable instance
 | 
						|
	"us-east-2":      "c4.large",
 | 
						|
	"ca-central-1":   "c4.large",
 | 
						|
	"eu-west-2":      "c4.large",
 | 
						|
	"ap-northeast-2": "c4.large",
 | 
						|
}
 | 
						|
 | 
						|
var awsDedicatedInstanceExceptions = map[string]bool{
 | 
						|
	"t2.nano":   true,
 | 
						|
	"t2.micro":  true,
 | 
						|
	"t2.small":  true,
 | 
						|
	"t2.medium": true,
 | 
						|
	"t2.large":  true,
 | 
						|
	"t2.xlarge": true,
 | 
						|
}
 | 
						|
 | 
						|
// PopulateInstanceGroupSpec sets default values in the InstanceGroup
 | 
						|
// The InstanceGroup is simpler than the cluster spec, so we just populate in place (like the rest of k8s)
 | 
						|
func PopulateInstanceGroupSpec(cluster *api.Cluster, input *api.InstanceGroup, channel *api.Channel) (*api.InstanceGroup, error) {
 | 
						|
	err := validation.ValidateInstanceGroup(input)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	ig := &api.InstanceGroup{}
 | 
						|
	utils.JsonMergeStruct(ig, input)
 | 
						|
 | 
						|
	// TODO: Clean up
 | 
						|
	if ig.IsMaster() {
 | 
						|
		if ig.Spec.MachineType == "" {
 | 
						|
			ig.Spec.MachineType = defaultMasterMachineType(cluster)
 | 
						|
		}
 | 
						|
		if ig.Spec.MinSize == nil {
 | 
						|
			ig.Spec.MinSize = fi.Int32(1)
 | 
						|
		}
 | 
						|
		if ig.Spec.MaxSize == nil {
 | 
						|
			ig.Spec.MaxSize = fi.Int32(1)
 | 
						|
		}
 | 
						|
	} else if ig.Spec.Role == api.InstanceGroupRoleBastion {
 | 
						|
		if ig.Spec.MachineType == "" {
 | 
						|
			ig.Spec.MachineType = defaultBastionMachineType(cluster)
 | 
						|
		}
 | 
						|
		if ig.Spec.MinSize == nil {
 | 
						|
			ig.Spec.MinSize = fi.Int32(1)
 | 
						|
		}
 | 
						|
		if ig.Spec.MaxSize == nil {
 | 
						|
			ig.Spec.MaxSize = fi.Int32(1)
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if ig.Spec.MachineType == "" {
 | 
						|
			ig.Spec.MachineType = defaultNodeMachineType(cluster)
 | 
						|
		}
 | 
						|
		if ig.Spec.MinSize == nil {
 | 
						|
			ig.Spec.MinSize = fi.Int32(2)
 | 
						|
		}
 | 
						|
		if ig.Spec.MaxSize == nil {
 | 
						|
			ig.Spec.MaxSize = fi.Int32(2)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if ig.Spec.Image == "" {
 | 
						|
		ig.Spec.Image = defaultImage(cluster, channel)
 | 
						|
	}
 | 
						|
 | 
						|
	if ig.Spec.Tenancy != "" && ig.Spec.Tenancy != "default" {
 | 
						|
		switch fi.CloudProviderID(cluster.Spec.CloudProvider) {
 | 
						|
		case fi.CloudProviderAWS:
 | 
						|
			if _, ok := awsDedicatedInstanceExceptions[ig.Spec.MachineType]; ok {
 | 
						|
				return nil, fmt.Errorf("Invalid dedicated instance type: %s", ig.Spec.MachineType)
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			glog.Warning("Trying to set tenancy on non-AWS environment")
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if ig.IsMaster() {
 | 
						|
		if len(ig.Spec.Subnets) == 0 {
 | 
						|
			return nil, fmt.Errorf("Master InstanceGroup %s did not specify any Subnets", ig.ObjectMeta.Name)
 | 
						|
		}
 | 
						|
	} else if ig.Spec.Role == api.InstanceGroupRoleBastion {
 | 
						|
		if len(ig.Spec.Subnets) == 0 {
 | 
						|
			for _, subnet := range cluster.Spec.Subnets {
 | 
						|
				if subnet.Type == api.SubnetTypeUtility {
 | 
						|
					ig.Spec.Subnets = append(ig.Spec.Subnets, subnet.Name)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if len(ig.Spec.Subnets) == 0 {
 | 
						|
			for _, subnet := range cluster.Spec.Subnets {
 | 
						|
				if subnet.Type != api.SubnetTypeUtility {
 | 
						|
					ig.Spec.Subnets = append(ig.Spec.Subnets, subnet.Name)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(ig.Spec.Subnets) == 0 {
 | 
						|
		return nil, fmt.Errorf("unable to infer any Subnets for InstanceGroup %s ", ig.ObjectMeta.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	return ig, nil
 | 
						|
}
 | 
						|
 | 
						|
// defaultNodeMachineType returns the default MachineType for nodes, based on the cloudprovider
 | 
						|
func defaultNodeMachineType(cluster *api.Cluster) string {
 | 
						|
	switch fi.CloudProviderID(cluster.Spec.CloudProvider) {
 | 
						|
	case fi.CloudProviderAWS:
 | 
						|
		return defaultNodeMachineTypeAWS
 | 
						|
	case fi.CloudProviderGCE:
 | 
						|
		return defaultNodeMachineTypeGCE
 | 
						|
	default:
 | 
						|
		glog.V(2).Infof("Cannot set default MachineType for CloudProvider=%q", cluster.Spec.CloudProvider)
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// defaultMasterMachineType returns the default MachineType for masters, based on the cloudprovider
 | 
						|
func defaultMasterMachineType(cluster *api.Cluster) string {
 | 
						|
	// TODO: We used to have logic like the following...
 | 
						|
	//	{{ if gt .TotalNodeCount 500 }}
 | 
						|
	//	MasterMachineType: n1-standard-32
 | 
						|
	//	{{ else if gt .TotalNodeCount 250 }}
 | 
						|
	//MasterMachineType: n1-standard-16
 | 
						|
	//{{ else if gt .TotalNodeCount 100 }}
 | 
						|
	//MasterMachineType: n1-standard-8
 | 
						|
	//{{ else if gt .TotalNodeCount 10 }}
 | 
						|
	//MasterMachineType: n1-standard-4
 | 
						|
	//{{ else if gt .TotalNodeCount 5 }}
 | 
						|
	//MasterMachineType: n1-standard-2
 | 
						|
	//{{ else }}
 | 
						|
	//MasterMachineType: n1-standard-1
 | 
						|
	//{{ end }}
 | 
						|
	//
 | 
						|
	//{{ if gt TotalNodeCount 500 }}
 | 
						|
	//MasterMachineType: c4.8xlarge
 | 
						|
	//{{ else if gt TotalNodeCount 250 }}
 | 
						|
	//MasterMachineType: c4.4xlarge
 | 
						|
	//{{ else if gt TotalNodeCount 100 }}
 | 
						|
	//MasterMachineType: m3.2xlarge
 | 
						|
	//{{ else if gt TotalNodeCount 10 }}
 | 
						|
	//MasterMachineType: m3.xlarge
 | 
						|
	//{{ else if gt TotalNodeCount 5 }}
 | 
						|
	//MasterMachineType: m3.large
 | 
						|
	//{{ else }}
 | 
						|
	//MasterMachineType: m3.medium
 | 
						|
	//{{ end }}
 | 
						|
 | 
						|
	switch fi.CloudProviderID(cluster.Spec.CloudProvider) {
 | 
						|
	case fi.CloudProviderAWS:
 | 
						|
		region, err := awsup.FindRegion(cluster)
 | 
						|
		if err != nil {
 | 
						|
			glog.Warningf("cannot determine region from cluster zones: %v", err)
 | 
						|
		}
 | 
						|
		// Check for special-cases
 | 
						|
		masterMachineType := masterMachineTypeExceptions[region]
 | 
						|
		if masterMachineType != "" {
 | 
						|
			glog.Warningf("%q instance is not available in region %q, will set master to %q instead", defaultMasterMachineTypeAWS, region, masterMachineType)
 | 
						|
			return masterMachineType
 | 
						|
		}
 | 
						|
		return defaultMasterMachineTypeAWS
 | 
						|
	case fi.CloudProviderGCE:
 | 
						|
		return defaultMasterMachineTypeGCE
 | 
						|
	default:
 | 
						|
		glog.V(2).Infof("Cannot set default MachineType for CloudProvider=%q", cluster.Spec.CloudProvider)
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// defaultBastionMachineType returns the default MachineType for bastions, based on the cloudprovider
 | 
						|
func defaultBastionMachineType(cluster *api.Cluster) string {
 | 
						|
	switch fi.CloudProviderID(cluster.Spec.CloudProvider) {
 | 
						|
	case fi.CloudProviderAWS:
 | 
						|
		return defaultBastionMachineTypeAWS
 | 
						|
	case fi.CloudProviderGCE:
 | 
						|
		return defaultBastionMachineTypeGCE
 | 
						|
	default:
 | 
						|
		glog.V(2).Infof("Cannot set default MachineType for CloudProvider=%q", cluster.Spec.CloudProvider)
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// defaultImage returns the default Image, based on the cloudprovider
 | 
						|
func defaultImage(cluster *api.Cluster, channel *api.Channel) string {
 | 
						|
	if channel != nil {
 | 
						|
		var kubernetesVersion *semver.Version
 | 
						|
		if cluster.Spec.KubernetesVersion != "" {
 | 
						|
			var err error
 | 
						|
			kubernetesVersion, err = util.ParseKubernetesVersion(cluster.Spec.KubernetesVersion)
 | 
						|
			if err != nil {
 | 
						|
				glog.Warningf("cannot parse KubernetesVersion %q in cluster", cluster.Spec.KubernetesVersion)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if kubernetesVersion != nil {
 | 
						|
			image := channel.FindImage(fi.CloudProviderID(cluster.Spec.CloudProvider), *kubernetesVersion)
 | 
						|
			if image != nil {
 | 
						|
				return image.Name
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	glog.Infof("Cannot set default Image for CloudProvider=%q", cluster.Spec.CloudProvider)
 | 
						|
	return ""
 | 
						|
}
 |