mirror of https://github.com/kubernetes/kops.git
Merge pull request #8856 from rifelpet/machine-types-2
Use ec2.DescribeInstanceTypes for machine type info
This commit is contained in:
commit
d5b8e6c90f
7
Makefile
7
Makefile
|
|
@ -829,13 +829,6 @@ build-docs-netlify:
|
||||||
pip install -r ${KOPS_ROOT}/images/mkdocs/requirements.txt
|
pip install -r ${KOPS_ROOT}/images/mkdocs/requirements.txt
|
||||||
mkdocs build
|
mkdocs build
|
||||||
|
|
||||||
# Update machine_types.go
|
|
||||||
.PHONY: update-machine-types
|
|
||||||
update-machine-types:
|
|
||||||
go build -o hack/machine_types/machine_types ${KOPS_ROOT}/hack/machine_types/
|
|
||||||
hack/machine_types/machine_types --out upup/pkg/fi/cloudup/awsup/machine_types.go
|
|
||||||
go fmt upup/pkg/fi/cloudup/awsup/machine_types.go
|
|
||||||
|
|
||||||
#-----------------------------------------------------------
|
#-----------------------------------------------------------
|
||||||
# development targets
|
# development targets
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,3 +49,8 @@ func (m *MockEC2) DescribeInstancesPages(request *ec2.DescribeInstancesInput, ca
|
||||||
func (m *MockEC2) DescribeInstancesPagesWithContext(aws.Context, *ec2.DescribeInstancesInput, func(*ec2.DescribeInstancesOutput, bool) bool, ...request.Option) error {
|
func (m *MockEC2) DescribeInstancesPagesWithContext(aws.Context, *ec2.DescribeInstancesInput, func(*ec2.DescribeInstancesOutput, bool) bool, ...request.Option) error {
|
||||||
panic("Not implemented")
|
panic("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockEC2) DescribeInstanceTypes(*ec2.DescribeInstanceTypesInput) (*ec2.DescribeInstanceTypesOutput, error) {
|
||||||
|
klog.Warningf("MockEc2::DescribeInstanceTypes is stub-implemented")
|
||||||
|
return &ec2.DescribeInstanceTypesOutput{}, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1231,7 +1231,7 @@ func RunCreateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *Cr
|
||||||
}
|
}
|
||||||
|
|
||||||
strict := false
|
strict := false
|
||||||
err = validation.DeepValidate(cluster, instanceGroups, strict)
|
err = validation.DeepValidate(cluster, instanceGroups, strict, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1255,7 +1255,7 @@ func RunCreateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *Cr
|
||||||
fullInstanceGroups = append(fullInstanceGroups, fullGroup)
|
fullInstanceGroups = append(fullInstanceGroups, fullGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validation.DeepValidate(fullCluster, fullInstanceGroups, true)
|
err = validation.DeepValidate(fullCluster, fullInstanceGroups, true, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,12 @@ func RunCreateInstanceGroup(ctx context.Context, f *util.Factory, cmd *cobra.Com
|
||||||
return fmt.Errorf("unexpected object type: %T", obj)
|
return fmt.Errorf("unexpected object type: %T", obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validation.CrossValidateInstanceGroup(group, cluster).ToAggregate()
|
cloud, err := cloudup.BuildCloud(cluster)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validation.CrossValidateInstanceGroup(group, cluster, cloud).ToAggregate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,12 @@ func RunEditCluster(ctx context.Context, f *util.Factory, cmd *cobra.Command, ar
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validation.DeepValidate(fullCluster, instanceGroups, true)
|
cloud, err := cloudup.BuildCloud(fullCluster)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validation.DeepValidate(fullCluster, instanceGroups, true, cloud)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results = editResults{
|
results = editResults{
|
||||||
file: file,
|
file: file,
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,12 @@ func RunEditInstanceGroup(ctx context.Context, f *util.Factory, cmd *cobra.Comma
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validation.CrossValidateInstanceGroup(fullGroup, fullCluster).ToAggregate()
|
cloud, err := cloudup.BuildCloud(fullCluster)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = validation.CrossValidateInstanceGroup(fullGroup, fullCluster, cloud).ToAggregate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
# Significant changes
|
# Significant changes
|
||||||
|
|
||||||
|
* Clusters using the Amazon VPC CNI provider now perform an `ec2.DescribeInstanceTypes` call at instance launch time. In large clusters or AWS accounts this may lead to API throttling which could delay node readiness. If this becomes a problem please open a GitHub issue.
|
||||||
|
|
||||||
# Breaking changes
|
# Breaking changes
|
||||||
|
|
||||||
* Support for Kubernetes 1.9 and 1.10 has been removed.
|
* Support for Kubernetes 1.9 and 1.10 has been removed.
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/openstack/designate
|
||||||
k8s.io/kops/dnsprovider/pkg/dnsprovider/rrstype
|
k8s.io/kops/dnsprovider/pkg/dnsprovider/rrstype
|
||||||
k8s.io/kops/dnsprovider/pkg/dnsprovider/tests
|
k8s.io/kops/dnsprovider/pkg/dnsprovider/tests
|
||||||
k8s.io/kops/examples/kops-api-example
|
k8s.io/kops/examples/kops-api-example
|
||||||
k8s.io/kops/hack/machine_types
|
|
||||||
k8s.io/kops/kube-discovery/cmd/kube-discovery
|
k8s.io/kops/kube-discovery/cmd/kube-discovery
|
||||||
k8s.io/kops/node-authorizer/cmd/node-authorizer
|
k8s.io/kops/node-authorizer/cmd/node-authorizer
|
||||||
k8s.io/kops/node-authorizer/pkg/authorizers/alwaysallow
|
k8s.io/kops/node-authorizer/pkg/authorizers/alwaysallow
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
machine_types
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = ["machine_types.go"],
|
|
||||||
importpath = "k8s.io/kops/hack/machine_types",
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
deps = [
|
|
||||||
"//upup/pkg/fi/cloudup/awsup:go_default_library",
|
|
||||||
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
|
|
||||||
"//vendor/github.com/aws/aws-sdk-go/aws/session:go_default_library",
|
|
||||||
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
|
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
go_binary(
|
|
||||||
name = "machinetypes",
|
|
||||||
embed = [":go_default_library"],
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
)
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
Machine Types Generator
|
|
||||||
=======================
|
|
||||||
|
|
||||||
To prevent errors or lagging updates, we use this generator to update the known aws machine types
|
|
||||||
that are [hard coded in kops](https://github.com/kubernetes/kops/blob/7d7112c1e9a52d4f677db6bd98943d308ec9f581/upup/pkg/fi/cloudup/awsup/machine_types.go#L76).
|
|
||||||
|
|
||||||
This generator uses the AWS Pricing API to get most of it's info on what instance types are supported.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
```
|
|
||||||
make update-machine-types
|
|
||||||
git add .
|
|
||||||
git commit -am "Updated machine types"
|
|
||||||
```
|
|
||||||
|
|
||||||
TODO:
|
|
||||||
-----
|
|
||||||
* Cross reference other regions besides us-east-1. Currently we just look at one region to determine instance types.
|
|
||||||
|
|
@ -1,328 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2019 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 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"k8s.io/klog"
|
|
||||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
|
||||||
)
|
|
||||||
|
|
||||||
var outputPath = ""
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.StringVar(&outputPath, "out", outputPath, "file to write")
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if err := run(); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func run() error {
|
|
||||||
if outputPath == "" {
|
|
||||||
return fmt.Errorf("must specify output file with --out")
|
|
||||||
}
|
|
||||||
|
|
||||||
klog.Info("Beginning AWS Machine Refresh")
|
|
||||||
|
|
||||||
// These are instance types not available in every account
|
|
||||||
// If they're not available, they wont be in the ec2.DescribeInstanceTypes response
|
|
||||||
// so they are hardcoded here for reference.
|
|
||||||
// Note that the m6g instances do not have ENI or IP information
|
|
||||||
machines := []awsup.AWSMachineTypeInfo{
|
|
||||||
{
|
|
||||||
Name: "cr1.8xlarge",
|
|
||||||
MemoryGB: 244,
|
|
||||||
Cores: 32,
|
|
||||||
InstanceENIs: 8,
|
|
||||||
InstanceIPsPerENI: 30,
|
|
||||||
EphemeralDisks: []int{120, 120},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "hs1.8xlarge",
|
|
||||||
MemoryGB: 117,
|
|
||||||
Cores: 16,
|
|
||||||
InstanceENIs: 8,
|
|
||||||
InstanceIPsPerENI: 30,
|
|
||||||
EphemeralDisks: []int{2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "m6g.medium",
|
|
||||||
MemoryGB: 4,
|
|
||||||
Cores: 1,
|
|
||||||
InstanceENIs: 0,
|
|
||||||
InstanceIPsPerENI: 0,
|
|
||||||
EphemeralDisks: nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Name: "m6g.large",
|
|
||||||
MemoryGB: 8,
|
|
||||||
Cores: 2,
|
|
||||||
InstanceENIs: 0,
|
|
||||||
InstanceIPsPerENI: 0,
|
|
||||||
EphemeralDisks: nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Name: "m6g.xlarge",
|
|
||||||
MemoryGB: 16,
|
|
||||||
Cores: 4,
|
|
||||||
InstanceENIs: 0,
|
|
||||||
InstanceIPsPerENI: 0,
|
|
||||||
EphemeralDisks: nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Name: "m6g.2xlarge",
|
|
||||||
MemoryGB: 32,
|
|
||||||
Cores: 8,
|
|
||||||
InstanceENIs: 0,
|
|
||||||
InstanceIPsPerENI: 0,
|
|
||||||
EphemeralDisks: nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Name: "m6g.4xlarge",
|
|
||||||
MemoryGB: 64,
|
|
||||||
Cores: 16,
|
|
||||||
InstanceENIs: 0,
|
|
||||||
InstanceIPsPerENI: 0,
|
|
||||||
EphemeralDisks: nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Name: "m6g.8xlarge",
|
|
||||||
MemoryGB: 128,
|
|
||||||
Cores: 32,
|
|
||||||
InstanceENIs: 0,
|
|
||||||
InstanceIPsPerENI: 0,
|
|
||||||
EphemeralDisks: nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Name: "m6g.12xlarge",
|
|
||||||
MemoryGB: 192,
|
|
||||||
Cores: 48,
|
|
||||||
InstanceENIs: 0,
|
|
||||||
InstanceIPsPerENI: 0,
|
|
||||||
EphemeralDisks: nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Name: "m6g.16xlarge",
|
|
||||||
MemoryGB: 256,
|
|
||||||
Cores: 64,
|
|
||||||
InstanceENIs: 0,
|
|
||||||
InstanceIPsPerENI: 0,
|
|
||||||
EphemeralDisks: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
families := map[string]struct{}{
|
|
||||||
"cr1": {},
|
|
||||||
"hs1": {},
|
|
||||||
"m6g": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
config := aws.NewConfig()
|
|
||||||
// Give verbose errors on auth problems
|
|
||||||
config = config.WithCredentialsChainVerboseErrors(true)
|
|
||||||
// Default to us-east-1
|
|
||||||
config = config.WithRegion("us-east-1")
|
|
||||||
|
|
||||||
sess, err := session.NewSession()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
client := ec2.New(sess, config)
|
|
||||||
instanceTypes := make([]*ec2.InstanceTypeInfo, 0)
|
|
||||||
err = client.DescribeInstanceTypesPages(&ec2.DescribeInstanceTypesInput{},
|
|
||||||
func(page *ec2.DescribeInstanceTypesOutput, lastPage bool) bool {
|
|
||||||
instanceTypes = append(instanceTypes, page.InstanceTypes...)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var warnings []string
|
|
||||||
|
|
||||||
seen := map[string]bool{}
|
|
||||||
for _, typeInfo := range instanceTypes {
|
|
||||||
instanceType := *typeInfo.InstanceType
|
|
||||||
|
|
||||||
if _, ok := seen[instanceType]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[instanceType] = true
|
|
||||||
machine := awsup.AWSMachineTypeInfo{
|
|
||||||
Name: instanceType,
|
|
||||||
GPU: typeInfo.GpuInfo != nil,
|
|
||||||
InstanceENIs: intValue(typeInfo.NetworkInfo.MaximumNetworkInterfaces),
|
|
||||||
InstanceIPsPerENI: intValue(typeInfo.NetworkInfo.Ipv4AddressesPerInterface),
|
|
||||||
}
|
|
||||||
memoryGB := float64(intValue(typeInfo.MemoryInfo.SizeInMiB)) / 1024
|
|
||||||
machine.MemoryGB = float32(math.Round(memoryGB*100) / 100)
|
|
||||||
|
|
||||||
if typeInfo.VCpuInfo != nil && typeInfo.VCpuInfo.DefaultVCpus != nil {
|
|
||||||
machine.Cores = intValue(typeInfo.VCpuInfo.DefaultVCpus)
|
|
||||||
}
|
|
||||||
if typeInfo.InstanceStorageInfo != nil && len(typeInfo.InstanceStorageInfo.Disks) > 0 {
|
|
||||||
disks := make([]int, 0)
|
|
||||||
for _, disk := range typeInfo.InstanceStorageInfo.Disks {
|
|
||||||
for i := 0; i < intValue(disk.Count); i++ {
|
|
||||||
disks = append(disks, intValue(disk.SizeInGB))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
machine.EphemeralDisks = disks
|
|
||||||
}
|
|
||||||
|
|
||||||
machines = append(machines, machine)
|
|
||||||
|
|
||||||
family := strings.Split(instanceType, ".")[0]
|
|
||||||
families[family] = struct{}{}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sortedFamilies := []string{}
|
|
||||||
for f := range families {
|
|
||||||
sortedFamilies = append(sortedFamilies, f)
|
|
||||||
}
|
|
||||||
sort.Strings(sortedFamilies)
|
|
||||||
|
|
||||||
sort.Slice(machines, func(i, j int) bool {
|
|
||||||
// Sort first by family
|
|
||||||
tokensI := strings.Split(machines[i].Name, ".")
|
|
||||||
tokensJ := strings.Split(machines[j].Name, ".")
|
|
||||||
|
|
||||||
if tokensI[0] != tokensJ[0] {
|
|
||||||
return tokensI[0] < tokensJ[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then sort by size within the family
|
|
||||||
if machines[i].MemoryGB != machines[j].MemoryGB {
|
|
||||||
return machines[i].MemoryGB < machines[j].MemoryGB
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: sort by name
|
|
||||||
return machines[i].Name < machines[j].Name
|
|
||||||
})
|
|
||||||
|
|
||||||
var output string
|
|
||||||
|
|
||||||
if len(warnings) != 0 {
|
|
||||||
output = output + "\n"
|
|
||||||
for _, warning := range warnings {
|
|
||||||
output = output + "// WARNING: " + warning + "\n"
|
|
||||||
}
|
|
||||||
output = output + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range sortedFamilies {
|
|
||||||
output = output + fmt.Sprintf("\n// %s family", f)
|
|
||||||
for _, m := range machines {
|
|
||||||
if family := strings.Split(m.Name, ".")[0]; family == f {
|
|
||||||
|
|
||||||
body := fmt.Sprintf(`
|
|
||||||
{
|
|
||||||
Name: "%s",
|
|
||||||
MemoryGB: %v,
|
|
||||||
Cores: %v,
|
|
||||||
InstanceENIs: %v,
|
|
||||||
InstanceIPsPerENI: %v,
|
|
||||||
`, m.Name, m.MemoryGB, m.Cores, m.InstanceENIs, m.InstanceIPsPerENI)
|
|
||||||
output = output + body
|
|
||||||
|
|
||||||
// Avoid awkward []int(nil) syntax
|
|
||||||
if len(m.EphemeralDisks) == 0 {
|
|
||||||
output = output + "EphemeralDisks: nil,\n"
|
|
||||||
} else {
|
|
||||||
output = output + fmt.Sprintf("EphemeralDisks: %#v,\n", m.EphemeralDisks)
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.GPU {
|
|
||||||
output = output + "GPU: true,\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
output = output + "},\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output = output + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
klog.Infof("Writing changes to %v", outputPath)
|
|
||||||
|
|
||||||
fileInput, err := ioutil.ReadFile(outputPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error reading %s: %v", outputPath, err)
|
|
||||||
}
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(fileInput))
|
|
||||||
|
|
||||||
scanner.Split(bufio.ScanLines)
|
|
||||||
|
|
||||||
var newfile string
|
|
||||||
flag := false
|
|
||||||
done := false
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
|
|
||||||
if strings.Contains(line, "END GENERATED CONTENT") {
|
|
||||||
flag = false
|
|
||||||
done = true
|
|
||||||
}
|
|
||||||
if !flag {
|
|
||||||
newfile = newfile + line + "\n"
|
|
||||||
}
|
|
||||||
if strings.Contains(line, "BEGIN GENERATED CONTENT") {
|
|
||||||
flag = true
|
|
||||||
newfile = newfile + output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !done {
|
|
||||||
return fmt.Errorf("BEGIN GENERATED CONTENT / END GENERATED CONTENT markers not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(outputPath, []byte(newfile), 0644)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error writing %s: %v", outputPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
klog.Info("Done.")
|
|
||||||
klog.Flush()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func intValue(v *int64) int {
|
|
||||||
return int(aws.Int64Value(v))
|
|
||||||
}
|
|
||||||
|
|
@ -452,8 +452,16 @@ func (b *KubeletBuilder) buildKubeletConfigSpec() (*kops.KubeletConfigSpec, erro
|
||||||
instanceTypeName = strings.Split(b.InstanceGroup.Spec.MachineType, ",")[0]
|
instanceTypeName = strings.Split(b.InstanceGroup.Spec.MachineType, ",")[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
region, err := awsup.FindRegion(b.Cluster)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
awsCloud, err := awsup.NewAWSCloud(region, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// Get the instance type's detailed information.
|
// Get the instance type's detailed information.
|
||||||
instanceType, err := awsup.GetMachineTypeInfo(instanceTypeName)
|
instanceType, err := awsup.GetMachineTypeInfo(awsCloud, instanceTypeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ go_test(
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/apis/kops:go_default_library",
|
"//pkg/apis/kops:go_default_library",
|
||||||
"//upup/pkg/fi:go_default_library",
|
"//upup/pkg/fi:go_default_library",
|
||||||
|
"//upup/pkg/fi/cloudup/awsup:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/kops/pkg/apis/kops"
|
"k8s.io/kops/pkg/apis/kops"
|
||||||
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,17 +39,21 @@ func awsValidateCluster(c *kops.Cluster) field.ErrorList {
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func awsValidateInstanceGroup(ig *kops.InstanceGroup) field.ErrorList {
|
func awsValidateInstanceGroup(ig *kops.InstanceGroup, cloud awsup.AWSCloud) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
allErrs = append(allErrs, awsValidateAdditionalSecurityGroups(field.NewPath("spec", "additionalSecurityGroups"), ig.Spec.AdditionalSecurityGroups)...)
|
allErrs = append(allErrs, awsValidateAdditionalSecurityGroups(field.NewPath("spec", "additionalSecurityGroups"), ig.Spec.AdditionalSecurityGroups)...)
|
||||||
|
|
||||||
allErrs = append(allErrs, awsValidateMachineType(field.NewPath(ig.GetName(), "spec", "machineType"), ig.Spec.MachineType)...)
|
allErrs = append(allErrs, awsValidateInstanceType(field.NewPath(ig.GetName(), "spec", "machineType"), ig.Spec.MachineType, cloud)...)
|
||||||
|
|
||||||
allErrs = append(allErrs, awsValidateSpotDurationInMinute(field.NewPath(ig.GetName(), "spec", "spotDurationInMinutes"), ig)...)
|
allErrs = append(allErrs, awsValidateSpotDurationInMinute(field.NewPath(ig.GetName(), "spec", "spotDurationInMinutes"), ig)...)
|
||||||
|
|
||||||
allErrs = append(allErrs, awsValidateInstanceInterruptionBehavior(field.NewPath(ig.GetName(), "spec", "instanceInterruptionBehavior"), ig)...)
|
allErrs = append(allErrs, awsValidateInstanceInterruptionBehavior(field.NewPath(ig.GetName(), "spec", "instanceInterruptionBehavior"), ig)...)
|
||||||
|
|
||||||
|
if ig.Spec.MixedInstancesPolicy != nil {
|
||||||
|
allErrs = append(allErrs, awsValidateMixedInstancesPolicy(field.NewPath("spec", "mixedInstancesPolicy"), ig.Spec.MixedInstancesPolicy, ig, cloud)...)
|
||||||
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,12 +78,11 @@ func awsValidateAdditionalSecurityGroups(fieldPath *field.Path, groups []string)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func awsValidateMachineType(fieldPath *field.Path, machineType string) field.ErrorList {
|
func awsValidateInstanceType(fieldPath *field.Path, instanceType string, cloud awsup.AWSCloud) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
if instanceType != "" && cloud != nil {
|
||||||
if machineType != "" {
|
for _, typ := range strings.Split(instanceType, ",") {
|
||||||
for _, typ := range strings.Split(machineType, ",") {
|
if _, err := cloud.DescribeInstanceType(typ); err != nil {
|
||||||
if _, err := awsup.GetMachineTypeInfo(typ); err != nil {
|
|
||||||
allErrs = append(allErrs, field.Invalid(fieldPath, typ, "machine type specified is invalid"))
|
allErrs = append(allErrs, field.Invalid(fieldPath, typ, "machine type specified is invalid"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -106,3 +110,35 @@ func awsValidateInstanceInterruptionBehavior(fieldPath *field.Path, ig *kops.Ins
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// awsValidateMixedInstancesPolicy is responsible for validating the user input of a mixed instance policy
|
||||||
|
func awsValidateMixedInstancesPolicy(path *field.Path, spec *kops.MixedInstancesPolicySpec, ig *kops.InstanceGroup, cloud awsup.AWSCloud) field.ErrorList {
|
||||||
|
var errs field.ErrorList
|
||||||
|
|
||||||
|
// @step: check the instance types are valid
|
||||||
|
for i, x := range spec.Instances {
|
||||||
|
errs = append(errs, awsValidateInstanceType(path.Child("instances").Index(i), x, cloud)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.OnDemandBase != nil {
|
||||||
|
if fi.Int64Value(spec.OnDemandBase) < 0 {
|
||||||
|
errs = append(errs, field.Invalid(path.Child("onDemandBase"), spec.OnDemandBase, "cannot be less than zero"))
|
||||||
|
}
|
||||||
|
if fi.Int64Value(spec.OnDemandBase) > int64(fi.Int32Value(ig.Spec.MaxSize)) {
|
||||||
|
errs = append(errs, field.Invalid(path.Child("onDemandBase"), spec.OnDemandBase, "cannot be greater than max size"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.OnDemandAboveBase != nil {
|
||||||
|
if fi.Int64Value(spec.OnDemandAboveBase) < 0 {
|
||||||
|
errs = append(errs, field.Invalid(path.Child("onDemandAboveBase"), spec.OnDemandAboveBase, "cannot be less than 0"))
|
||||||
|
}
|
||||||
|
if fi.Int64Value(spec.OnDemandAboveBase) > 100 {
|
||||||
|
errs = append(errs, field.Invalid(path.Child("onDemandAboveBase"), spec.OnDemandAboveBase, "cannot be greater than 100"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errs = append(errs, IsValidValue(path.Child("spotAllocationStrategy"), spec.SpotAllocationStrategy, kops.SpotAllocationStrategies)...)
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kops/upup/pkg/fi"
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
|
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||||
|
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/kops/pkg/apis/kops"
|
"k8s.io/kops/pkg/apis/kops"
|
||||||
|
|
@ -143,6 +144,7 @@ func TestValidateInstanceGroupSpec(t *testing.T) {
|
||||||
ExpectedErrors: []string{},
|
ExpectedErrors: []string{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
cloud := awsup.BuildMockAWSCloud("us-east-1", "abc")
|
||||||
for _, g := range grid {
|
for _, g := range grid {
|
||||||
ig := &kops.InstanceGroup{
|
ig := &kops.InstanceGroup{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
|
@ -150,7 +152,7 @@ func TestValidateInstanceGroupSpec(t *testing.T) {
|
||||||
},
|
},
|
||||||
Spec: g.Input,
|
Spec: g.Input,
|
||||||
}
|
}
|
||||||
errs := awsValidateInstanceGroup(ig)
|
errs := awsValidateInstanceGroup(ig, cloud)
|
||||||
|
|
||||||
testErrors(t, g.Input, errs, g.ExpectedErrors)
|
testErrors(t, g.Input, errs, g.ExpectedErrors)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,11 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/kops/pkg/apis/kops"
|
"k8s.io/kops/pkg/apis/kops"
|
||||||
"k8s.io/kops/upup/pkg/fi"
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
|
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidateInstanceGroup is responsible for validating the configuration of a instancegroup
|
// ValidateInstanceGroup is responsible for validating the configuration of a instancegroup
|
||||||
func ValidateInstanceGroup(g *kops.InstanceGroup) field.ErrorList {
|
func ValidateInstanceGroup(g *kops.InstanceGroup, cloud fi.Cloud) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if g.ObjectMeta.Name == "" {
|
if g.ObjectMeta.Name == "" {
|
||||||
|
|
@ -75,10 +76,6 @@ func ValidateInstanceGroup(g *kops.InstanceGroup) field.ErrorList {
|
||||||
allErrs = append(allErrs, validateFileAssetSpec(&g.Spec.FileAssets[i], field.NewPath("spec", "fileAssets").Index(i))...)
|
allErrs = append(allErrs, validateFileAssetSpec(&g.Spec.FileAssets[i], field.NewPath("spec", "fileAssets").Index(i))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.Spec.MixedInstancesPolicy != nil {
|
|
||||||
allErrs = append(allErrs, validatedMixedInstancesPolicy(field.NewPath("spec", "mixedInstancesPolicy"), g.Spec.MixedInstancesPolicy, g)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, UserDataInfo := range g.Spec.AdditionalUserData {
|
for _, UserDataInfo := range g.Spec.AdditionalUserData {
|
||||||
allErrs = append(allErrs, validateExtraUserData(&UserDataInfo)...)
|
allErrs = append(allErrs, validateExtraUserData(&UserDataInfo)...)
|
||||||
}
|
}
|
||||||
|
|
@ -118,41 +115,13 @@ func ValidateInstanceGroup(g *kops.InstanceGroup) field.ErrorList {
|
||||||
allErrs = append(allErrs, validateRollingUpdate(g.Spec.RollingUpdate, field.NewPath("spec", "rollingUpdate"), g.Spec.Role == kops.InstanceGroupRoleMaster)...)
|
allErrs = append(allErrs, validateRollingUpdate(g.Spec.RollingUpdate, field.NewPath("spec", "rollingUpdate"), g.Spec.Role == kops.InstanceGroupRoleMaster)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cloud != nil && cloud.ProviderID() == kops.CloudProviderAWS {
|
||||||
|
allErrs = append(allErrs, awsValidateInstanceGroup(g, cloud.(awsup.AWSCloud))...)
|
||||||
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// validatedMixedInstancesPolicy is responsible for validating the user input of a mixed instance policy
|
|
||||||
func validatedMixedInstancesPolicy(path *field.Path, spec *kops.MixedInstancesPolicySpec, ig *kops.InstanceGroup) field.ErrorList {
|
|
||||||
var errs field.ErrorList
|
|
||||||
|
|
||||||
// @step: check the instances are validate
|
|
||||||
for i, x := range spec.Instances {
|
|
||||||
errs = append(errs, awsValidateMachineType(path.Child("instances").Index(i).Child("instanceType"), x)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if spec.OnDemandBase != nil {
|
|
||||||
if fi.Int64Value(spec.OnDemandBase) < 0 {
|
|
||||||
errs = append(errs, field.Invalid(path.Child("onDemandBase"), spec.OnDemandBase, "cannot be less than zero"))
|
|
||||||
}
|
|
||||||
if fi.Int64Value(spec.OnDemandBase) > int64(fi.Int32Value(ig.Spec.MaxSize)) {
|
|
||||||
errs = append(errs, field.Invalid(path.Child("onDemandBase"), spec.OnDemandBase, "cannot be greater than max size"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if spec.OnDemandAboveBase != nil {
|
|
||||||
if fi.Int64Value(spec.OnDemandAboveBase) < 0 {
|
|
||||||
errs = append(errs, field.Invalid(path.Child("onDemandAboveBase"), spec.OnDemandAboveBase, "cannot be less than 0"))
|
|
||||||
}
|
|
||||||
if fi.Int64Value(spec.OnDemandAboveBase) > 100 {
|
|
||||||
errs = append(errs, field.Invalid(path.Child("onDemandAboveBase"), spec.OnDemandAboveBase, "cannot be greater than 100"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errs = append(errs, IsValidValue(path.Child("spotAllocationStrategy"), spec.SpotAllocationStrategy, kops.SpotAllocationStrategies)...)
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateVolumeSpec is responsible for checking a volume spec is ok
|
// validateVolumeSpec is responsible for checking a volume spec is ok
|
||||||
func validateVolumeSpec(path *field.Path, v *kops.VolumeSpec) field.ErrorList {
|
func validateVolumeSpec(path *field.Path, v *kops.VolumeSpec) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
@ -187,8 +156,8 @@ func validateVolumeMountSpec(path *field.Path, spec *kops.VolumeMountSpec) field
|
||||||
|
|
||||||
// CrossValidateInstanceGroup performs validation of the instance group, including that it is consistent with the Cluster
|
// CrossValidateInstanceGroup performs validation of the instance group, including that it is consistent with the Cluster
|
||||||
// It calls ValidateInstanceGroup, so all that validation is included.
|
// It calls ValidateInstanceGroup, so all that validation is included.
|
||||||
func CrossValidateInstanceGroup(g *kops.InstanceGroup, cluster *kops.Cluster) field.ErrorList {
|
func CrossValidateInstanceGroup(g *kops.InstanceGroup, cluster *kops.Cluster, cloud fi.Cloud) field.ErrorList {
|
||||||
allErrs := ValidateInstanceGroup(g)
|
allErrs := ValidateInstanceGroup(g, cloud)
|
||||||
|
|
||||||
if g.Spec.Role == kops.InstanceGroupRoleMaster {
|
if g.Spec.Role == kops.InstanceGroupRoleMaster {
|
||||||
allErrs = append(allErrs, ValidateMasterInstanceGroup(g, cluster)...)
|
allErrs = append(allErrs, ValidateMasterInstanceGroup(g, cluster)...)
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ func TestValidBootDevice(t *testing.T) {
|
||||||
RootVolumeType: fi.String(g.volumeType),
|
RootVolumeType: fi.String(g.volumeType),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
errs := CrossValidateInstanceGroup(ig, cluster)
|
errs := CrossValidateInstanceGroup(ig, cluster, nil)
|
||||||
testErrors(t, g.volumeType, errs, g.expected)
|
testErrors(t, g.volumeType, errs, g.expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,7 @@ func validateSubnetCIDR(networkCIDR *net.IPNet, additionalNetworkCIDRs []*net.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepValidate is responsible for validating the instancegroups within the cluster spec
|
// DeepValidate is responsible for validating the instancegroups within the cluster spec
|
||||||
func DeepValidate(c *kops.Cluster, groups []*kops.InstanceGroup, strict bool) error {
|
func DeepValidate(c *kops.Cluster, groups []*kops.InstanceGroup, strict bool, cloud fi.Cloud) error {
|
||||||
if errs := ValidateCluster(c, strict); len(errs) != 0 {
|
if errs := ValidateCluster(c, strict); len(errs) != 0 {
|
||||||
return errs.ToAggregate()
|
return errs.ToAggregate()
|
||||||
}
|
}
|
||||||
|
|
@ -535,17 +535,11 @@ func DeepValidate(c *kops.Cluster, groups []*kops.InstanceGroup, strict bool) er
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, g := range groups {
|
for _, g := range groups {
|
||||||
errs := CrossValidateInstanceGroup(g, c)
|
errs := CrossValidateInstanceGroup(g, c, cloud)
|
||||||
|
|
||||||
// Additional cloud-specific validation rules,
|
// Additional cloud-specific validation rules
|
||||||
// such as making sure that identifiers match the expected formats for the given cloud
|
if kops.CloudProviderID(c.Spec.CloudProvider) != kops.CloudProviderAWS && len(g.Spec.Volumes) > 0 {
|
||||||
switch kops.CloudProviderID(c.Spec.CloudProvider) {
|
errs = append(errs, field.Forbidden(field.NewPath("spec", "volumes"), "instancegroup volumes are only available with aws at present"))
|
||||||
case kops.CloudProviderAWS:
|
|
||||||
errs = append(errs, awsValidateInstanceGroup(g)...)
|
|
||||||
default:
|
|
||||||
if len(g.Spec.Volumes) > 0 {
|
|
||||||
errs = append(errs, field.Forbidden(field.NewPath("spec", "volumes"), "instancegroup volumes are only available with aws at present"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ func NewInstanceGroupMirror(cluster *kopsapi.Cluster, configBase vfs.Path) Insta
|
||||||
}
|
}
|
||||||
r.init(kind, configBase.Join("instancegroup"), StoreVersion)
|
r.init(kind, configBase.Join("instancegroup"), StoreVersion)
|
||||||
r.validate = func(o runtime.Object) error {
|
r.validate = func(o runtime.Object) error {
|
||||||
return validation.ValidateInstanceGroup(o.(*kopsapi.InstanceGroup)).ToAggregate()
|
return validation.ValidateInstanceGroup(o.(*kopsapi.InstanceGroup), nil).ToAggregate()
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +80,7 @@ func newInstanceGroupVFS(c *VFSClientset, cluster *kopsapi.Cluster) *InstanceGro
|
||||||
}
|
}
|
||||||
r.init(kind, c.basePath.Join(clusterName, "instancegroup"), StoreVersion)
|
r.init(kind, c.basePath.Join(clusterName, "instancegroup"), StoreVersion)
|
||||||
r.validate = func(o runtime.Object) error {
|
r.validate = func(o runtime.Object) error {
|
||||||
return validation.ValidateInstanceGroup(o.(*kopsapi.InstanceGroup)).ToAggregate()
|
return validation.ValidateInstanceGroup(o.(*kopsapi.InstanceGroup), nil).ToAggregate()
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func UpdateCluster(ctx context.Context, clientset simple.Clientset, cluster *kop
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validation.DeepValidate(fullCluster, instanceGroups, true)
|
err = validation.DeepValidate(fullCluster, instanceGroups, true, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,12 +232,17 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validation.DeepValidate(c.Cluster, c.InstanceGroups, true)
|
cluster := c.Cluster
|
||||||
|
|
||||||
|
cloud, err := BuildCloud(cluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster := c.Cluster
|
err = validation.DeepValidate(c.Cluster, c.InstanceGroups, true, cloud)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if cluster.Spec.KubernetesVersion == "" {
|
if cluster.Spec.KubernetesVersion == "" {
|
||||||
return fmt.Errorf("KubernetesVersion not set")
|
return fmt.Errorf("KubernetesVersion not set")
|
||||||
|
|
@ -323,11 +328,6 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) error {
|
||||||
"mirrorSecrets": &fitasks.MirrorSecrets{},
|
"mirrorSecrets": &fitasks.MirrorSecrets{},
|
||||||
})
|
})
|
||||||
|
|
||||||
cloud, err := BuildCloud(cluster)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
region := ""
|
region := ""
|
||||||
project := ""
|
project := ""
|
||||||
|
|
||||||
|
|
@ -446,8 +446,6 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) error {
|
||||||
if len(sshPublicKeys) > 1 {
|
if len(sshPublicKeys) > 1 {
|
||||||
return fmt.Errorf("exactly one 'admin' SSH public key can be specified when running with AWS; please delete a key using `kops delete secret`")
|
return fmt.Errorf("exactly one 'admin' SSH public key can be specified when running with AWS; please delete a key using `kops delete secret`")
|
||||||
}
|
}
|
||||||
|
|
||||||
l.TemplateFunctions["MachineTypeInfo"] = awsup.GetMachineTypeInfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case kops.CloudProviderALI:
|
case kops.CloudProviderALI:
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
|
|
||||||
// buildEphemeralDevices looks up the machine type and discovery any ephemeral device mappings
|
// buildEphemeralDevices looks up the machine type and discovery any ephemeral device mappings
|
||||||
func buildEphemeralDevices(cloud awsup.AWSCloud, machineType string) (map[string]*BlockDeviceMapping, error) {
|
func buildEphemeralDevices(cloud awsup.AWSCloud, machineType string) (map[string]*BlockDeviceMapping, error) {
|
||||||
mt, err := awsup.GetMachineTypeInfo(machineType)
|
mt, err := awsup.GetMachineTypeInfo(cloud, machineType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to find instance type details on: %s, error: %s", machineType, err)
|
return nil, fmt.Errorf("failed to find instance type details on: %s, error: %s", machineType, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,9 @@ type AWSCloud interface {
|
||||||
// DefaultInstanceType determines a suitable instance type for the specified instance group
|
// DefaultInstanceType determines a suitable instance type for the specified instance group
|
||||||
DefaultInstanceType(cluster *kops.Cluster, ig *kops.InstanceGroup) (string, error)
|
DefaultInstanceType(cluster *kops.Cluster, ig *kops.InstanceGroup) (string, error)
|
||||||
|
|
||||||
|
// DescribeInstanceType calls ec2.DescribeInstanceType to get information for a particular instance type
|
||||||
|
DescribeInstanceType(instanceType string) (*ec2.InstanceTypeInfo, error)
|
||||||
|
|
||||||
// FindClusterStatus gets the status of the cluster as it exists in AWS, inferred from volumes
|
// FindClusterStatus gets the status of the cluster as it exists in AWS, inferred from volumes
|
||||||
FindClusterStatus(cluster *kops.Cluster) (*kops.ClusterStatus, error)
|
FindClusterStatus(cluster *kops.Cluster) (*kops.ClusterStatus, error)
|
||||||
}
|
}
|
||||||
|
|
@ -174,6 +177,8 @@ type awsCloudImplementation struct {
|
||||||
tags map[string]string
|
tags map[string]string
|
||||||
|
|
||||||
regionDelayers *RegionDelayers
|
regionDelayers *RegionDelayers
|
||||||
|
|
||||||
|
instanceTypes *instanceTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegionDelayers struct {
|
type RegionDelayers struct {
|
||||||
|
|
@ -181,6 +186,11 @@ type RegionDelayers struct {
|
||||||
delayerMap map[string]*k8s_aws.CrossRequestRetryDelay
|
delayerMap map[string]*k8s_aws.CrossRequestRetryDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type instanceTypes struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
typeMap map[string]*ec2.InstanceTypeInfo
|
||||||
|
}
|
||||||
|
|
||||||
var _ fi.Cloud = &awsCloudImplementation{}
|
var _ fi.Cloud = &awsCloudImplementation{}
|
||||||
|
|
||||||
func (c *awsCloudImplementation) ProviderID() kops.CloudProviderID {
|
func (c *awsCloudImplementation) ProviderID() kops.CloudProviderID {
|
||||||
|
|
@ -201,6 +211,9 @@ func NewAWSCloud(region string, tags map[string]string) (AWSCloud, error) {
|
||||||
regionDelayers: &RegionDelayers{
|
regionDelayers: &RegionDelayers{
|
||||||
delayerMap: make(map[string]*k8s_aws.CrossRequestRetryDelay),
|
delayerMap: make(map[string]*k8s_aws.CrossRequestRetryDelay),
|
||||||
},
|
},
|
||||||
|
instanceTypes: &instanceTypes{
|
||||||
|
typeMap: make(map[string]*ec2.InstanceTypeInfo),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
config := aws.NewConfig().WithRegion(region)
|
config := aws.NewConfig().WithRegion(region)
|
||||||
|
|
@ -1537,3 +1550,33 @@ func (c *awsCloudImplementation) zonesWithInstanceType(instanceType string) (set
|
||||||
|
|
||||||
return zones, nil
|
return zones, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DescribeInstanceType calls ec2.DescribeInstanceType to get information for a particular instance type
|
||||||
|
func (c *awsCloudImplementation) DescribeInstanceType(instanceType string) (*ec2.InstanceTypeInfo, error) {
|
||||||
|
if info, ok := c.instanceTypes.typeMap[instanceType]; ok {
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
c.instanceTypes.mutex.Lock()
|
||||||
|
defer c.instanceTypes.mutex.Unlock()
|
||||||
|
|
||||||
|
info, err := describeInstanceType(c, instanceType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.instanceTypes.typeMap[instanceType] = info
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func describeInstanceType(c AWSCloud, instanceType string) (*ec2.InstanceTypeInfo, error) {
|
||||||
|
req := &ec2.DescribeInstanceTypesInput{
|
||||||
|
InstanceTypes: aws.StringSlice([]string{instanceType}),
|
||||||
|
}
|
||||||
|
resp, err := c.EC2().DescribeInstanceTypes(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(resp.InstanceTypes) != 1 {
|
||||||
|
return nil, fmt.Errorf("invalid instance type specified: %v", instanceType)
|
||||||
|
}
|
||||||
|
return resp.InstanceTypes[0], nil
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -270,3 +270,33 @@ func (c *MockAWSCloud) DefaultInstanceType(cluster *kops.Cluster, ig *kops.Insta
|
||||||
return "", fmt.Errorf("MockAWSCloud DefaultInstanceType does not handle %s", ig.Spec.Role)
|
return "", fmt.Errorf("MockAWSCloud DefaultInstanceType does not handle %s", ig.Spec.Role)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DescribeInstanceType calls ec2.DescribeInstanceType to get information for a particular instance type
|
||||||
|
func (c *MockAWSCloud) DescribeInstanceType(instanceType string) (*ec2.InstanceTypeInfo, error) {
|
||||||
|
if instanceType == "t2.invalidType" {
|
||||||
|
return nil, fmt.Errorf("invalid instance type specified: t2.invalidType")
|
||||||
|
}
|
||||||
|
info := &ec2.InstanceTypeInfo{
|
||||||
|
NetworkInfo: &ec2.NetworkInfo{
|
||||||
|
MaximumNetworkInterfaces: aws.Int64(1),
|
||||||
|
Ipv4AddressesPerInterface: aws.Int64(1),
|
||||||
|
},
|
||||||
|
MemoryInfo: &ec2.MemoryInfo{
|
||||||
|
SizeInMiB: aws.Int64(1024),
|
||||||
|
},
|
||||||
|
VCpuInfo: &ec2.VCpuInfo{
|
||||||
|
DefaultVCpus: aws.Int64(2),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if instanceType == "m3.medium" {
|
||||||
|
info.InstanceStorageInfo = &ec2.InstanceStorageInfo{
|
||||||
|
Disks: []*ec2.DiskInfo{
|
||||||
|
{
|
||||||
|
Count: aws.Int64(1),
|
||||||
|
SizeInGB: aws.Int64(1024),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ func TestDeepValidate_OK(t *testing.T) {
|
||||||
var groups []*kopsapi.InstanceGroup
|
var groups []*kopsapi.InstanceGroup
|
||||||
groups = append(groups, buildMinimalMasterInstanceGroup("subnet-us-mock-1a"))
|
groups = append(groups, buildMinimalMasterInstanceGroup("subnet-us-mock-1a"))
|
||||||
groups = append(groups, buildMinimalNodeInstanceGroup("subnet-us-mock-1a"))
|
groups = append(groups, buildMinimalNodeInstanceGroup("subnet-us-mock-1a"))
|
||||||
err := validation.DeepValidate(c, groups, true)
|
err := validation.DeepValidate(c, groups, true, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error from DeepValidate, got %v", err)
|
t.Fatalf("Expected no error from DeepValidate, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -172,7 +172,7 @@ func TestDeepValidate_MissingEtcdMember(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectErrorFromDeepValidate(t *testing.T, c *kopsapi.Cluster, groups []*kopsapi.InstanceGroup, message string) {
|
func expectErrorFromDeepValidate(t *testing.T, c *kopsapi.Cluster, groups []*kopsapi.InstanceGroup, message string) {
|
||||||
err := validation.DeepValidate(c, groups, true)
|
err := validation.DeepValidate(c, groups, true, nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected error %q from DeepValidate (strict=true), not no error raised", message)
|
t.Fatalf("Expected error %q from DeepValidate (strict=true), not no error raised", message)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ const (
|
||||||
defaultALINodeImage = "centos_7_04_64_20G_alibase_201701015.vhd"
|
defaultALINodeImage = "centos_7_04_64_20G_alibase_201701015.vhd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: this hardcoded list can be replaced with DescribeInstanceTypes' DedicatedHostsSupported field
|
||||||
var awsDedicatedInstanceExceptions = map[string]bool{
|
var awsDedicatedInstanceExceptions = map[string]bool{
|
||||||
"t2.nano": true,
|
"t2.nano": true,
|
||||||
"t2.micro": true,
|
"t2.micro": true,
|
||||||
|
|
@ -60,7 +61,7 @@ var awsDedicatedInstanceExceptions = map[string]bool{
|
||||||
// The InstanceGroup is simpler than the cluster spec, so we just populate in place (like the rest of k8s)
|
// The InstanceGroup is simpler than the cluster spec, so we just populate in place (like the rest of k8s)
|
||||||
func PopulateInstanceGroupSpec(cluster *kops.Cluster, input *kops.InstanceGroup, channel *kops.Channel) (*kops.InstanceGroup, error) {
|
func PopulateInstanceGroupSpec(cluster *kops.Cluster, input *kops.InstanceGroup, channel *kops.Channel) (*kops.InstanceGroup, error) {
|
||||||
var err error
|
var err error
|
||||||
err = validation.ValidateInstanceGroup(input).ToAggregate()
|
err = validation.ValidateInstanceGroup(input, nil).ToAggregate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -528,7 +528,7 @@ func (_ *Elastigroup) create(cloud awsup.AWSCloud, a, e, changes *Elastigroup) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ephemeralDevices, err := e.buildEphemeralDevices(e.OnDemandInstanceType)
|
ephemeralDevices, err := e.buildEphemeralDevices(cloud, e.OnDemandInstanceType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -956,7 +956,7 @@ func (_ *Elastigroup) update(cloud awsup.AWSCloud, a, e, changes *Elastigroup) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ephemeralDevices, err := e.buildEphemeralDevices(e.OnDemandInstanceType)
|
ephemeralDevices, err := e.buildEphemeralDevices(cloud, e.OnDemandInstanceType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1494,7 +1494,7 @@ func (_ *Elastigroup) RenderTerraform(t *terraform.TerraformTarget, a, e, change
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ephemeralDevices, err := e.buildEphemeralDevices(e.OnDemandInstanceType)
|
ephemeralDevices, err := e.buildEphemeralDevices(cloud, e.OnDemandInstanceType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1638,12 +1638,12 @@ func (e *Elastigroup) buildAutoScaleLabels(labelsMap map[string]string) []*aws.A
|
||||||
return labels
|
return labels
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Elastigroup) buildEphemeralDevices(instanceTypeName *string) (map[string]*awstasks.BlockDeviceMapping, error) {
|
func (e *Elastigroup) buildEphemeralDevices(c awsup.AWSCloud, instanceTypeName *string) (map[string]*awstasks.BlockDeviceMapping, error) {
|
||||||
if instanceTypeName == nil {
|
if instanceTypeName == nil {
|
||||||
return nil, fi.RequiredField("InstanceType")
|
return nil, fi.RequiredField("InstanceType")
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceType, err := awsup.GetMachineTypeInfo(*instanceTypeName)
|
instanceType, err := awsup.GetMachineTypeInfo(c, *instanceTypeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue