Skip to content

Commit 23f8487

Browse files
authored
Add Services Support for EKS and EMR (#72)
* support service aggregate filters for eks and emr * add eks tests * add virt type test * add service tests * add emr tests * address pr comments * add semver license
1 parent db22031 commit 23f8487

File tree

16 files changed

+2744
-8
lines changed

16 files changed

+2744
-8
lines changed

THIRD_PARTY_LICENSES

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,3 +784,63 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
784784
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
785785
THE SOFTWARE.
786786

787+
------
788+
789+
** github.com/imdario/mergo; version v0.3.11 --
790+
https://github.com/imdario/mergo
791+
792+
Copyright (c) 2013 Dario Castañé. All rights reserved.
793+
Copyright (c) 2012 The Go Authors. All rights reserved.
794+
795+
Redistribution and use in source and binary forms, with or without
796+
modification, are permitted provided that the following conditions are
797+
met:
798+
799+
* Redistributions of source code must retain the above copyright
800+
notice, this list of conditions and the following disclaimer.
801+
* Redistributions in binary form must reproduce the above
802+
copyright notice, this list of conditions and the following disclaimer
803+
in the documentation and/or other materials provided with the
804+
distribution.
805+
* Neither the name of Google Inc. nor the names of its
806+
contributors may be used to endorse or promote products derived from
807+
this software without specific prior written permission.
808+
809+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
810+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
811+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
812+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
813+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
814+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
815+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
816+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
817+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
818+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
819+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
820+
821+
------
822+
823+
** github.com/blang/semver; version v4.0.0 --
824+
https://github.com/blang/semver
825+
826+
The MIT License
827+
828+
Copyright (c) 2014 Benedikt Lang <github at benediktlang.de>
829+
830+
Permission is hereby granted, free of charge, to any person obtaining a copy
831+
of this software and associated documentation files (the "Software"), to deal
832+
in the Software without restriction, including without limitation the rights
833+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
834+
copies of the Software, and to permit persons to whom the Software is
835+
furnished to do so, subject to the following conditions:
836+
837+
The above copyright notice and this permission notice shall be included in
838+
all copies or substantial portions of the Software.
839+
840+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
841+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
842+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
843+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
844+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
845+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
846+
THE SOFTWARE.

cmd/main.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,14 @@ const (
6868
networkPerformance = "network-performance"
6969
allowList = "allow-list"
7070
denyList = "deny-list"
71+
virtualizationType = "virtualization-type"
7172
)
7273

7374
// Aggregate Filter Flags
7475
const (
7576
instanceTypeBase = "base-instance-type"
7677
flexible = "flexible"
78+
service = "service"
7779
)
7880

7981
// Configuration Flag Constants
@@ -121,29 +123,31 @@ Full docs can be found at github.com/aws/amazon-` + binName
121123
cli.IntMinMaxRangeFlags(vcpus, cli.StringMe("c"), nil, "Number of vcpus available to the instance type.")
122124
cli.ByteQuantityMinMaxRangeFlags(memory, cli.StringMe("m"), nil, "Amount of Memory available (Example: 4 GiB)")
123125
cli.RatioFlag(vcpusToMemoryRatio, nil, nil, "The ratio of vcpus to GiBs of memory. (Example: 1:2)")
124-
cli.StringFlag(cpuArchitecture, cli.StringMe("a"), nil, "CPU architecture [x86_64/amd64, i386, or arm64]", nil)
126+
cli.StringOptionsFlag(cpuArchitecture, cli.StringMe("a"), nil, "CPU architecture [x86_64/amd64, i386, or arm64]", []string{"x86_64", "amd64", "i386", "arm64"})
125127
cli.IntMinMaxRangeFlags(gpus, cli.StringMe("g"), nil, "Total Number of GPUs (Example: 4)")
126128
cli.ByteQuantityMinMaxRangeFlags(gpuMemoryTotal, nil, nil, "Number of GPUs' total memory (Example: 4 GiB)")
127-
cli.StringFlag(placementGroupStrategy, nil, nil, "Placement group strategy: [cluster, partition, spread]", nil)
128-
cli.StringFlag(usageClass, cli.StringMe("u"), nil, "Usage class: [spot or on-demand]", nil)
129-
cli.StringFlag(rootDeviceType, nil, nil, "Supported root device types: [ebs or instance-store]", nil)
129+
cli.StringOptionsFlag(placementGroupStrategy, nil, nil, "Placement group strategy: [cluster, partition, spread]", []string{"cluster", "partition", "spread"})
130+
cli.StringOptionsFlag(usageClass, cli.StringMe("u"), nil, "Usage class: [spot or on-demand]", []string{"spot", "on-demand"})
131+
cli.StringOptionsFlag(rootDeviceType, nil, nil, "Supported root device types: [ebs or instance-store]", []string{"ebs", "instance-store"})
130132
cli.BoolFlag(enaSupport, cli.StringMe("e"), nil, "Instance types where ENA is supported or required")
131133
cli.BoolFlag(hibernationSupport, nil, nil, "Hibernation supported")
132134
cli.BoolFlag(baremetal, nil, nil, "Bare Metal instance types (.metal instances)")
133135
cli.BoolFlag(fpgaSupport, cli.StringMe("f"), nil, "FPGA instance types")
134136
cli.BoolFlag(burstSupport, cli.StringMe("b"), nil, "Burstable instance types")
135-
cli.StringFlag(hypervisor, nil, nil, "Hypervisor: [xen or nitro]", nil)
137+
cli.StringOptionsFlag(hypervisor, nil, nil, "Hypervisor: [xen or nitro]", []string{"xen", "nitro"})
136138
cli.StringSliceFlag(availabilityZones, cli.StringMe("z"), nil, "Availability zones or zone ids to check EC2 capacity offered in specific AZs")
137139
cli.BoolFlag(currentGeneration, nil, nil, "Current generation instance types (explicitly set this to false to not return current generation instance types)")
138140
cli.IntMinMaxRangeFlags(networkInterfaces, nil, nil, "Number of network interfaces (ENIs) that can be attached to the instance")
139141
cli.IntMinMaxRangeFlags(networkPerformance, nil, nil, "Bandwidth in Gib/s of network performance (Example: 100)")
140142
cli.RegexFlag(allowList, nil, nil, "List of allowed instance types to select from w/ regex syntax (Example: m[3-5]\\.*)")
141143
cli.RegexFlag(denyList, nil, nil, "List of instance types which should be excluded w/ regex syntax (Example: m[1-2]\\.*)")
144+
cli.StringOptionsFlag(virtualizationType, nil, nil, "Virtualization Type supported: [hvm or pv]", []string{"hvm", "paravirtual", "pv"})
142145

143146
// Suite Flags - higher level aggregate filters that return opinionated result
144147

145148
cli.SuiteStringFlag(instanceTypeBase, nil, nil, "Instance Type used to retrieve similarly spec'd instance types", nil)
146149
cli.SuiteBoolFlag(flexible, nil, nil, "Retrieves a group of instance types spanning multiple generations based on opinionated defaults and user overridden resource filters")
150+
cli.SuiteStringFlag(service, nil, nil, "Filter instance types based on service support (Example: eks, eks-20201211, or emr-5.20.0)", nil)
147151

148152
// Configuration Flags - These will be grouped at the bottom of the help flags
149153

@@ -206,6 +210,8 @@ Full docs can be found at github.com/aws/amazon-` + binName
206210
DenyList: cli.RegexMe(flags[denyList]),
207211
InstanceTypeBase: cli.StringMe(flags[instanceTypeBase]),
208212
Flexible: cli.BoolMe(flags[flexible]),
213+
Service: cli.StringMe(flags[service]),
214+
VirtualizationType: cli.StringMe(flags[virtualizationType]),
209215
}
210216

211217
if flags[verbose] != nil {

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ go 1.15
44

55
require (
66
github.com/aws/aws-sdk-go v1.31.12
7+
github.com/blang/semver/v4 v4.0.0
78
github.com/ghodss/yaml v1.0.0
89
github.com/hashicorp/hcl v1.0.0
10+
github.com/imdario/mergo v0.3.11
911
github.com/mitchellh/go-homedir v1.1.0
1012
github.com/smartystreets/goconvey v1.6.4 // indirect
1113
github.com/spf13/cobra v0.0.7

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ github.com/aws/aws-sdk-go v1.31.12 h1:SxRRGyhlCagI0DYkhOg+FgdXGXzRTE3vEX/gsgFaiK
88
github.com/aws/aws-sdk-go v1.31.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
99
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
1010
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
11+
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
12+
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
1113
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
1214
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
1315
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -49,6 +51,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
4951
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
5052
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
5153
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
54+
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
55+
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
5256
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
5357
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
5458
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
@@ -166,4 +170,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
166170
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
167171
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
168172
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
173+
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
174+
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
169175
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

pkg/selector/aggregates.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (itf Selector) TransformBaseInstanceType(filters Filters) (Filters, error)
4848
if filters.BareMetal == nil {
4949
filters.BareMetal = instanceTypeInfo.BareMetal
5050
}
51-
if filters.CPUArchitecture == nil {
51+
if filters.CPUArchitecture == nil && len(instanceTypeInfo.ProcessorInfo.SupportedArchitectures) == 1 {
5252
filters.CPUArchitecture = instanceTypeInfo.ProcessorInfo.SupportedArchitectures[0]
5353
}
5454
if filters.Fpga == nil {
@@ -72,6 +72,9 @@ func (itf Selector) TransformBaseInstanceType(filters Filters) (Filters, error)
7272
upperBound := int(float64(*instanceTypeInfo.VCpuInfo.DefaultVCpus) * AggregateHighPercentile)
7373
filters.VCpusRange = &IntRangeFilter{LowerBound: lowerBound, UpperBound: upperBound}
7474
}
75+
if filters.VirtualizationType == nil && len(instanceTypeInfo.SupportedVirtualizationTypes) == 1 {
76+
filters.VirtualizationType = instanceTypeInfo.SupportedVirtualizationTypes[0]
77+
}
7578
filters.InstanceTypeBase = nil
7679

7780
return filters, nil
@@ -107,3 +110,8 @@ func (itf Selector) TransformFlexible(filters Filters) (Filters, error) {
107110

108111
return filters, nil
109112
}
113+
114+
// TransformForService transforms lower level filters based on the service
115+
func (itf Selector) TransformForService(filters Filters) (Filters, error) {
116+
return itf.ServiceRegistry.ExecuteTransforms(filters)
117+
}

pkg/selector/eks.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package selector
15+
16+
import (
17+
"archive/zip"
18+
"bytes"
19+
"fmt"
20+
"io/ioutil"
21+
"log"
22+
"net/http"
23+
"strings"
24+
25+
"github.com/aws/aws-sdk-go/aws"
26+
)
27+
28+
const (
29+
eksAMIRepoURL = "https://github.com/awslabs/amazon-eks-ami"
30+
eksFallbackLatestAMIVersion = "v20210125"
31+
eksInstanceTypesFile = "eni-max-pods.txt"
32+
)
33+
34+
// EKS is a Service type for a custom service filter transform
35+
type EKS struct {
36+
AMIRepoURL string
37+
}
38+
39+
// Filters implements the Service interface contract for EKS
40+
func (e *EKS) Filters(version string) (Filters, error) {
41+
if e.AMIRepoURL == "" {
42+
e.AMIRepoURL = eksAMIRepoURL
43+
}
44+
filters := Filters{}
45+
46+
if version == "" {
47+
var err error
48+
version, err = e.getLatestAMIVersion()
49+
if err != nil {
50+
log.Printf("There was a problem fetching the latest EKS AMI version, using hardcoded fallback version %s\n", eksFallbackLatestAMIVersion)
51+
version = eksFallbackLatestAMIVersion
52+
}
53+
}
54+
if !strings.HasPrefix(version, "v") {
55+
version = fmt.Sprintf("v%s", version)
56+
}
57+
supportedInstanceTypes, err := e.getSupportedInstanceTypes(version)
58+
if err != nil {
59+
log.Printf("Unable to retrieve EKS supported instance types for version %s: %v", version, err)
60+
return filters, err
61+
}
62+
filters.InstanceTypes = &supportedInstanceTypes
63+
filters.VirtualizationType = aws.String("hvm")
64+
return filters, nil
65+
}
66+
67+
func (e *EKS) getSupportedInstanceTypes(version string) ([]string, error) {
68+
supportedInstanceTypes := []string{}
69+
resp, err := http.Get(fmt.Sprintf("%s/archive/%s.zip", e.AMIRepoURL, version))
70+
if err != nil {
71+
return supportedInstanceTypes, err
72+
}
73+
74+
defer resp.Body.Close()
75+
if resp.StatusCode != 200 {
76+
return supportedInstanceTypes, fmt.Errorf("Unable to retrieve EKS supported instance types, got non-200 status code: %d", resp.StatusCode)
77+
}
78+
79+
body, err := ioutil.ReadAll(resp.Body)
80+
if err != nil {
81+
return supportedInstanceTypes, err
82+
}
83+
84+
zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body)))
85+
if err != nil {
86+
return supportedInstanceTypes, err
87+
}
88+
89+
// Read all the files from zip archive
90+
for _, zipFile := range zipReader.File {
91+
filePathParts := strings.Split(zipFile.Name, "/")
92+
fileName := filePathParts[len(filePathParts)-1]
93+
if fileName == eksInstanceTypesFile {
94+
unzippedFileBytes, err := readZipFile(zipFile)
95+
if err != nil {
96+
log.Println(err)
97+
continue
98+
}
99+
supportedInstanceTypesFileBody := string(unzippedFileBytes)
100+
for _, line := range strings.Split(strings.Replace(supportedInstanceTypesFileBody, "\r\n", "\n", -1), "\n") {
101+
if !strings.HasPrefix(line, "#") {
102+
instanceType := strings.Split(line, " ")[0]
103+
supportedInstanceTypes = append(supportedInstanceTypes, instanceType)
104+
}
105+
}
106+
}
107+
}
108+
return supportedInstanceTypes, nil
109+
}
110+
111+
func (e EKS) getLatestAMIVersion() (string, error) {
112+
client := &http.Client{
113+
CheckRedirect: func(req *http.Request, via []*http.Request) error {
114+
return http.ErrUseLastResponse
115+
},
116+
}
117+
// Get latest version
118+
resp, err := client.Get(fmt.Sprintf("%s/releases/latest", e.AMIRepoURL))
119+
if err != nil {
120+
return "", err
121+
}
122+
if resp.StatusCode != 302 {
123+
return "", fmt.Errorf("Can't retrieve latest release from github because redirect was not sent")
124+
}
125+
versionRedirect := resp.Header.Get("location")
126+
pathParts := strings.Split(versionRedirect, "/")
127+
return pathParts[len(pathParts)-1], nil
128+
}
129+
130+
func readZipFile(zf *zip.File) ([]byte, error) {
131+
f, err := zf.Open()
132+
if err != nil {
133+
return nil, err
134+
}
135+
defer f.Close()
136+
return ioutil.ReadAll(f)
137+
}

0 commit comments

Comments
 (0)