Skip to content

Commit

Permalink
Set gp3 as the default VolumeType
Browse files Browse the repository at this point in the history
Also:
- Provide default settings for IOPS and Throughput for gp3 type
- Provide default setting for IOPS io1 type
- Ensure given IOPS and Throughput values are within accepted ranges
  • Loading branch information
Callisto13 committed Jan 18, 2021
1 parent b91c8b5 commit 0b434e7
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 35 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ require (
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect
github.com/vektra/mockery v0.0.0-20181123154057-e78b021dcbb5
github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2 // indirect
github.com/weaveworks/goformation/v4 v4.10.2-0.20201201143936-6016cb59d8ca
github.com/weaveworks/goformation/v4 v4.10.2-0.20210115145130-4565586043b7
github.com/weaveworks/launcher v0.0.2-0.20200715141516-1ca323f1de15
github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0
golang.org/x/tools v0.0.0-20210105210202-9ed45478a130
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1291,8 +1291,8 @@ github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2 h1:txplJASvd6b/hrE0s/Ixfpp2cuwH9IO9oZBAN9iYa4A=
github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2/go.mod h1:DGCIhurYgnLz8J9ga1fMV/fbLDyUvTyrWXVWUIyJon4=
github.com/weaveworks/goformation/v4 v4.10.2-0.20201201143936-6016cb59d8ca h1:QUYvB73KRYUJc4yy9tXu0bSiTZVt/OA6n0F6bjfvIOc=
github.com/weaveworks/goformation/v4 v4.10.2-0.20201201143936-6016cb59d8ca/go.mod h1:x92o12+Azh6DQ4yoXT5oEuE7dhQHR5V2vy/fmZ6pO7k=
github.com/weaveworks/goformation/v4 v4.10.2-0.20210115145130-4565586043b7 h1:Q4yLi7Ha3t7B6/XH4CWR69awDkkeWgiwa17be8zjIyI=
github.com/weaveworks/goformation/v4 v4.10.2-0.20210115145130-4565586043b7/go.mod h1:x92o12+Azh6DQ4yoXT5oEuE7dhQHR5V2vy/fmZ6pO7k=
github.com/weaveworks/launcher v0.0.2-0.20200715141516-1ca323f1de15 h1:i/RhLevywqC6cuUWtGdoaNrsJd+/zWh3PXbkXZIyZsU=
github.com/weaveworks/launcher v0.0.2-0.20200715141516-1ca323f1de15/go.mod h1:w9Z1vnQmPobkEZ0F3oyiqRYP+62qDqTGnK6t5uhe1kg=
github.com/weaveworks/mesh v0.0.0-20170419100114-1f158d31de55/go.mod h1:mcON9Ws1aW0crSErpXWp7U1ErCDEKliDX2OhVlbWRKk=
Expand Down
22 changes: 16 additions & 6 deletions pkg/apis/eksctl.io/v1alpha5/assets/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -910,13 +910,17 @@
"x-intellij-html-description": "gigabytes",
"default": 80
},
"volumeThroughput": {
"type": "integer"
},
"volumeType": {
"type": "string",
"description": "Valid variants are: `\"gp2\"` is General Purpose SSD (default), `\"io1\"` is Provisioned IOPS SSD, `\"sc1\"` is Cold HDD, `\"st1\"` is Throughput Optimized HDD.",
"x-intellij-html-description": "Valid variants are: <code>&quot;gp2&quot;</code> is General Purpose SSD (default), <code>&quot;io1&quot;</code> is Provisioned IOPS SSD, <code>&quot;sc1&quot;</code> is Cold HDD, <code>&quot;st1&quot;</code> is Throughput Optimized HDD.",
"default": "gp2",
"description": "Valid variants are: `\"gp2\"` is General Purpose SSD, `\"gp3\"` is General Purpose SSD which can be optimised for high throughput (default), `\"io1\"` is Provisioned IOPS SSD, `\"sc1\"` is Cold HDD, `\"st1\"` is Throughput Optimized HDD.",
"x-intellij-html-description": "Valid variants are: <code>&quot;gp2&quot;</code> is General Purpose SSD, <code>&quot;gp3&quot;</code> is General Purpose SSD which can be optimised for high throughput (default), <code>&quot;io1&quot;</code> is Provisioned IOPS SSD, <code>&quot;sc1&quot;</code> is Cold HDD, <code>&quot;st1&quot;</code> is Throughput Optimized HDD.",
"default": "gp3",
"enum": [
"gp2",
"gp3",
"io1",
"sc1",
"st1"
Expand Down Expand Up @@ -950,6 +954,7 @@
"volumeEncrypted",
"volumeKmsKeyID",
"volumeIOPS",
"volumeThroughput",
"preBootstrapCommands",
"overrideBootstrapCommand",
"disableIMDSv1",
Expand Down Expand Up @@ -1194,13 +1199,17 @@
"x-intellij-html-description": "gigabytes",
"default": 80
},
"volumeThroughput": {
"type": "integer"
},
"volumeType": {
"type": "string",
"description": "Valid variants are: `\"gp2\"` is General Purpose SSD (default), `\"io1\"` is Provisioned IOPS SSD, `\"sc1\"` is Cold HDD, `\"st1\"` is Throughput Optimized HDD.",
"x-intellij-html-description": "Valid variants are: <code>&quot;gp2&quot;</code> is General Purpose SSD (default), <code>&quot;io1&quot;</code> is Provisioned IOPS SSD, <code>&quot;sc1&quot;</code> is Cold HDD, <code>&quot;st1&quot;</code> is Throughput Optimized HDD.",
"default": "gp2",
"description": "Valid variants are: `\"gp2\"` is General Purpose SSD, `\"gp3\"` is General Purpose SSD which can be optimised for high throughput (default), `\"io1\"` is Provisioned IOPS SSD, `\"sc1\"` is Cold HDD, `\"st1\"` is Throughput Optimized HDD.",
"x-intellij-html-description": "Valid variants are: <code>&quot;gp2&quot;</code> is General Purpose SSD, <code>&quot;gp3&quot;</code> is General Purpose SSD which can be optimised for high throughput (default), <code>&quot;io1&quot;</code> is Provisioned IOPS SSD, <code>&quot;sc1&quot;</code> is Cold HDD, <code>&quot;st1&quot;</code> is Throughput Optimized HDD.",
"default": "gp3",
"enum": [
"gp2",
"gp3",
"io1",
"sc1",
"st1"
Expand Down Expand Up @@ -1234,6 +1243,7 @@
"volumeEncrypted",
"volumeKmsKeyID",
"volumeIOPS",
"volumeThroughput",
"preBootstrapCommands",
"overrideBootstrapCommand",
"disableIMDSv1",
Expand Down
14 changes: 14 additions & 0 deletions pkg/apis/eksctl.io/v1alpha5/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/aws/aws-sdk-go/aws"
"github.com/weaveworks/eksctl/pkg/git"
)

Expand Down Expand Up @@ -105,6 +106,19 @@ func SetNodeGroupDefaults(ng *NodeGroup, meta *ClusterMeta) {
ng.VolumeSize = &DefaultNodeVolumeSize
}

if *ng.VolumeType == NodeVolumeTypeGP3 {
if ng.VolumeIOPS == nil {
ng.VolumeIOPS = aws.Int(DefaultNodeVolumeGP3IOPS)
}
if ng.VolumeThroughput == nil {
ng.VolumeThroughput = aws.Int(DefaultNodeVolumeThroughput)
}
}

if *ng.VolumeType == NodeVolumeTypeIO1 && ng.VolumeIOPS == nil {
ng.VolumeIOPS = aws.Int(DefaultNodeVolumeIO1IOPS)
}

if ng.SecurityGroups.WithLocal == nil {
ng.SecurityGroups.WithLocal = Enabled()
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/apis/eksctl.io/v1alpha5/schema.go

Large diffs are not rendered by default.

15 changes: 13 additions & 2 deletions pkg/apis/eksctl.io/v1alpha5/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,10 @@ const (

// Values for `VolumeType`
const (
// NodeVolumeTypeGP2 is General Purpose SSD (default)
// NodeVolumeTypeGP2 is General Purpose SSD
NodeVolumeTypeGP2 = "gp2"
// NodeVolumeTypeGP3 is General Purpose SSD which can be optimised for high throughput (default)
NodeVolumeTypeGP3 = "gp3"
// NodeVolumeTypeIO1 is Provisioned IOPS SSD
NodeVolumeTypeIO1 = "io1"
// NodeVolumeTypeSC1 is Cold HDD
Expand All @@ -269,6 +271,12 @@ const (
NodeGroupTypeUnmanaged NodeGroupType = "unmanaged"
// NodeGroupTypeUnowned defines an unowned managed nodegroup
NodeGroupTypeUnowned NodeGroupType = "unowned"
// DefaultNodeVolumeThroughput defines the default throughput for gp3 volumes, set to the min value
DefaultNodeVolumeThroughput = 125
// DefaultNodeVolumeIO1IOPS defines the default throughput for io1 volumes, set to the min value
DefaultNodeVolumeIO1IOPS = 100
// DefaultNodeVolumeGP3IOPS defines the default throughput for gp3, set to the min value
DefaultNodeVolumeGP3IOPS = 3000
)

var (
Expand All @@ -279,7 +287,7 @@ var (
DefaultNodeSSHPublicKeyPath = "~/.ssh/id_rsa.pub"

// DefaultNodeVolumeType defines the default root volume type to use
DefaultNodeVolumeType = NodeVolumeTypeGP2
DefaultNodeVolumeType = NodeVolumeTypeGP3

// DefaultNodeVolumeSize defines the default root volume size
DefaultNodeVolumeSize = 80
Expand Down Expand Up @@ -397,6 +405,7 @@ func IsSupportedVersion(version string) bool {
func SupportedNodeVolumeTypes() []string {
return []string{
NodeVolumeTypeGP2,
NodeVolumeTypeGP3,
NodeVolumeTypeIO1,
NodeVolumeTypeSC1,
NodeVolumeTypeST1,
Expand Down Expand Up @@ -1146,6 +1155,8 @@ type NodeGroupBase struct {
VolumeKmsKeyID *string `json:"volumeKmsKeyID,omitempty"`
// +optional
VolumeIOPS *int `json:"volumeIOPS,omitempty"`
// +optional
VolumeThroughput *int `json:"volumeThroughput,omitempty"`

// PreBootstrapCommands are executed before bootstrapping instances to the
// cluster
Expand Down
46 changes: 40 additions & 6 deletions pkg/apis/eksctl.io/v1alpha5/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ import (
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
)

// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-blockdevicemapping-ebs.html
const (
minThroughput = DefaultNodeVolumeThroughput
maxThroughput = 1000
minIO1Iops = DefaultNodeVolumeIO1IOPS
maxIO1Iops = 64000
minGP3Iops = DefaultNodeVolumeGP3IOPS
maxGP3Iops = 16000
)

var (
// ErrClusterEndpointNoAccess indicates the config prevents API access
ErrClusterEndpointNoAccess = errors.New("Kubernetes API access must have one of public or private clusterEndpoints enabled")
Expand Down Expand Up @@ -189,12 +199,8 @@ func validateNodeGroupBase(ng *NodeGroupBase, path string) error {
}
}

if ng.VolumeType != nil && *ng.VolumeType == NodeVolumeTypeIO1 {
if ng.VolumeIOPS == nil {
return fmt.Errorf("%s.volumeIOPS is required for %s volume type", path, NodeVolumeTypeIO1)
}
} else if ng.VolumeIOPS != nil {
return fmt.Errorf("%s.volumeIOPS is only supported for %s volume type", path, NodeVolumeTypeIO1)
if err := validateVolumeOpts(ng, path); err != nil {
return err
}

if ng.VolumeEncrypted == nil || IsDisabled(ng.VolumeEncrypted) {
Expand Down Expand Up @@ -228,6 +234,34 @@ func validateNodeGroupBase(ng *NodeGroupBase, path string) error {
return nil
}

func validateVolumeOpts(ng *NodeGroupBase, path string) error {
if ng.VolumeType != nil {
if ng.VolumeIOPS != nil && !(*ng.VolumeType == NodeVolumeTypeIO1 || *ng.VolumeType == NodeVolumeTypeGP3) {
return fmt.Errorf("%s.volumeIOPS is only supported for %s and %s volume types", path, NodeVolumeTypeIO1, NodeVolumeTypeGP3)
}

if *ng.VolumeType == NodeVolumeTypeIO1 {
if ng.VolumeIOPS != nil && !(*ng.VolumeIOPS >= minIO1Iops && *ng.VolumeIOPS <= maxIO1Iops) {
return fmt.Errorf("value for %s.volumeIOPS must be within range %d-%d", path, minIO1Iops, maxIO1Iops)
}
}

if ng.VolumeThroughput != nil && *ng.VolumeType != NodeVolumeTypeGP3 {
return fmt.Errorf("%s.volumeThroughput is only supported for %s volume type", path, NodeVolumeTypeGP3)
}
}

if ng.VolumeIOPS != nil && !(*ng.VolumeIOPS >= minGP3Iops && *ng.VolumeIOPS <= maxGP3Iops) {
return fmt.Errorf("value for %s.volumeIOPS must be within range %d-%d for ", path, minGP3Iops, maxGP3Iops)
}

if ng.VolumeThroughput != nil && !(*ng.VolumeThroughput >= minThroughput && *ng.VolumeThroughput <= maxThroughput) {
return fmt.Errorf("value for %s.volumeThroughput must be within range %d-%d", path, minThroughput, maxThroughput)
}

return nil
}

// ValidateNodeGroup checks compatible fields of a given nodegroup
func ValidateNodeGroup(i int, ng *NodeGroup) error {
path := fmt.Sprintf("nodeGroups[%d]", i)
Expand Down
125 changes: 120 additions & 5 deletions pkg/apis/eksctl.io/v1alpha5/validation_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package v1alpha5

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
"github.com/weaveworks/eksctl/pkg/utils/strings"
)

var _ = Describe("ClusterConfig validation", func() {
newNodeGroup := func() *NodeGroup {
return &NodeGroup{
NodeGroupBase: &NodeGroupBase{},
}
}
Describe("nodeGroups[*].name", func() {
var (
cfg *ClusterConfig
Expand Down Expand Up @@ -53,6 +51,117 @@ var _ = Describe("ClusterConfig validation", func() {
})
})

Describe("nodeGroups[*].volumeX", func() {
var (
cfg *ClusterConfig
ng0 *NodeGroup
)

BeforeEach(func() {
cfg = NewClusterConfig()
ng0 = cfg.NewNodeGroup()
ng0.Name = "ng0"
})

When("volumeIOPS is set", func() {
BeforeEach(func() {
ng0.VolumeIOPS = aws.Int(3000)
})

When("VolumeType is gp3", func() {
BeforeEach(func() {
*ng0.VolumeType = NodeVolumeTypeGP3
})

It("does not fail", func() {
Expect(ValidateNodeGroup(0, ng0)).To(Succeed())
})

When(fmt.Sprintf("the value of volumeIOPS is < %d", minGP3Iops), func() {
It("returns an error", func() {
ng0.VolumeIOPS = aws.Int(minGP3Iops - 1)
Expect(ValidateNodeGroup(0, ng0)).To(MatchError("value for nodeGroups[0].volumeIOPS must be within range 3000-16000"))
})
})

When(fmt.Sprintf("the value of volumeIOPS is > %d", maxGP3Iops), func() {
It("returns an error", func() {
ng0.VolumeIOPS = aws.Int(maxGP3Iops + 1)
Expect(ValidateNodeGroup(0, ng0)).To(MatchError("value for nodeGroups[0].volumeIOPS must be within range 3000-16000"))
})
})
})

When("VolumeType is io1", func() {
BeforeEach(func() {
*ng0.VolumeType = NodeVolumeTypeIO1
})

It("does not fail", func() {
Expect(ValidateNodeGroup(0, ng0)).To(Succeed())
})

When(fmt.Sprintf("the value of volumeIOPS is < %d", minIO1Iops), func() {
It("returns an error", func() {
ng0.VolumeIOPS = aws.Int(minIO1Iops - 1)
Expect(ValidateNodeGroup(0, ng0)).To(MatchError("value for nodeGroups[0].volumeIOPS must be within range 100-64000"))
})
})

When(fmt.Sprintf("the value of volumeIOPS is > %d", maxIO1Iops), func() {
It("returns an error", func() {
ng0.VolumeIOPS = aws.Int(maxIO1Iops + 1)
Expect(ValidateNodeGroup(0, ng0)).To(MatchError("value for nodeGroups[0].volumeIOPS must be within range 100-64000"))
})
})
})

When("VolumeType is one for which IOPS is not supported", func() {
It("returns an error", func() {
*ng0.VolumeType = NodeVolumeTypeGP2
Expect(ValidateNodeGroup(0, ng0)).To(MatchError("nodeGroups[0].volumeIOPS is only supported for io1 and gp3 volume types"))
})
})
})

When("volumeThroughput is set", func() {
BeforeEach(func() {
ng0.VolumeThroughput = aws.Int(125)
})

When("VolumeType is gp3", func() {
BeforeEach(func() {
*ng0.VolumeType = NodeVolumeTypeGP3
})

It("does not fail", func() {
Expect(ValidateNodeGroup(0, ng0)).To(Succeed())
})

When(fmt.Sprintf("the value of volumeThroughput is < %d", minThroughput), func() {
It("returns an error", func() {
ng0.VolumeThroughput = aws.Int(minThroughput - 1)
Expect(ValidateNodeGroup(0, ng0)).To(MatchError("value for nodeGroups[0].volumeThroughput must be within range 125-1000"))
})
})

When(fmt.Sprintf("the value of volumeIOPS is > %d", maxThroughput), func() {
It("returns an error", func() {
ng0.VolumeThroughput = aws.Int(maxThroughput + 1)
Expect(ValidateNodeGroup(0, ng0)).To(MatchError("value for nodeGroups[0].volumeThroughput must be within range 125-1000"))
})
})
})

When("VolumeType is one for which Throughput is not supported", func() {
It("returns an error", func() {
*ng0.VolumeType = NodeVolumeTypeGP2
Expect(ValidateNodeGroup(0, ng0)).To(MatchError("nodeGroups[0].volumeThroughput is only supported for gp3 volume type"))
})
})
})
})

Describe("nodeGroups[*].iam", func() {
var (
cfg *ClusterConfig
Expand Down Expand Up @@ -874,3 +983,9 @@ func newInt(value int) *int {
v := value
return &v
}

func newNodeGroup() *NodeGroup {
return &NodeGroup{
NodeGroupBase: &NodeGroupBase{},
}
}
5 changes: 5 additions & 0 deletions pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0b434e7

Please sign in to comment.