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 }