github.com/kubernetes-incubator/kube-aws@v0.16.4/pkg/api/worker_node_pool.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/kubernetes-incubator/kube-aws/logger"
     7  	"github.com/kubernetes-incubator/kube-aws/naming"
     8  )
     9  
    10  type WorkerNodePool struct {
    11  	Experimental        `yaml:",inline"`
    12  	Kubelet             `yaml:",inline"`
    13  	KubeClusterSettings `yaml:",inline"`
    14  	DeploymentSettings  `yaml:",inline"`
    15  
    16  	Plugins      PluginConfigs `yaml:"kubeAwsPlugins,omitempty"`
    17  	Private      bool          `yaml:"private,omitempty"`
    18  	NodePoolName string        `yaml:"name,omitempty"`
    19  
    20  	APIEndpointName           string           `yaml:"apiEndpointName,omitempty"`
    21  	AutoScalingGroup          AutoScalingGroup `yaml:"autoScalingGroup,omitempty"`
    22  	SpotFleet                 SpotFleet        `yaml:"spotFleet,omitempty"`
    23  	EC2Instance               `yaml:",inline"`
    24  	IAMConfig                 IAMConfig              `yaml:"iam,omitempty"`
    25  	SpotPrice                 string                 `yaml:"spotPrice,omitempty"`
    26  	SecurityGroupIds          []string               `yaml:"securityGroupIds,omitempty"`
    27  	CustomSettings            map[string]interface{} `yaml:"customSettings,omitempty"`
    28  	VolumeMounts              []NodeVolumeMount      `yaml:"volumeMounts,omitempty"`
    29  	Raid0Mounts               []Raid0Mount           `yaml:"raid0Mounts,omitempty"`
    30  	NodeSettings              `yaml:",inline"`
    31  	NodeStatusUpdateFrequency string              `yaml:"nodeStatusUpdateFrequency"`
    32  	CustomFiles               []CustomFile        `yaml:"customFiles,omitempty"`
    33  	CustomSystemdUnits        []CustomSystemdUnit `yaml:"customSystemdUnits,omitempty"`
    34  	Gpu                       Gpu                 `yaml:"gpu"`
    35  	NodePoolRollingStrategy   string              `yaml:"nodePoolRollingStrategy,omitempty"`
    36  	UnknownKeys               `yaml:",inline"`
    37  }
    38  
    39  func (c *WorkerNodePool) UnmarshalYAML(unmarshal func(interface{}) error) error {
    40  	type t WorkerNodePool
    41  	work := t(NewDefaultNodePoolConfig())
    42  	if err := unmarshal(&work); err != nil {
    43  		return fmt.Errorf("failed to parse node pool config: %v", err)
    44  	}
    45  	*c = WorkerNodePool(work)
    46  
    47  	return nil
    48  }
    49  
    50  func NewDefaultNodePoolConfig() WorkerNodePool {
    51  	return WorkerNodePool{
    52  		SpotFleet: newDefaultSpotFleet(),
    53  		EC2Instance: EC2Instance{
    54  			Count:         1,
    55  			CreateTimeout: "PT15M",
    56  			InstanceType:  "t2.medium",
    57  			RootVolume: RootVolume{
    58  				Type: "gp2",
    59  				IOPS: 0,
    60  				Size: 30,
    61  			},
    62  			Tenancy: "default",
    63  		},
    64  		NodeSettings:     newNodeSettings(),
    65  		SecurityGroupIds: []string{},
    66  		Gpu:              newDefaultGpu(),
    67  	}
    68  }
    69  
    70  func newDefaultSpotFleet() SpotFleet {
    71  	return SpotFleet{
    72  		SpotPrice:          "0.06",
    73  		UnitRootVolumeSize: 30,
    74  		RootVolumeType:     "gp2",
    75  		LaunchSpecifications: []LaunchSpecification{
    76  			NewLaunchSpecification(1, "c4.large"),
    77  			NewLaunchSpecification(2, "c4.xlarge"),
    78  		},
    79  	}
    80  }
    81  
    82  func (c WorkerNodePool) LogicalName() string {
    83  	return "Workers"
    84  }
    85  
    86  func (c WorkerNodePool) LaunchConfigurationLogicalName() string {
    87  	return c.LogicalName() + "LC"
    88  }
    89  
    90  func (c WorkerNodePool) LaunchTemplateLogicalName() string {
    91  	return c.LogicalName() + "LT"
    92  }
    93  
    94  // NodePoolLogicalName returns a sanitized name of this pool which is usable as a valid cloudformation nested stack name
    95  func (c WorkerNodePool) NodePoolLogicalName() string {
    96  	return naming.FromStackToCfnResource(c.NodePoolName)
    97  }
    98  
    99  func (c WorkerNodePool) validate(experimentalGpuSupportEnabled bool) error {
   100  	// one is the default WorkerCount
   101  	if c.Count != 1 && (c.AutoScalingGroup.MinSize != nil && *c.AutoScalingGroup.MinSize != 0 || c.AutoScalingGroup.MaxSize != 0) {
   102  		return fmt.Errorf("`worker.autoScalingGroup.minSize` and `worker.autoScalingGroup.maxSize` can only be specified without `count`=%d", c.Count)
   103  	}
   104  
   105  	if err := c.AutoScalingGroup.Validate(); err != nil {
   106  		return err
   107  	}
   108  
   109  	if c.Tenancy != "default" && c.SpotFleet.Enabled() {
   110  		return fmt.Errorf("selected worker tenancy (%s) is incompatible with spot fleet", c.Tenancy)
   111  	}
   112  
   113  	if c.Tenancy != "default" && c.SpotPrice != "" {
   114  		return fmt.Errorf("selected worker tenancy (%s) is incompatible with spot instances", c.Tenancy)
   115  	}
   116  
   117  	if err := c.RootVolume.Validate(); err != nil {
   118  		return err
   119  	}
   120  
   121  	if err := c.SpotFleet.Validate(); c.SpotFleet.Enabled() && err != nil {
   122  		return err
   123  	}
   124  
   125  	if err := ValidateVolumeMounts(c.VolumeMounts); err != nil {
   126  		return err
   127  	}
   128  
   129  	// c.VolumeMounts are supplied to check for device and path overlaps with contents of c.Raid0Mounts.
   130  	if err := ValidateRaid0Mounts(c.VolumeMounts, c.Raid0Mounts); err != nil {
   131  		return err
   132  	}
   133  
   134  	if c.InstanceType == "t2.micro" || c.InstanceType == "t2.nano" {
   135  		logger.Warnf(`instance types "t2.nano" and "t2.micro" are not recommended. See https://github.com/kubernetes-incubator/kube-aws/issues/258 for more information`)
   136  	}
   137  
   138  	if err := c.IAMConfig.Validate(); err != nil {
   139  		return err
   140  	}
   141  
   142  	if err := c.Gpu.Validate(c.InstanceType, experimentalGpuSupportEnabled); err != nil {
   143  		return err
   144  	}
   145  
   146  	// By design, kube-aws doesn't allow customizing the following settings among node pools.
   147  	//
   148  	// Every node pool imports subnets from the main stack and therefore there's no need for setting:
   149  	// * VPC.ID(FromStackOutput)
   150  	// * InternetGateway.ID(FromStackOutput)
   151  	// * RouteTableID
   152  	// * VPCCIDR
   153  	// * InstanceCIDR
   154  	// * MapPublicIPs
   155  	// * ElasticFileSystemID
   156  	if c.VPC.HasIdentifier() {
   157  		return fmt.Errorf("although you can't customize VPC per node pool but you did specify \"%v\" in your cluster.yaml", c.VPC)
   158  	}
   159  	if c.InternetGateway.HasIdentifier() {
   160  		return fmt.Errorf("although you can't customize internet gateway per node pool but you did specify \"%v\" in your cluster.yaml", c.InternetGateway)
   161  	}
   162  	if c.VPCCIDR != "" {
   163  		return fmt.Errorf("although you can't customize `vpcCIDR` per node pool but you did specify \"%s\" in your cluster.yaml", c.VPCCIDR)
   164  	}
   165  	if c.InstanceCIDR != "" {
   166  		return fmt.Errorf("although you can't customize `instanceCIDR` per node pool but you did specify \"%s\" in your cluster.yaml", c.InstanceCIDR)
   167  	}
   168  
   169  	// Believing it is impossible to mix different values, we also forbid customization of:
   170  	// * Region
   171  	// * ContainerRuntime
   172  	// * KMSKeyARN
   173  
   174  	if !c.Region.IsEmpty() {
   175  		return fmt.Errorf("although you can't customize `region` per node pool but you did specify \"%s\" in your cluster.yaml", c.Region)
   176  	}
   177  	if c.ContainerRuntime != "" {
   178  		return fmt.Errorf("although you can't customize `containerRuntime` per node pool but you did specify \"%s\" in your cluster.yaml", c.ContainerRuntime)
   179  	}
   180  	if c.KMSKeyARN != "" {
   181  		return fmt.Errorf("although you can't customize `kmsKeyArn` per node pool but you did specify \"%s\" in your cluster.yaml", c.KMSKeyARN)
   182  	}
   183  
   184  	if err := c.Experimental.Validate(c.NodePoolName); err != nil {
   185  		return err
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  func (c WorkerNodePool) MinCount() int {
   192  	if c.AutoScalingGroup.MinSize == nil {
   193  		return c.Count
   194  	}
   195  	return *c.AutoScalingGroup.MinSize
   196  }
   197  
   198  func (c WorkerNodePool) MaxCount() int {
   199  	if c.AutoScalingGroup.MaxSize == 0 {
   200  		return c.MinCount()
   201  	}
   202  	return c.AutoScalingGroup.MaxSize
   203  }
   204  
   205  func (c WorkerNodePool) RollingUpdateMinInstancesInService() int {
   206  	if c.AutoScalingGroup.RollingUpdateMinInstancesInService == nil {
   207  		if c.NodePoolRollingStrategy == "AvailabilityZone" {
   208  			return 0
   209  		}
   210  		if c.MaxCount() > 0 {
   211  			return c.MaxCount() - 1
   212  		}
   213  		return 0
   214  	}
   215  	return *c.AutoScalingGroup.RollingUpdateMinInstancesInService
   216  }
   217  
   218  func (c WorkerNodePool) Validate(experimental Experimental) error {
   219  	return c.validate(experimental.GpuSupport.Enabled)
   220  }
   221  
   222  func (c WorkerNodePool) WithDefaultsFrom(main DefaultWorkerSettings) WorkerNodePool {
   223  	if c.RootVolume.Type == "" {
   224  		c.RootVolume.Type = main.WorkerRootVolumeType
   225  	}
   226  
   227  	if c.RootVolume.IOPS == 0 && c.RootVolume.Type == "io1" {
   228  		c.RootVolume.IOPS = main.WorkerRootVolumeIOPS
   229  	}
   230  
   231  	if c.SpotFleet.RootVolumeType == "" {
   232  		c.SpotFleet.RootVolumeType = c.RootVolume.Type
   233  	}
   234  
   235  	if c.RootVolume.Size == 0 {
   236  		c.RootVolume.Size = main.WorkerRootVolumeSize
   237  	}
   238  
   239  	if c.Tenancy == "" {
   240  		c.Tenancy = main.WorkerTenancy
   241  	}
   242  
   243  	if c.InstanceType == "" {
   244  		c.InstanceType = main.WorkerInstanceType
   245  	}
   246  
   247  	if c.CreateTimeout == "" {
   248  		c.CreateTimeout = main.WorkerCreateTimeout
   249  	}
   250  
   251  	return c
   252  }