Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
105
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/BUILD
generated
vendored
Normal file
105
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/BUILD
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"aws.go",
|
||||
"aws_fakes.go",
|
||||
"aws_instancegroups.go",
|
||||
"aws_loadbalancer.go",
|
||||
"aws_metrics.go",
|
||||
"aws_routes.go",
|
||||
"aws_utils.go",
|
||||
"device_allocator.go",
|
||||
"instances.go",
|
||||
"log_handler.go",
|
||||
"regions.go",
|
||||
"retry_handler.go",
|
||||
"sets_ippermissions.go",
|
||||
"tags.go",
|
||||
"volumes.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/aws",
|
||||
deps = [
|
||||
"//pkg/api/v1/service:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/credentialprovider/aws:go_default_library",
|
||||
"//pkg/kubelet/apis:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws/awserr:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws/credentials:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws/ec2metadata:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws/request:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws/session:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/autoscaling:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/elb:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/elbv2:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/kms:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/sts:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
"//vendor/gopkg.in/gcfg.v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"aws_loadbalancer_test.go",
|
||||
"aws_test.go",
|
||||
"device_allocator_test.go",
|
||||
"instances_test.go",
|
||||
"regions_test.go",
|
||||
"retry_handler_test.go",
|
||||
"tags_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubelet/apis:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/elb:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/mock:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
11
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/OWNERS
generated
vendored
Normal file
11
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/OWNERS
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
approvers:
|
||||
- justinsb
|
||||
- zmerlynn
|
||||
- gnufied
|
||||
- jsafrane
|
||||
reviewers:
|
||||
- gnufied
|
||||
- jsafrane
|
||||
- justinsb
|
||||
- zmerlynn
|
||||
- chrislovecnm
|
4332
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws.go
generated
vendored
Normal file
4332
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
534
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_fakes.go
generated
vendored
Normal file
534
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_fakes.go
generated
vendored
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
Copyright 2017 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 aws
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/autoscaling"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/elb"
|
||||
"github.com/aws/aws-sdk-go/service/elbv2"
|
||||
"github.com/aws/aws-sdk-go/service/kms"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type FakeAWSServices struct {
|
||||
region string
|
||||
instances []*ec2.Instance
|
||||
selfInstance *ec2.Instance
|
||||
networkInterfacesMacs []string
|
||||
networkInterfacesPrivateIPs [][]string
|
||||
networkInterfacesVpcIDs []string
|
||||
|
||||
ec2 FakeEC2
|
||||
elb ELB
|
||||
elbv2 ELBV2
|
||||
asg *FakeASG
|
||||
metadata *FakeMetadata
|
||||
kms *FakeKMS
|
||||
}
|
||||
|
||||
func NewFakeAWSServices(clusterId string) *FakeAWSServices {
|
||||
s := &FakeAWSServices{}
|
||||
s.region = "us-east-1"
|
||||
s.ec2 = &FakeEC2Impl{aws: s}
|
||||
s.elb = &FakeELB{aws: s}
|
||||
s.elbv2 = &FakeELBV2{aws: s}
|
||||
s.asg = &FakeASG{aws: s}
|
||||
s.metadata = &FakeMetadata{aws: s}
|
||||
s.kms = &FakeKMS{aws: s}
|
||||
|
||||
s.networkInterfacesMacs = []string{"aa:bb:cc:dd:ee:00", "aa:bb:cc:dd:ee:01"}
|
||||
s.networkInterfacesVpcIDs = []string{"vpc-mac0", "vpc-mac1"}
|
||||
|
||||
selfInstance := &ec2.Instance{}
|
||||
selfInstance.InstanceId = aws.String("i-self")
|
||||
selfInstance.Placement = &ec2.Placement{
|
||||
AvailabilityZone: aws.String("us-east-1a"),
|
||||
}
|
||||
selfInstance.PrivateDnsName = aws.String("ip-172-20-0-100.ec2.internal")
|
||||
selfInstance.PrivateIpAddress = aws.String("192.168.0.1")
|
||||
selfInstance.PublicIpAddress = aws.String("1.2.3.4")
|
||||
s.selfInstance = selfInstance
|
||||
s.instances = []*ec2.Instance{selfInstance}
|
||||
|
||||
var tag ec2.Tag
|
||||
tag.Key = aws.String(TagNameKubernetesClusterLegacy)
|
||||
tag.Value = aws.String(clusterId)
|
||||
selfInstance.Tags = []*ec2.Tag{&tag}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *FakeAWSServices) WithAz(az string) *FakeAWSServices {
|
||||
if s.selfInstance.Placement == nil {
|
||||
s.selfInstance.Placement = &ec2.Placement{}
|
||||
}
|
||||
s.selfInstance.Placement.AvailabilityZone = aws.String(az)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *FakeAWSServices) Compute(region string) (EC2, error) {
|
||||
return s.ec2, nil
|
||||
}
|
||||
|
||||
func (s *FakeAWSServices) LoadBalancing(region string) (ELB, error) {
|
||||
return s.elb, nil
|
||||
}
|
||||
|
||||
func (s *FakeAWSServices) LoadBalancingV2(region string) (ELBV2, error) {
|
||||
return s.elbv2, nil
|
||||
}
|
||||
|
||||
func (s *FakeAWSServices) Autoscaling(region string) (ASG, error) {
|
||||
return s.asg, nil
|
||||
}
|
||||
|
||||
func (s *FakeAWSServices) Metadata() (EC2Metadata, error) {
|
||||
return s.metadata, nil
|
||||
}
|
||||
|
||||
func (s *FakeAWSServices) KeyManagement(region string) (KMS, error) {
|
||||
return s.kms, nil
|
||||
}
|
||||
|
||||
type FakeEC2 interface {
|
||||
EC2
|
||||
CreateSubnet(*ec2.Subnet) (*ec2.CreateSubnetOutput, error)
|
||||
RemoveSubnets()
|
||||
CreateRouteTable(*ec2.RouteTable) (*ec2.CreateRouteTableOutput, error)
|
||||
RemoveRouteTables()
|
||||
}
|
||||
|
||||
type FakeEC2Impl struct {
|
||||
aws *FakeAWSServices
|
||||
Subnets []*ec2.Subnet
|
||||
DescribeSubnetsInput *ec2.DescribeSubnetsInput
|
||||
RouteTables []*ec2.RouteTable
|
||||
DescribeRouteTablesInput *ec2.DescribeRouteTablesInput
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) {
|
||||
matches := []*ec2.Instance{}
|
||||
for _, instance := range ec2i.aws.instances {
|
||||
if request.InstanceIds != nil {
|
||||
if instance.InstanceId == nil {
|
||||
glog.Warning("Instance with no instance id: ", instance)
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, instanceID := range request.InstanceIds {
|
||||
if *instanceID == *instance.InstanceId {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if request.Filters != nil {
|
||||
allMatch := true
|
||||
for _, filter := range request.Filters {
|
||||
if !instanceMatchesFilter(instance, filter) {
|
||||
allMatch = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allMatch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
matches = append(matches, instance)
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) AttachVolume(request *ec2.AttachVolumeInput) (resp *ec2.VolumeAttachment, err error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DetachVolume(request *ec2.DetachVolumeInput) (resp *ec2.VolumeAttachment, err error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DescribeVolumes(request *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) CreateVolume(request *ec2.CreateVolumeInput) (resp *ec2.Volume, err error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DeleteVolume(request *ec2.DeleteVolumeInput) (resp *ec2.DeleteVolumeOutput, err error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) CreateSecurityGroup(*ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DeleteSecurityGroup(*ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) AuthorizeSecurityGroupIngress(*ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) RevokeSecurityGroupIngress(*ec2.RevokeSecurityGroupIngressInput) (*ec2.RevokeSecurityGroupIngressOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DescribeVolumeModifications(*ec2.DescribeVolumesModificationsInput) ([]*ec2.VolumeModification, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) ModifyVolume(*ec2.ModifyVolumeInput) (*ec2.ModifyVolumeOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) CreateSubnet(request *ec2.Subnet) (*ec2.CreateSubnetOutput, error) {
|
||||
ec2i.Subnets = append(ec2i.Subnets, request)
|
||||
response := &ec2.CreateSubnetOutput{
|
||||
Subnet: request,
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DescribeSubnets(request *ec2.DescribeSubnetsInput) ([]*ec2.Subnet, error) {
|
||||
ec2i.DescribeSubnetsInput = request
|
||||
return ec2i.Subnets, nil
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) RemoveSubnets() {
|
||||
ec2i.Subnets = ec2i.Subnets[:0]
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) CreateTags(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) ([]*ec2.RouteTable, error) {
|
||||
ec2i.DescribeRouteTablesInput = request
|
||||
return ec2i.RouteTables, nil
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) CreateRouteTable(request *ec2.RouteTable) (*ec2.CreateRouteTableOutput, error) {
|
||||
ec2i.RouteTables = append(ec2i.RouteTables, request)
|
||||
response := &ec2.CreateRouteTableOutput{
|
||||
RouteTable: request,
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) RemoveRouteTables() {
|
||||
ec2i.RouteTables = ec2i.RouteTables[:0]
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DeleteRoute(request *ec2.DeleteRouteInput) (*ec2.DeleteRouteOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) ModifyInstanceAttribute(request *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (ec2i *FakeEC2Impl) DescribeVpcs(request *ec2.DescribeVpcsInput) (*ec2.DescribeVpcsOutput, error) {
|
||||
return &ec2.DescribeVpcsOutput{Vpcs: []*ec2.Vpc{{CidrBlock: aws.String("172.20.0.0/16")}}}, nil
|
||||
}
|
||||
|
||||
type FakeMetadata struct {
|
||||
aws *FakeAWSServices
|
||||
}
|
||||
|
||||
func (m *FakeMetadata) GetMetadata(key string) (string, error) {
|
||||
networkInterfacesPrefix := "network/interfaces/macs/"
|
||||
i := m.aws.selfInstance
|
||||
if key == "placement/availability-zone" {
|
||||
az := ""
|
||||
if i.Placement != nil {
|
||||
az = aws.StringValue(i.Placement.AvailabilityZone)
|
||||
}
|
||||
return az, nil
|
||||
} else if key == "instance-id" {
|
||||
return aws.StringValue(i.InstanceId), nil
|
||||
} else if key == "local-hostname" {
|
||||
return aws.StringValue(i.PrivateDnsName), nil
|
||||
} else if key == "public-hostname" {
|
||||
return aws.StringValue(i.PublicDnsName), nil
|
||||
} else if key == "local-ipv4" {
|
||||
return aws.StringValue(i.PrivateIpAddress), nil
|
||||
} else if key == "public-ipv4" {
|
||||
return aws.StringValue(i.PublicIpAddress), nil
|
||||
} else if strings.HasPrefix(key, networkInterfacesPrefix) {
|
||||
if key == networkInterfacesPrefix {
|
||||
return strings.Join(m.aws.networkInterfacesMacs, "/\n") + "/\n", nil
|
||||
} else {
|
||||
keySplit := strings.Split(key, "/")
|
||||
macParam := keySplit[3]
|
||||
if len(keySplit) == 5 && keySplit[4] == "vpc-id" {
|
||||
for i, macElem := range m.aws.networkInterfacesMacs {
|
||||
if macParam == macElem {
|
||||
return m.aws.networkInterfacesVpcIDs[i], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(keySplit) == 5 && keySplit[4] == "local-ipv4s" {
|
||||
for i, macElem := range m.aws.networkInterfacesMacs {
|
||||
if macParam == macElem {
|
||||
return strings.Join(m.aws.networkInterfacesPrivateIPs[i], "/\n"), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
type FakeELB struct {
|
||||
aws *FakeAWSServices
|
||||
}
|
||||
|
||||
func (elb *FakeELB) CreateLoadBalancer(*elb.CreateLoadBalancerInput) (*elb.CreateLoadBalancerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) DeleteLoadBalancer(input *elb.DeleteLoadBalancerInput) (*elb.DeleteLoadBalancerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) DescribeLoadBalancers(input *elb.DescribeLoadBalancersInput) (*elb.DescribeLoadBalancersOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) AddTags(input *elb.AddTagsInput) (*elb.AddTagsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) RegisterInstancesWithLoadBalancer(*elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) DeregisterInstancesFromLoadBalancer(*elb.DeregisterInstancesFromLoadBalancerInput) (*elb.DeregisterInstancesFromLoadBalancerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) DetachLoadBalancerFromSubnets(*elb.DetachLoadBalancerFromSubnetsInput) (*elb.DetachLoadBalancerFromSubnetsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) AttachLoadBalancerToSubnets(*elb.AttachLoadBalancerToSubnetsInput) (*elb.AttachLoadBalancerToSubnetsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) CreateLoadBalancerListeners(*elb.CreateLoadBalancerListenersInput) (*elb.CreateLoadBalancerListenersOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) DeleteLoadBalancerListeners(*elb.DeleteLoadBalancerListenersInput) (*elb.DeleteLoadBalancerListenersOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) ApplySecurityGroupsToLoadBalancer(*elb.ApplySecurityGroupsToLoadBalancerInput) (*elb.ApplySecurityGroupsToLoadBalancerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) ConfigureHealthCheck(*elb.ConfigureHealthCheckInput) (*elb.ConfigureHealthCheckOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) CreateLoadBalancerPolicy(*elb.CreateLoadBalancerPolicyInput) (*elb.CreateLoadBalancerPolicyOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) SetLoadBalancerPoliciesForBackendServer(*elb.SetLoadBalancerPoliciesForBackendServerInput) (*elb.SetLoadBalancerPoliciesForBackendServerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) SetLoadBalancerPoliciesOfListener(input *elb.SetLoadBalancerPoliciesOfListenerInput) (*elb.SetLoadBalancerPoliciesOfListenerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) DescribeLoadBalancerPolicies(input *elb.DescribeLoadBalancerPoliciesInput) (*elb.DescribeLoadBalancerPoliciesOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) DescribeLoadBalancerAttributes(*elb.DescribeLoadBalancerAttributesInput) (*elb.DescribeLoadBalancerAttributesOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (elb *FakeELB) ModifyLoadBalancerAttributes(*elb.ModifyLoadBalancerAttributesInput) (*elb.ModifyLoadBalancerAttributesOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELB) expectDescribeLoadBalancers(loadBalancerName string) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
type FakeELBV2 struct {
|
||||
aws *FakeAWSServices
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) AddTags(input *elbv2.AddTagsInput) (*elbv2.AddTagsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) CreateLoadBalancer(*elbv2.CreateLoadBalancerInput) (*elbv2.CreateLoadBalancerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) DescribeLoadBalancers(*elbv2.DescribeLoadBalancersInput) (*elbv2.DescribeLoadBalancersOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) DeleteLoadBalancer(*elbv2.DeleteLoadBalancerInput) (*elbv2.DeleteLoadBalancerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) ModifyLoadBalancerAttributes(*elbv2.ModifyLoadBalancerAttributesInput) (*elbv2.ModifyLoadBalancerAttributesOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) DescribeLoadBalancerAttributes(*elbv2.DescribeLoadBalancerAttributesInput) (*elbv2.DescribeLoadBalancerAttributesOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) CreateTargetGroup(*elbv2.CreateTargetGroupInput) (*elbv2.CreateTargetGroupOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) DescribeTargetGroups(*elbv2.DescribeTargetGroupsInput) (*elbv2.DescribeTargetGroupsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) ModifyTargetGroup(*elbv2.ModifyTargetGroupInput) (*elbv2.ModifyTargetGroupOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) DeleteTargetGroup(*elbv2.DeleteTargetGroupInput) (*elbv2.DeleteTargetGroupOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) DescribeTargetHealth(input *elbv2.DescribeTargetHealthInput) (*elbv2.DescribeTargetHealthOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) DescribeTargetGroupAttributes(*elbv2.DescribeTargetGroupAttributesInput) (*elbv2.DescribeTargetGroupAttributesOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) ModifyTargetGroupAttributes(*elbv2.ModifyTargetGroupAttributesInput) (*elbv2.ModifyTargetGroupAttributesOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) RegisterTargets(*elbv2.RegisterTargetsInput) (*elbv2.RegisterTargetsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) DeregisterTargets(*elbv2.DeregisterTargetsInput) (*elbv2.DeregisterTargetsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) CreateListener(*elbv2.CreateListenerInput) (*elbv2.CreateListenerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) DescribeListeners(*elbv2.DescribeListenersInput) (*elbv2.DescribeListenersOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) DeleteListener(*elbv2.DeleteListenerInput) (*elbv2.DeleteListenerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
func (self *FakeELBV2) ModifyListener(*elbv2.ModifyListenerInput) (*elbv2.ModifyListenerOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (self *FakeELBV2) WaitUntilLoadBalancersDeleted(*elbv2.DescribeLoadBalancersInput) error {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
type FakeASG struct {
|
||||
aws *FakeAWSServices
|
||||
}
|
||||
|
||||
func (a *FakeASG) UpdateAutoScalingGroup(*autoscaling.UpdateAutoScalingGroupInput) (*autoscaling.UpdateAutoScalingGroupOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func (a *FakeASG) DescribeAutoScalingGroups(*autoscaling.DescribeAutoScalingGroupsInput) (*autoscaling.DescribeAutoScalingGroupsOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
type FakeKMS struct {
|
||||
aws *FakeAWSServices
|
||||
}
|
||||
|
||||
func (kms *FakeKMS) DescribeKey(*kms.DescribeKeyInput) (*kms.DescribeKeyOutput, error) {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func instanceMatchesFilter(instance *ec2.Instance, filter *ec2.Filter) bool {
|
||||
name := *filter.Name
|
||||
if name == "private-dns-name" {
|
||||
if instance.PrivateDnsName == nil {
|
||||
return false
|
||||
}
|
||||
return contains(filter.Values, *instance.PrivateDnsName)
|
||||
}
|
||||
|
||||
if name == "instance-state-name" {
|
||||
return contains(filter.Values, *instance.State.Name)
|
||||
}
|
||||
|
||||
if name == "tag-key" {
|
||||
for _, instanceTag := range instance.Tags {
|
||||
if contains(filter.Values, aws.StringValue(instanceTag.Key)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.HasPrefix(name, "tag:") {
|
||||
tagName := name[4:]
|
||||
for _, instanceTag := range instance.Tags {
|
||||
if aws.StringValue(instanceTag.Key) == tagName && contains(filter.Values, aws.StringValue(instanceTag.Value)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
panic("Unknown filter name: " + name)
|
||||
}
|
||||
|
||||
func contains(haystack []*string, needle string) bool {
|
||||
for _, s := range haystack {
|
||||
// (deliberately panic if s == nil)
|
||||
if needle == *s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
90
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_instancegroups.go
generated
vendored
Normal file
90
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_instancegroups.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright 2014 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 aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/autoscaling"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// AWSCloud implements InstanceGroups
|
||||
var _ InstanceGroups = &Cloud{}
|
||||
|
||||
// ResizeInstanceGroup sets the size of the specificed instancegroup Exported
|
||||
// so it can be used by the e2e tests, which don't want to instantiate a full
|
||||
// cloudprovider.
|
||||
func ResizeInstanceGroup(asg ASG, instanceGroupName string, size int) error {
|
||||
request := &autoscaling.UpdateAutoScalingGroupInput{
|
||||
AutoScalingGroupName: aws.String(instanceGroupName),
|
||||
MinSize: aws.Int64(int64(size)),
|
||||
MaxSize: aws.Int64(int64(size)),
|
||||
}
|
||||
if _, err := asg.UpdateAutoScalingGroup(request); err != nil {
|
||||
return fmt.Errorf("error resizing AWS autoscaling group: %q", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implement InstanceGroups.ResizeInstanceGroup
|
||||
// Set the size to the fixed size
|
||||
func (c *Cloud) ResizeInstanceGroup(instanceGroupName string, size int) error {
|
||||
return ResizeInstanceGroup(c.asg, instanceGroupName, size)
|
||||
}
|
||||
|
||||
// DescribeInstanceGroup gets info about the specified instancegroup
|
||||
// Exported so it can be used by the e2e tests,
|
||||
// which don't want to instantiate a full cloudprovider.
|
||||
func DescribeInstanceGroup(asg ASG, instanceGroupName string) (InstanceGroupInfo, error) {
|
||||
request := &autoscaling.DescribeAutoScalingGroupsInput{
|
||||
AutoScalingGroupNames: []*string{aws.String(instanceGroupName)},
|
||||
}
|
||||
response, err := asg.DescribeAutoScalingGroups(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing AWS autoscaling group (%s): %q", instanceGroupName, err)
|
||||
}
|
||||
|
||||
if len(response.AutoScalingGroups) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(response.AutoScalingGroups) > 1 {
|
||||
glog.Warning("AWS returned multiple autoscaling groups with name ", instanceGroupName)
|
||||
}
|
||||
group := response.AutoScalingGroups[0]
|
||||
return &awsInstanceGroup{group: group}, nil
|
||||
}
|
||||
|
||||
// Implement InstanceGroups.DescribeInstanceGroup
|
||||
// Queries the cloud provider for information about the specified instance group
|
||||
func (c *Cloud) DescribeInstanceGroup(instanceGroupName string) (InstanceGroupInfo, error) {
|
||||
return DescribeInstanceGroup(c.asg, instanceGroupName)
|
||||
}
|
||||
|
||||
// awsInstanceGroup implements InstanceGroupInfo
|
||||
var _ InstanceGroupInfo = &awsInstanceGroup{}
|
||||
|
||||
type awsInstanceGroup struct {
|
||||
group *autoscaling.Group
|
||||
}
|
||||
|
||||
// Implement InstanceGroupInfo.CurrentSize
|
||||
// The number of instances currently running under control of this group
|
||||
func (g *awsInstanceGroup) CurrentSize() (int, error) {
|
||||
return len(g.group.Instances), nil
|
||||
}
|
1496
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go
generated
vendored
Normal file
1496
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
161
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer_test.go
generated
vendored
Normal file
161
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer_test.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
Copyright 2017 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 aws
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestElbProtocolsAreEqual(t *testing.T) {
|
||||
grid := []struct {
|
||||
L *string
|
||||
R *string
|
||||
Expected bool
|
||||
}{
|
||||
{
|
||||
L: aws.String("http"),
|
||||
R: aws.String("http"),
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
L: aws.String("HTTP"),
|
||||
R: aws.String("http"),
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
L: aws.String("HTTP"),
|
||||
R: aws.String("TCP"),
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
L: aws.String(""),
|
||||
R: aws.String("TCP"),
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
L: aws.String(""),
|
||||
R: aws.String(""),
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
L: nil,
|
||||
R: aws.String(""),
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
L: aws.String(""),
|
||||
R: nil,
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
L: nil,
|
||||
R: nil,
|
||||
Expected: true,
|
||||
},
|
||||
}
|
||||
for _, g := range grid {
|
||||
actual := elbProtocolsAreEqual(g.L, g.R)
|
||||
if actual != g.Expected {
|
||||
t.Errorf("unexpected result from protocolsEquals(%v, %v)", g.L, g.R)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWSARNEquals(t *testing.T) {
|
||||
grid := []struct {
|
||||
L *string
|
||||
R *string
|
||||
Expected bool
|
||||
}{
|
||||
{
|
||||
L: aws.String("arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"),
|
||||
R: aws.String("arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"),
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
L: aws.String("ARN:AWS:ACM:US-EAST-1:123456789012:CERTIFICATE/12345678-1234-1234-1234-123456789012"),
|
||||
R: aws.String("arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"),
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
L: aws.String("arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"),
|
||||
R: aws.String(""),
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
L: aws.String(""),
|
||||
R: aws.String(""),
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
L: nil,
|
||||
R: aws.String(""),
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
L: aws.String(""),
|
||||
R: nil,
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
L: nil,
|
||||
R: nil,
|
||||
Expected: true,
|
||||
},
|
||||
}
|
||||
for _, g := range grid {
|
||||
actual := awsArnEquals(g.L, g.R)
|
||||
if actual != g.Expected {
|
||||
t.Errorf("unexpected result from awsArnEquals(%v, %v)", g.L, g.R)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNLB(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
annotations map[string]string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
"NLB annotation provided",
|
||||
map[string]string{"service.beta.kubernetes.io/aws-load-balancer-type": "nlb"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"NLB annotation has invalid value",
|
||||
map[string]string{"service.beta.kubernetes.io/aws-load-balancer-type": "elb"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"NLB annotation absent",
|
||||
map[string]string{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Logf("Running test case %s", test.name)
|
||||
got := isNLB(test.annotations)
|
||||
|
||||
if got != test.want {
|
||||
t.Errorf("Incorrect value for isNLB() case %s. Got %t, expected %t.", test.name, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
60
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_metrics.go
generated
vendored
Normal file
60
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_metrics.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2017 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 aws
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
var (
|
||||
awsAPIMetric = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "cloudprovider_aws_api_request_duration_seconds",
|
||||
Help: "Latency of AWS API calls",
|
||||
},
|
||||
[]string{"request"})
|
||||
|
||||
awsAPIErrorMetric = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "cloudprovider_aws_api_request_errors",
|
||||
Help: "AWS API errors",
|
||||
},
|
||||
[]string{"request"})
|
||||
|
||||
awsAPIThrottlesMetric = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "cloudprovider_aws_api_throttled_requests_total",
|
||||
Help: "AWS API throttled requests",
|
||||
},
|
||||
[]string{"operation_name"})
|
||||
)
|
||||
|
||||
func recordAWSMetric(actionName string, timeTaken float64, err error) {
|
||||
if err != nil {
|
||||
awsAPIErrorMetric.With(prometheus.Labels{"request": actionName}).Inc()
|
||||
} else {
|
||||
awsAPIMetric.With(prometheus.Labels{"request": actionName}).Observe(timeTaken)
|
||||
}
|
||||
}
|
||||
|
||||
func recordAWSThrottlesMetric(operation string) {
|
||||
awsAPIThrottlesMetric.With(prometheus.Labels{"operation_name": operation}).Inc()
|
||||
}
|
||||
|
||||
func registerMetrics() {
|
||||
prometheus.MustRegister(awsAPIMetric)
|
||||
prometheus.MustRegister(awsAPIErrorMetric)
|
||||
prometheus.MustRegister(awsAPIThrottlesMetric)
|
||||
}
|
218
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go
generated
vendored
Normal file
218
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
Copyright 2014 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 aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
func (c *Cloud) findRouteTable(clusterName string) (*ec2.RouteTable, error) {
|
||||
// This should be unnecessary (we already filter on TagNameKubernetesCluster,
|
||||
// and something is broken if cluster name doesn't match, but anyway...
|
||||
// TODO: All clouds should be cluster-aware by default
|
||||
var tables []*ec2.RouteTable
|
||||
|
||||
if c.cfg.Global.RouteTableID != "" {
|
||||
request := &ec2.DescribeRouteTablesInput{Filters: []*ec2.Filter{newEc2Filter("route-table-id", c.cfg.Global.RouteTableID)}}
|
||||
response, err := c.ec2.DescribeRouteTables(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables = response
|
||||
} else {
|
||||
request := &ec2.DescribeRouteTablesInput{Filters: c.tagging.addFilters(nil)}
|
||||
response, err := c.ec2.DescribeRouteTables(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, table := range response {
|
||||
if c.tagging.hasClusterTag(table.Tags) {
|
||||
tables = append(tables, table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(tables) == 0 {
|
||||
return nil, fmt.Errorf("unable to find route table for AWS cluster: %s", clusterName)
|
||||
}
|
||||
|
||||
if len(tables) != 1 {
|
||||
return nil, fmt.Errorf("found multiple matching AWS route tables for AWS cluster: %s", clusterName)
|
||||
}
|
||||
return tables[0], nil
|
||||
}
|
||||
|
||||
// ListRoutes implements Routes.ListRoutes
|
||||
// List all routes that match the filter
|
||||
func (c *Cloud) ListRoutes(ctx context.Context, clusterName string) ([]*cloudprovider.Route, error) {
|
||||
table, err := c.findRouteTable(clusterName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var routes []*cloudprovider.Route
|
||||
var instanceIDs []*string
|
||||
|
||||
for _, r := range table.Routes {
|
||||
instanceID := aws.StringValue(r.InstanceId)
|
||||
|
||||
if instanceID == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
instanceIDs = append(instanceIDs, &instanceID)
|
||||
}
|
||||
|
||||
instances, err := c.getInstancesByIDs(instanceIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, r := range table.Routes {
|
||||
destinationCIDR := aws.StringValue(r.DestinationCidrBlock)
|
||||
if destinationCIDR == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
route := &cloudprovider.Route{
|
||||
Name: clusterName + "-" + destinationCIDR,
|
||||
DestinationCIDR: destinationCIDR,
|
||||
}
|
||||
|
||||
// Capture blackhole routes
|
||||
if aws.StringValue(r.State) == ec2.RouteStateBlackhole {
|
||||
route.Blackhole = true
|
||||
routes = append(routes, route)
|
||||
continue
|
||||
}
|
||||
|
||||
// Capture instance routes
|
||||
instanceID := aws.StringValue(r.InstanceId)
|
||||
if instanceID != "" {
|
||||
instance, found := instances[instanceID]
|
||||
if found {
|
||||
route.TargetNode = mapInstanceToNodeName(instance)
|
||||
routes = append(routes, route)
|
||||
} else {
|
||||
glog.Warningf("unable to find instance ID %s in the list of instances being routed to", instanceID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
// Sets the instance attribute "source-dest-check" to the specified value
|
||||
func (c *Cloud) configureInstanceSourceDestCheck(instanceID string, sourceDestCheck bool) error {
|
||||
request := &ec2.ModifyInstanceAttributeInput{}
|
||||
request.InstanceId = aws.String(instanceID)
|
||||
request.SourceDestCheck = &ec2.AttributeBooleanValue{Value: aws.Bool(sourceDestCheck)}
|
||||
|
||||
_, err := c.ec2.ModifyInstanceAttribute(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error configuring source-dest-check on instance %s: %q", instanceID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRoute implements Routes.CreateRoute
|
||||
// Create the described route
|
||||
func (c *Cloud) CreateRoute(ctx context.Context, clusterName string, nameHint string, route *cloudprovider.Route) error {
|
||||
instance, err := c.getInstanceByNodeName(route.TargetNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// In addition to configuring the route itself, we also need to configure the instance to accept that traffic
|
||||
// On AWS, this requires turning source-dest checks off
|
||||
err = c.configureInstanceSourceDestCheck(aws.StringValue(instance.InstanceId), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table, err := c.findRouteTable(clusterName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var deleteRoute *ec2.Route
|
||||
for _, r := range table.Routes {
|
||||
destinationCIDR := aws.StringValue(r.DestinationCidrBlock)
|
||||
|
||||
if destinationCIDR != route.DestinationCIDR {
|
||||
continue
|
||||
}
|
||||
|
||||
if aws.StringValue(r.State) == ec2.RouteStateBlackhole {
|
||||
deleteRoute = r
|
||||
}
|
||||
}
|
||||
|
||||
if deleteRoute != nil {
|
||||
glog.Infof("deleting blackholed route: %s", aws.StringValue(deleteRoute.DestinationCidrBlock))
|
||||
|
||||
request := &ec2.DeleteRouteInput{}
|
||||
request.DestinationCidrBlock = deleteRoute.DestinationCidrBlock
|
||||
request.RouteTableId = table.RouteTableId
|
||||
|
||||
_, err = c.ec2.DeleteRoute(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting blackholed AWS route (%s): %q", aws.StringValue(deleteRoute.DestinationCidrBlock), err)
|
||||
}
|
||||
}
|
||||
|
||||
request := &ec2.CreateRouteInput{}
|
||||
// TODO: use ClientToken for idempotency?
|
||||
request.DestinationCidrBlock = aws.String(route.DestinationCIDR)
|
||||
request.InstanceId = instance.InstanceId
|
||||
request.RouteTableId = table.RouteTableId
|
||||
|
||||
_, err = c.ec2.CreateRoute(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating AWS route (%s): %q", route.DestinationCIDR, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRoute implements Routes.DeleteRoute
|
||||
// Delete the specified route
|
||||
func (c *Cloud) DeleteRoute(ctx context.Context, clusterName string, route *cloudprovider.Route) error {
|
||||
table, err := c.findRouteTable(clusterName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := &ec2.DeleteRouteInput{}
|
||||
request.DestinationCidrBlock = aws.String(route.DestinationCIDR)
|
||||
request.RouteTableId = table.RouteTableId
|
||||
|
||||
_, err = c.ec2.DeleteRoute(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting AWS route (%s): %q", route.DestinationCIDR, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
1350
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_test.go
generated
vendored
Normal file
1350
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
44
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_utils.go
generated
vendored
Normal file
44
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_utils.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2014 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 aws
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
func stringSetToPointers(in sets.String) []*string {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := make([]*string, 0, len(in))
|
||||
for k := range in {
|
||||
out = append(out, aws.String(k))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func stringSetFromPointers(in []*string) sets.String {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := sets.NewString()
|
||||
for i := range in {
|
||||
out.Insert(aws.StringValue(in[i]))
|
||||
}
|
||||
return out
|
||||
}
|
130
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/device_allocator.go
generated
vendored
Normal file
130
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/device_allocator.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright 2016 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 aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ExistingDevices is a map of assigned devices. Presence of a key with a device
|
||||
// name in the map means that the device is allocated. Value is irrelevant and
|
||||
// can be used for anything that DeviceAllocator user wants.
|
||||
// Only the relevant part of device name should be in the map, e.g. "ba" for
|
||||
// "/dev/xvdba".
|
||||
type ExistingDevices map[mountDevice]awsVolumeID
|
||||
|
||||
// On AWS, we should assign new (not yet used) device names to attached volumes.
|
||||
// If we reuse a previously used name, we may get the volume "attaching" forever,
|
||||
// see https://aws.amazon.com/premiumsupport/knowledge-center/ebs-stuck-attaching/.
|
||||
// DeviceAllocator finds available device name, taking into account already
|
||||
// assigned device names from ExistingDevices map. It tries to find the next
|
||||
// device name to the previously assigned one (from previous DeviceAllocator
|
||||
// call), so all available device names are used eventually and it minimizes
|
||||
// device name reuse.
|
||||
// All these allocations are in-memory, nothing is written to / read from
|
||||
// /dev directory.
|
||||
type DeviceAllocator interface {
|
||||
// GetNext returns a free device name or error when there is no free device
|
||||
// name. Only the device suffix is returned, e.g. "ba" for "/dev/xvdba".
|
||||
// It's up to the called to add appropriate "/dev/sd" or "/dev/xvd" prefix.
|
||||
GetNext(existingDevices ExistingDevices) (mountDevice, error)
|
||||
|
||||
// Deprioritize the device so as it can't be used immediately again
|
||||
Deprioritize(mountDevice)
|
||||
|
||||
// Lock the deviceAllocator
|
||||
Lock()
|
||||
|
||||
// Unlock the deviceAllocator
|
||||
Unlock()
|
||||
}
|
||||
|
||||
type deviceAllocator struct {
|
||||
possibleDevices map[mountDevice]int
|
||||
counter int
|
||||
deviceLock sync.Mutex
|
||||
}
|
||||
|
||||
var _ DeviceAllocator = &deviceAllocator{}
|
||||
|
||||
type devicePair struct {
|
||||
deviceName mountDevice
|
||||
deviceIndex int
|
||||
}
|
||||
|
||||
type devicePairList []devicePair
|
||||
|
||||
func (p devicePairList) Len() int { return len(p) }
|
||||
func (p devicePairList) Less(i, j int) bool { return p[i].deviceIndex < p[j].deviceIndex }
|
||||
func (p devicePairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// Allocates device names according to scheme ba..bz, ca..cz
|
||||
// it moves along the ring and always picks next device until
|
||||
// device list is exhausted.
|
||||
func NewDeviceAllocator() DeviceAllocator {
|
||||
possibleDevices := make(map[mountDevice]int)
|
||||
for _, firstChar := range []rune{'b', 'c'} {
|
||||
for i := 'a'; i <= 'z'; i++ {
|
||||
dev := mountDevice([]rune{firstChar, i})
|
||||
possibleDevices[dev] = 0
|
||||
}
|
||||
}
|
||||
return &deviceAllocator{
|
||||
possibleDevices: possibleDevices,
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// GetNext gets next available device from the pool, this function assumes that caller
|
||||
// holds the necessary lock on deviceAllocator
|
||||
func (d *deviceAllocator) GetNext(existingDevices ExistingDevices) (mountDevice, error) {
|
||||
for _, devicePair := range d.sortByCount() {
|
||||
if _, found := existingDevices[devicePair.deviceName]; !found {
|
||||
return devicePair.deviceName, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no devices are available")
|
||||
}
|
||||
|
||||
func (d *deviceAllocator) sortByCount() devicePairList {
|
||||
dpl := make(devicePairList, 0)
|
||||
for deviceName, deviceIndex := range d.possibleDevices {
|
||||
dpl = append(dpl, devicePair{deviceName, deviceIndex})
|
||||
}
|
||||
sort.Sort(dpl)
|
||||
return dpl
|
||||
}
|
||||
|
||||
func (d *deviceAllocator) Lock() {
|
||||
d.deviceLock.Lock()
|
||||
}
|
||||
|
||||
func (d *deviceAllocator) Unlock() {
|
||||
d.deviceLock.Unlock()
|
||||
}
|
||||
|
||||
// Deprioritize the device so as it can't be used immediately again
|
||||
func (d *deviceAllocator) Deprioritize(chosen mountDevice) {
|
||||
d.deviceLock.Lock()
|
||||
defer d.deviceLock.Unlock()
|
||||
if _, ok := d.possibleDevices[chosen]; ok {
|
||||
d.counter++
|
||||
d.possibleDevices[chosen] = d.counter
|
||||
}
|
||||
}
|
81
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/device_allocator_test.go
generated
vendored
Normal file
81
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/device_allocator_test.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2016 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 aws
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDeviceAllocator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
existingDevices ExistingDevices
|
||||
deviceMap map[mountDevice]int
|
||||
expectedOutput mountDevice
|
||||
}{
|
||||
{
|
||||
"empty device list with wrap",
|
||||
ExistingDevices{},
|
||||
generateUnsortedDeviceList(),
|
||||
"bd", // next to 'zz' is the first one, 'ba'
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
allocator := NewDeviceAllocator().(*deviceAllocator)
|
||||
for k, v := range test.deviceMap {
|
||||
allocator.possibleDevices[k] = v
|
||||
}
|
||||
|
||||
got, err := allocator.GetNext(test.existingDevices)
|
||||
if err != nil {
|
||||
t.Errorf("text %q: unexpected error: %v", test.name, err)
|
||||
}
|
||||
if got != test.expectedOutput {
|
||||
t.Errorf("text %q: expected %q, got %q", test.name, test.expectedOutput, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateUnsortedDeviceList() map[mountDevice]int {
|
||||
possibleDevices := make(map[mountDevice]int)
|
||||
for _, firstChar := range []rune{'b', 'c'} {
|
||||
for i := 'a'; i <= 'z'; i++ {
|
||||
dev := mountDevice([]rune{firstChar, i})
|
||||
possibleDevices[dev] = 3
|
||||
}
|
||||
}
|
||||
possibleDevices["bd"] = 0
|
||||
return possibleDevices
|
||||
}
|
||||
|
||||
func TestDeviceAllocatorError(t *testing.T) {
|
||||
allocator := NewDeviceAllocator().(*deviceAllocator)
|
||||
existingDevices := ExistingDevices{}
|
||||
|
||||
// make all devices used
|
||||
var first, second byte
|
||||
for first = 'b'; first <= 'c'; first++ {
|
||||
for second = 'a'; second <= 'z'; second++ {
|
||||
device := [2]byte{first, second}
|
||||
existingDevices[mountDevice(device[:])] = "used"
|
||||
}
|
||||
}
|
||||
|
||||
device, err := allocator.GetNext(existingDevices)
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got device %q", device)
|
||||
}
|
||||
}
|
272
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/instances.go
generated
vendored
Normal file
272
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/instances.go
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
Copyright 2017 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 aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// awsInstanceRegMatch represents Regex Match for AWS instance.
|
||||
var awsInstanceRegMatch = regexp.MustCompile("^i-[^/]*$")
|
||||
|
||||
// awsInstanceID represents the ID of the instance in the AWS API, e.g. i-12345678
|
||||
// The "traditional" format is "i-12345678"
|
||||
// A new longer format is also being introduced: "i-12345678abcdef01"
|
||||
// We should not assume anything about the length or format, though it seems
|
||||
// reasonable to assume that instances will continue to start with "i-".
|
||||
type awsInstanceID string
|
||||
|
||||
func (i awsInstanceID) awsString() *string {
|
||||
return aws.String(string(i))
|
||||
}
|
||||
|
||||
// kubernetesInstanceID represents the id for an instance in the kubernetes API;
|
||||
// the following form
|
||||
// * aws:///<zone>/<awsInstanceId>
|
||||
// * aws:////<awsInstanceId>
|
||||
// * <awsInstanceId>
|
||||
type kubernetesInstanceID string
|
||||
|
||||
// mapToAWSInstanceID extracts the awsInstanceID from the kubernetesInstanceID
|
||||
func (name kubernetesInstanceID) mapToAWSInstanceID() (awsInstanceID, error) {
|
||||
s := string(name)
|
||||
|
||||
if !strings.HasPrefix(s, "aws://") {
|
||||
// Assume a bare aws volume id (vol-1234...)
|
||||
// Build a URL with an empty host (AZ)
|
||||
s = "aws://" + "/" + "/" + s
|
||||
}
|
||||
url, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Invalid instance name (%s): %v", name, err)
|
||||
}
|
||||
if url.Scheme != "aws" {
|
||||
return "", fmt.Errorf("Invalid scheme for AWS instance (%s)", name)
|
||||
}
|
||||
|
||||
awsID := ""
|
||||
tokens := strings.Split(strings.Trim(url.Path, "/"), "/")
|
||||
if len(tokens) == 1 {
|
||||
// instanceId
|
||||
awsID = tokens[0]
|
||||
} else if len(tokens) == 2 {
|
||||
// az/instanceId
|
||||
awsID = tokens[1]
|
||||
}
|
||||
|
||||
// We sanity check the resulting volume; the two known formats are
|
||||
// i-12345678 and i-12345678abcdef01
|
||||
if awsID == "" || !awsInstanceRegMatch.MatchString(awsID) {
|
||||
return "", fmt.Errorf("Invalid format for AWS instance (%s)", name)
|
||||
}
|
||||
|
||||
return awsInstanceID(awsID), nil
|
||||
}
|
||||
|
||||
// mapToAWSInstanceID extracts the awsInstanceIDs from the Nodes, returning an error if a Node cannot be mapped
|
||||
func mapToAWSInstanceIDs(nodes []*v1.Node) ([]awsInstanceID, error) {
|
||||
var instanceIDs []awsInstanceID
|
||||
for _, node := range nodes {
|
||||
if node.Spec.ProviderID == "" {
|
||||
return nil, fmt.Errorf("node %q did not have ProviderID set", node.Name)
|
||||
}
|
||||
instanceID, err := kubernetesInstanceID(node.Spec.ProviderID).mapToAWSInstanceID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse ProviderID %q for node %q", node.Spec.ProviderID, node.Name)
|
||||
}
|
||||
instanceIDs = append(instanceIDs, instanceID)
|
||||
}
|
||||
|
||||
return instanceIDs, nil
|
||||
}
|
||||
|
||||
// mapToAWSInstanceIDsTolerant extracts the awsInstanceIDs from the Nodes, skipping Nodes that cannot be mapped
|
||||
func mapToAWSInstanceIDsTolerant(nodes []*v1.Node) []awsInstanceID {
|
||||
var instanceIDs []awsInstanceID
|
||||
for _, node := range nodes {
|
||||
if node.Spec.ProviderID == "" {
|
||||
glog.Warningf("node %q did not have ProviderID set", node.Name)
|
||||
continue
|
||||
}
|
||||
instanceID, err := kubernetesInstanceID(node.Spec.ProviderID).mapToAWSInstanceID()
|
||||
if err != nil {
|
||||
glog.Warningf("unable to parse ProviderID %q for node %q", node.Spec.ProviderID, node.Name)
|
||||
continue
|
||||
}
|
||||
instanceIDs = append(instanceIDs, instanceID)
|
||||
}
|
||||
|
||||
return instanceIDs
|
||||
}
|
||||
|
||||
// Gets the full information about this instance from the EC2 API
|
||||
func describeInstance(ec2Client EC2, instanceID awsInstanceID) (*ec2.Instance, error) {
|
||||
request := &ec2.DescribeInstancesInput{
|
||||
InstanceIds: []*string{instanceID.awsString()},
|
||||
}
|
||||
|
||||
instances, err := ec2Client.DescribeInstances(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(instances) == 0 {
|
||||
return nil, fmt.Errorf("no instances found for instance: %s", instanceID)
|
||||
}
|
||||
if len(instances) > 1 {
|
||||
return nil, fmt.Errorf("multiple instances found for instance: %s", instanceID)
|
||||
}
|
||||
return instances[0], nil
|
||||
}
|
||||
|
||||
// instanceCache manages the cache of DescribeInstances
|
||||
type instanceCache struct {
|
||||
// TODO: Get rid of this field, send all calls through the instanceCache
|
||||
cloud *Cloud
|
||||
|
||||
mutex sync.Mutex
|
||||
snapshot *allInstancesSnapshot
|
||||
}
|
||||
|
||||
// Gets the full information about these instance from the EC2 API
|
||||
func (c *instanceCache) describeAllInstancesUncached() (*allInstancesSnapshot, error) {
|
||||
now := time.Now()
|
||||
|
||||
glog.V(4).Infof("EC2 DescribeInstances - fetching all instances")
|
||||
|
||||
filters := []*ec2.Filter{}
|
||||
instances, err := c.cloud.describeInstances(filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[awsInstanceID]*ec2.Instance)
|
||||
for _, i := range instances {
|
||||
id := awsInstanceID(aws.StringValue(i.InstanceId))
|
||||
m[id] = i
|
||||
}
|
||||
|
||||
snapshot := &allInstancesSnapshot{now, m}
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.snapshot != nil && snapshot.olderThan(c.snapshot) {
|
||||
// If this happens a lot, we could run this function in a mutex and only return one result
|
||||
glog.Infof("Not caching concurrent AWS DescribeInstances results")
|
||||
} else {
|
||||
c.snapshot = snapshot
|
||||
}
|
||||
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
// cacheCriteria holds criteria that must hold to use a cached snapshot
|
||||
type cacheCriteria struct {
|
||||
// MaxAge indicates the maximum age of a cached snapshot we can accept.
|
||||
// If set to 0 (i.e. unset), cached values will not time out because of age.
|
||||
MaxAge time.Duration
|
||||
|
||||
// HasInstances is a list of awsInstanceIDs that must be in a cached snapshot for it to be considered valid.
|
||||
// If an instance is not found in the cached snapshot, the snapshot be ignored and we will re-fetch.
|
||||
HasInstances []awsInstanceID
|
||||
}
|
||||
|
||||
// describeAllInstancesCached returns all instances, using cached results if applicable
|
||||
func (c *instanceCache) describeAllInstancesCached(criteria cacheCriteria) (*allInstancesSnapshot, error) {
|
||||
var err error
|
||||
snapshot := c.getSnapshot()
|
||||
if snapshot != nil && !snapshot.MeetsCriteria(criteria) {
|
||||
snapshot = nil
|
||||
}
|
||||
|
||||
if snapshot == nil {
|
||||
snapshot, err = c.describeAllInstancesUncached()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
glog.V(6).Infof("EC2 DescribeInstances - using cached results")
|
||||
}
|
||||
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
// getSnapshot returns a snapshot if one exists
|
||||
func (c *instanceCache) getSnapshot() *allInstancesSnapshot {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
return c.snapshot
|
||||
}
|
||||
|
||||
// olderThan is a simple helper to encapsulate timestamp comparison
|
||||
func (s *allInstancesSnapshot) olderThan(other *allInstancesSnapshot) bool {
|
||||
// After() is technically broken by time changes until we have monotonic time
|
||||
return other.timestamp.After(s.timestamp)
|
||||
}
|
||||
|
||||
// MeetsCriteria returns true if the snapshot meets the criteria in cacheCriteria
|
||||
func (s *allInstancesSnapshot) MeetsCriteria(criteria cacheCriteria) bool {
|
||||
if criteria.MaxAge > 0 {
|
||||
// Sub() is technically broken by time changes until we have monotonic time
|
||||
now := time.Now()
|
||||
if now.Sub(s.timestamp) > criteria.MaxAge {
|
||||
glog.V(6).Infof("instanceCache snapshot cannot be used as is older than MaxAge=%s", criteria.MaxAge)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(criteria.HasInstances) != 0 {
|
||||
for _, id := range criteria.HasInstances {
|
||||
if nil == s.instances[id] {
|
||||
glog.V(6).Infof("instanceCache snapshot cannot be used as does not contain instance %s", id)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// allInstancesSnapshot holds the results from querying for all instances,
|
||||
// along with the timestamp for cache-invalidation purposes
|
||||
type allInstancesSnapshot struct {
|
||||
timestamp time.Time
|
||||
instances map[awsInstanceID]*ec2.Instance
|
||||
}
|
||||
|
||||
// FindInstances returns the instances corresponding to the specified ids. If an id is not found, it is ignored.
|
||||
func (s *allInstancesSnapshot) FindInstances(ids []awsInstanceID) map[awsInstanceID]*ec2.Instance {
|
||||
m := make(map[awsInstanceID]*ec2.Instance)
|
||||
for _, id := range ids {
|
||||
instance := s.instances[id]
|
||||
if instance != nil {
|
||||
m[id] = instance
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
199
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/instances_test.go
generated
vendored
Normal file
199
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/instances_test.go
generated
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
Copyright 2017 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 aws
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParseInstance(t *testing.T) {
|
||||
tests := []struct {
|
||||
Kubernetes kubernetesInstanceID
|
||||
Aws awsInstanceID
|
||||
ExpectError bool
|
||||
}{
|
||||
{
|
||||
Kubernetes: "aws:///us-east-1a/i-12345678",
|
||||
Aws: "i-12345678",
|
||||
},
|
||||
{
|
||||
Kubernetes: "aws:////i-12345678",
|
||||
Aws: "i-12345678",
|
||||
},
|
||||
{
|
||||
Kubernetes: "i-12345678",
|
||||
Aws: "i-12345678",
|
||||
},
|
||||
{
|
||||
Kubernetes: "aws:///us-east-1a/i-12345678abcdef01",
|
||||
Aws: "i-12345678abcdef01",
|
||||
},
|
||||
{
|
||||
Kubernetes: "aws:////i-12345678abcdef01",
|
||||
Aws: "i-12345678abcdef01",
|
||||
},
|
||||
{
|
||||
Kubernetes: "i-12345678abcdef01",
|
||||
Aws: "i-12345678abcdef01",
|
||||
},
|
||||
{
|
||||
Kubernetes: "vol-123456789",
|
||||
ExpectError: true,
|
||||
},
|
||||
{
|
||||
Kubernetes: "aws:///us-east-1a/vol-12345678abcdef01",
|
||||
ExpectError: true,
|
||||
},
|
||||
{
|
||||
Kubernetes: "aws://accountid/us-east-1a/vol-12345678abcdef01",
|
||||
ExpectError: true,
|
||||
},
|
||||
{
|
||||
Kubernetes: "aws:///us-east-1a/vol-12345678abcdef01/suffix",
|
||||
ExpectError: true,
|
||||
},
|
||||
{
|
||||
Kubernetes: "",
|
||||
ExpectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
awsID, err := test.Kubernetes.mapToAWSInstanceID()
|
||||
if err != nil {
|
||||
if !test.ExpectError {
|
||||
t.Errorf("unexpected error parsing %s: %v", test.Kubernetes, err)
|
||||
}
|
||||
} else {
|
||||
if test.ExpectError {
|
||||
t.Errorf("expected error parsing %s", test.Kubernetes)
|
||||
} else if test.Aws != awsID {
|
||||
t.Errorf("unexpected value parsing %s, got %s", test.Kubernetes, awsID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
node := &v1.Node{}
|
||||
node.Spec.ProviderID = string(test.Kubernetes)
|
||||
|
||||
awsInstanceIds, err := mapToAWSInstanceIDs([]*v1.Node{node})
|
||||
if err != nil {
|
||||
if !test.ExpectError {
|
||||
t.Errorf("unexpected error parsing %s: %v", test.Kubernetes, err)
|
||||
}
|
||||
} else {
|
||||
if test.ExpectError {
|
||||
t.Errorf("expected error parsing %s", test.Kubernetes)
|
||||
} else if len(awsInstanceIds) != 1 {
|
||||
t.Errorf("unexpected value parsing %s, got %s", test.Kubernetes, awsInstanceIds)
|
||||
} else if awsInstanceIds[0] != test.Aws {
|
||||
t.Errorf("unexpected value parsing %s, got %s", test.Kubernetes, awsInstanceIds)
|
||||
}
|
||||
}
|
||||
|
||||
awsInstanceIds = mapToAWSInstanceIDsTolerant([]*v1.Node{node})
|
||||
if test.ExpectError {
|
||||
if len(awsInstanceIds) != 0 {
|
||||
t.Errorf("unexpected results parsing %s: %s", test.Kubernetes, awsInstanceIds)
|
||||
}
|
||||
} else {
|
||||
if len(awsInstanceIds) != 1 {
|
||||
t.Errorf("unexpected value parsing %s, got %s", test.Kubernetes, awsInstanceIds)
|
||||
} else if awsInstanceIds[0] != test.Aws {
|
||||
t.Errorf("unexpected value parsing %s, got %s", test.Kubernetes, awsInstanceIds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshotMeetsCriteria(t *testing.T) {
|
||||
snapshot := &allInstancesSnapshot{timestamp: time.Now().Add(-3601 * time.Second)}
|
||||
|
||||
if !snapshot.MeetsCriteria(cacheCriteria{}) {
|
||||
t.Errorf("Snapshot should always meet empty criteria")
|
||||
}
|
||||
|
||||
if snapshot.MeetsCriteria(cacheCriteria{MaxAge: time.Hour}) {
|
||||
t.Errorf("Snapshot did not honor MaxAge")
|
||||
}
|
||||
|
||||
if snapshot.MeetsCriteria(cacheCriteria{HasInstances: []awsInstanceID{awsInstanceID("i-12345678")}}) {
|
||||
t.Errorf("Snapshot did not honor HasInstances with missing instances")
|
||||
}
|
||||
|
||||
snapshot.instances = make(map[awsInstanceID]*ec2.Instance)
|
||||
snapshot.instances[awsInstanceID("i-12345678")] = &ec2.Instance{}
|
||||
|
||||
if !snapshot.MeetsCriteria(cacheCriteria{HasInstances: []awsInstanceID{awsInstanceID("i-12345678")}}) {
|
||||
t.Errorf("Snapshot did not honor HasInstances with matching instances")
|
||||
}
|
||||
|
||||
if snapshot.MeetsCriteria(cacheCriteria{HasInstances: []awsInstanceID{awsInstanceID("i-12345678"), awsInstanceID("i-00000000")}}) {
|
||||
t.Errorf("Snapshot did not honor HasInstances with partially matching instances")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOlderThan(t *testing.T) {
|
||||
t1 := time.Now()
|
||||
t2 := t1.Add(time.Second)
|
||||
|
||||
s1 := &allInstancesSnapshot{timestamp: t1}
|
||||
s2 := &allInstancesSnapshot{timestamp: t2}
|
||||
|
||||
assert.True(t, s1.olderThan(s2), "s1 should be olderThan s2")
|
||||
assert.False(t, s2.olderThan(s1), "s2 not should be olderThan s1")
|
||||
assert.False(t, s1.olderThan(s1), "s1 not should be olderThan itself")
|
||||
}
|
||||
|
||||
func TestSnapshotFindInstances(t *testing.T) {
|
||||
snapshot := &allInstancesSnapshot{}
|
||||
|
||||
snapshot.instances = make(map[awsInstanceID]*ec2.Instance)
|
||||
{
|
||||
id := awsInstanceID("i-12345678")
|
||||
snapshot.instances[id] = &ec2.Instance{InstanceId: id.awsString()}
|
||||
}
|
||||
{
|
||||
id := awsInstanceID("i-23456789")
|
||||
snapshot.instances[id] = &ec2.Instance{InstanceId: id.awsString()}
|
||||
}
|
||||
|
||||
instances := snapshot.FindInstances([]awsInstanceID{awsInstanceID("i-12345678"), awsInstanceID("i-23456789"), awsInstanceID("i-00000000")})
|
||||
if len(instances) != 2 {
|
||||
t.Errorf("findInstances returned %d results, expected 2", len(instances))
|
||||
}
|
||||
|
||||
for _, id := range []awsInstanceID{awsInstanceID("i-12345678"), awsInstanceID("i-23456789")} {
|
||||
i := instances[id]
|
||||
if i == nil {
|
||||
t.Errorf("findInstances did not return %s", id)
|
||||
continue
|
||||
}
|
||||
if aws.StringValue(i.InstanceId) != string(id) {
|
||||
t.Errorf("findInstances did not return expected instanceId for %s", id)
|
||||
}
|
||||
if i != snapshot.instances[id] {
|
||||
t.Errorf("findInstances did not return expected instance (reference equality) for %s", id)
|
||||
}
|
||||
}
|
||||
}
|
48
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/log_handler.go
generated
vendored
Normal file
48
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/log_handler.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2015 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 aws
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Handler for aws-sdk-go that logs all requests
|
||||
func awsHandlerLogger(req *request.Request) {
|
||||
service, name := awsServiceAndName(req)
|
||||
glog.V(4).Infof("AWS request: %s %s", service, name)
|
||||
}
|
||||
|
||||
func awsSendHandlerLogger(req *request.Request) {
|
||||
service, name := awsServiceAndName(req)
|
||||
glog.V(4).Infof("AWS API Send: %s %s %v %v", service, name, req.Operation, req.Params)
|
||||
}
|
||||
|
||||
func awsValidateResponseHandlerLogger(req *request.Request) {
|
||||
service, name := awsServiceAndName(req)
|
||||
glog.V(4).Infof("AWS API ValidateResponse: %s %s %v %v %s", service, name, req.Operation, req.Params, req.HTTPResponse.Status)
|
||||
}
|
||||
|
||||
func awsServiceAndName(req *request.Request) (string, string) {
|
||||
service := req.ClientInfo.ServiceName
|
||||
|
||||
name := "?"
|
||||
if req.Operation != nil {
|
||||
name = req.Operation.Name
|
||||
}
|
||||
return service, name
|
||||
}
|
94
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/regions.go
generated
vendored
Normal file
94
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/regions.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright 2016 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 aws
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
awscredentialprovider "k8s.io/kubernetes/pkg/credentialprovider/aws"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// WellKnownRegions is the complete list of regions known to the AWS cloudprovider
|
||||
// and credentialprovider.
|
||||
var WellKnownRegions = [...]string{
|
||||
// from `aws ec2 describe-regions --region us-east-1 --query Regions[].RegionName | sort`
|
||||
"ap-northeast-1",
|
||||
"ap-northeast-2",
|
||||
"ap-south-1",
|
||||
"ap-southeast-1",
|
||||
"ap-southeast-2",
|
||||
"ca-central-1",
|
||||
"eu-central-1",
|
||||
"eu-west-1",
|
||||
"eu-west-2",
|
||||
"sa-east-1",
|
||||
"us-east-1",
|
||||
"us-east-2",
|
||||
"us-west-1",
|
||||
"us-west-2",
|
||||
|
||||
// these are not registered in many / most accounts
|
||||
"cn-north-1",
|
||||
"us-gov-west-1",
|
||||
}
|
||||
|
||||
// awsRegionsMutex protects awsRegions
|
||||
var awsRegionsMutex sync.Mutex
|
||||
|
||||
// awsRegions is a set of recognized regions
|
||||
var awsRegions sets.String
|
||||
|
||||
// RecognizeRegion is called for each AWS region we know about.
|
||||
// It currently registers a credential provider for that region.
|
||||
// There are two paths to discovering a region:
|
||||
// * we hard-code some well-known regions
|
||||
// * if a region is discovered from instance metadata, we add that
|
||||
func RecognizeRegion(region string) {
|
||||
awsRegionsMutex.Lock()
|
||||
defer awsRegionsMutex.Unlock()
|
||||
|
||||
if awsRegions == nil {
|
||||
awsRegions = sets.NewString()
|
||||
}
|
||||
|
||||
if awsRegions.Has(region) {
|
||||
glog.V(6).Infof("found AWS region %q again - ignoring", region)
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(4).Infof("found AWS region %q", region)
|
||||
|
||||
awscredentialprovider.RegisterCredentialsProvider(region)
|
||||
|
||||
awsRegions.Insert(region)
|
||||
}
|
||||
|
||||
// RecognizeWellKnownRegions calls RecognizeRegion on each WellKnownRegion
|
||||
func RecognizeWellKnownRegions() {
|
||||
for _, region := range WellKnownRegions {
|
||||
RecognizeRegion(region)
|
||||
}
|
||||
}
|
||||
|
||||
// isRegionValid checks if the region is in the set of known regions
|
||||
func isRegionValid(region string) bool {
|
||||
awsRegionsMutex.Lock()
|
||||
defer awsRegionsMutex.Unlock()
|
||||
|
||||
return awsRegions.Has(region)
|
||||
}
|
85
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/regions_test.go
generated
vendored
Normal file
85
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/regions_test.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2016 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 aws
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestRegions does basic checking of region verification / addition
|
||||
func TestRegions(t *testing.T) {
|
||||
RecognizeWellKnownRegions()
|
||||
|
||||
tests := []struct {
|
||||
Add string
|
||||
Lookup string
|
||||
ExpectIsRegion bool
|
||||
}{
|
||||
{
|
||||
Lookup: "us-east-1",
|
||||
ExpectIsRegion: true,
|
||||
},
|
||||
{
|
||||
Lookup: "us-east-1a",
|
||||
ExpectIsRegion: false,
|
||||
},
|
||||
{
|
||||
Add: "us-test-1",
|
||||
Lookup: "us-east-1",
|
||||
ExpectIsRegion: true,
|
||||
},
|
||||
{
|
||||
Lookup: "us-test-1",
|
||||
ExpectIsRegion: true,
|
||||
},
|
||||
{
|
||||
Add: "us-test-1",
|
||||
Lookup: "us-test-1",
|
||||
ExpectIsRegion: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.Add != "" {
|
||||
RecognizeRegion(test.Add)
|
||||
}
|
||||
|
||||
if test.Lookup != "" {
|
||||
if isRegionValid(test.Lookup) != test.ExpectIsRegion {
|
||||
t.Fatalf("region valid mismatch: %q", test.Lookup)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestRecognizesNewRegion verifies that we see a region from metadata, we recognize it as valid
|
||||
func TestRecognizesNewRegion(t *testing.T) {
|
||||
region := "us-testrecognizesnewregion-1"
|
||||
if isRegionValid(region) {
|
||||
t.Fatalf("region already valid: %q", region)
|
||||
}
|
||||
|
||||
awsServices := NewFakeAWSServices(TestClusterId).WithAz(region + "a")
|
||||
_, err := newAWSCloud(CloudConfig{}, awsServices)
|
||||
if err != nil {
|
||||
t.Errorf("error building AWS cloud: %v", err)
|
||||
}
|
||||
|
||||
if !isRegionValid(region) {
|
||||
t.Fatalf("newly discovered region not valid: %q", region)
|
||||
}
|
||||
}
|
174
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/retry_handler.go
generated
vendored
Normal file
174
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/retry_handler.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright 2015 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 aws
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
decayIntervalSeconds = 20
|
||||
decayFraction = 0.8
|
||||
maxDelay = 60 * time.Second
|
||||
)
|
||||
|
||||
// CrossRequestRetryDelay inserts delays before AWS calls, when we are observing RequestLimitExceeded errors
|
||||
// Note that we share a CrossRequestRetryDelay across multiple AWS requests; this is a process-wide back-off,
|
||||
// whereas the aws-sdk-go implements a per-request exponential backoff/retry
|
||||
type CrossRequestRetryDelay struct {
|
||||
backoff Backoff
|
||||
}
|
||||
|
||||
// Create a new CrossRequestRetryDelay
|
||||
func NewCrossRequestRetryDelay() *CrossRequestRetryDelay {
|
||||
c := &CrossRequestRetryDelay{}
|
||||
c.backoff.init(decayIntervalSeconds, decayFraction, maxDelay)
|
||||
return c
|
||||
}
|
||||
|
||||
// Added to the Sign chain; called before each request
|
||||
func (c *CrossRequestRetryDelay) BeforeSign(r *request.Request) {
|
||||
now := time.Now()
|
||||
delay := c.backoff.ComputeDelayForRequest(now)
|
||||
if delay > 0 {
|
||||
glog.Warningf("Inserting delay before AWS request (%s) to avoid RequestLimitExceeded: %s",
|
||||
describeRequest(r), delay.String())
|
||||
|
||||
if sleepFn := r.Config.SleepDelay; sleepFn != nil {
|
||||
// Support SleepDelay for backwards compatibility
|
||||
sleepFn(delay)
|
||||
} else if err := aws.SleepWithContext(r.Context(), delay); err != nil {
|
||||
r.Error = awserr.New(request.CanceledErrorCode, "request context canceled", err)
|
||||
r.Retryable = aws.Bool(false)
|
||||
return
|
||||
}
|
||||
|
||||
// Avoid clock skew problems
|
||||
r.Time = now
|
||||
}
|
||||
}
|
||||
|
||||
// Return the operation name, for use in log messages and metrics
|
||||
func operationName(r *request.Request) string {
|
||||
name := "?"
|
||||
if r.Operation != nil {
|
||||
name = r.Operation.Name
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Return a user-friendly string describing the request, for use in log messages
|
||||
func describeRequest(r *request.Request) string {
|
||||
service := r.ClientInfo.ServiceName
|
||||
return service + "::" + operationName(r)
|
||||
}
|
||||
|
||||
// Added to the AfterRetry chain; called after any error
|
||||
func (c *CrossRequestRetryDelay) AfterRetry(r *request.Request) {
|
||||
if r.Error == nil {
|
||||
return
|
||||
}
|
||||
awsError, ok := r.Error.(awserr.Error)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if awsError.Code() == "RequestLimitExceeded" {
|
||||
c.backoff.ReportError()
|
||||
recordAWSThrottlesMetric(operationName(r))
|
||||
glog.Warningf("Got RequestLimitExceeded error on AWS request (%s)",
|
||||
describeRequest(r))
|
||||
}
|
||||
}
|
||||
|
||||
// Backoff manages a backoff that varies based on the recently observed failures
|
||||
type Backoff struct {
|
||||
decayIntervalSeconds int64
|
||||
decayFraction float64
|
||||
maxDelay time.Duration
|
||||
|
||||
mutex sync.Mutex
|
||||
|
||||
// We count all requests & the number of requests which hit a
|
||||
// RequestLimit. We only really care about 'recent' requests, so we
|
||||
// decay the counts exponentially to bias towards recent values.
|
||||
countErrorsRequestLimit float32
|
||||
countRequests float32
|
||||
lastDecay int64
|
||||
}
|
||||
|
||||
func (b *Backoff) init(decayIntervalSeconds int, decayFraction float64, maxDelay time.Duration) {
|
||||
b.lastDecay = time.Now().Unix()
|
||||
// Bias so that if the first request hits the limit we don't immediately apply the full delay
|
||||
b.countRequests = 4
|
||||
b.decayIntervalSeconds = int64(decayIntervalSeconds)
|
||||
b.decayFraction = decayFraction
|
||||
b.maxDelay = maxDelay
|
||||
}
|
||||
|
||||
// Computes the delay required for a request, also updating internal state to count this request
|
||||
func (b *Backoff) ComputeDelayForRequest(now time.Time) time.Duration {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
// Apply exponential decay to the counters
|
||||
timeDeltaSeconds := now.Unix() - b.lastDecay
|
||||
if timeDeltaSeconds > b.decayIntervalSeconds {
|
||||
intervals := float64(timeDeltaSeconds) / float64(b.decayIntervalSeconds)
|
||||
decay := float32(math.Pow(b.decayFraction, intervals))
|
||||
b.countErrorsRequestLimit *= decay
|
||||
b.countRequests *= decay
|
||||
b.lastDecay = now.Unix()
|
||||
}
|
||||
|
||||
// Count this request
|
||||
b.countRequests += 1.0
|
||||
|
||||
// Compute the failure rate
|
||||
errorFraction := float32(0.0)
|
||||
if b.countRequests > 0.5 {
|
||||
// Avoid tiny residuals & rounding errors
|
||||
errorFraction = b.countErrorsRequestLimit / b.countRequests
|
||||
}
|
||||
|
||||
// Ignore a low fraction of errors
|
||||
// This also allows them to time-out
|
||||
if errorFraction < 0.1 {
|
||||
return time.Duration(0)
|
||||
}
|
||||
|
||||
// Delay by the max delay multiplied by the recent error rate
|
||||
// (i.e. we apply a linear delay function)
|
||||
// TODO: This is pretty arbitrary
|
||||
delay := time.Nanosecond * time.Duration(float32(b.maxDelay.Nanoseconds())*errorFraction)
|
||||
// Round down to the nearest second for sanity
|
||||
return time.Second * time.Duration(int(delay.Seconds()))
|
||||
}
|
||||
|
||||
// Called when we observe a throttling error
|
||||
func (b *Backoff) ReportError() {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
b.countErrorsRequestLimit += 1.0
|
||||
}
|
135
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/retry_handler_test.go
generated
vendored
Normal file
135
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/retry_handler_test.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
Copyright 2016 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 aws
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// There follows a group of tests for the backoff logic. There's nothing
|
||||
// particularly special about the values chosen: if we tweak the values in the
|
||||
// backoff logic then we might well have to update the tests. However the key
|
||||
// behavioural elements should remain (e.g. no errors => no backoff), and these
|
||||
// are each tested by one of the tests below.
|
||||
|
||||
// Test that we don't apply any delays when there are no errors
|
||||
func TestBackoffNoErrors(t *testing.T) {
|
||||
b := &Backoff{}
|
||||
b.init(decayIntervalSeconds, decayFraction, maxDelay)
|
||||
|
||||
now := time.Now()
|
||||
for i := 0; i < 100; i++ {
|
||||
d := b.ComputeDelayForRequest(now)
|
||||
if d.Nanoseconds() != 0 {
|
||||
t.Fatalf("unexpected delay during no-error case")
|
||||
}
|
||||
now = now.Add(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we always apply a delay when there are errors, and also that we
|
||||
// don't "flap" - that our own delay doesn't cause us to oscillate between
|
||||
// delay and no-delay.
|
||||
func TestBackoffAllErrors(t *testing.T) {
|
||||
b := &Backoff{}
|
||||
b.init(decayIntervalSeconds, decayFraction, maxDelay)
|
||||
|
||||
now := time.Now()
|
||||
// Warm up
|
||||
for i := 0; i < 10; i++ {
|
||||
_ = b.ComputeDelayForRequest(now)
|
||||
b.ReportError()
|
||||
now = now.Add(time.Second)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
d := b.ComputeDelayForRequest(now)
|
||||
b.ReportError()
|
||||
if d.Seconds() < 5 {
|
||||
t.Fatalf("unexpected short-delay during all-error case: %v", d)
|
||||
}
|
||||
t.Logf("delay @%d %v", i, d)
|
||||
now = now.Add(d)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we do come close to our max delay, when we see all errors at 1
|
||||
// second intervals (this simulates multiple concurrent requests, because we
|
||||
// don't wait for delay in between requests)
|
||||
func TestBackoffHitsMax(t *testing.T) {
|
||||
b := &Backoff{}
|
||||
b.init(decayIntervalSeconds, decayFraction, maxDelay)
|
||||
|
||||
now := time.Now()
|
||||
for i := 0; i < 100; i++ {
|
||||
_ = b.ComputeDelayForRequest(now)
|
||||
b.ReportError()
|
||||
now = now.Add(time.Second)
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
d := b.ComputeDelayForRequest(now)
|
||||
b.ReportError()
|
||||
if float32(d.Nanoseconds()) < (float32(maxDelay.Nanoseconds()) * 0.95) {
|
||||
t.Fatalf("expected delay to be >= 95 percent of max delay, was %v", d)
|
||||
}
|
||||
t.Logf("delay @%d %v", i, d)
|
||||
now = now.Add(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that after a phase of errors, we eventually stop applying a delay once there are
|
||||
// no more errors.
|
||||
func TestBackoffRecovers(t *testing.T) {
|
||||
b := &Backoff{}
|
||||
b.init(decayIntervalSeconds, decayFraction, maxDelay)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// Phase of all-errors
|
||||
for i := 0; i < 100; i++ {
|
||||
_ = b.ComputeDelayForRequest(now)
|
||||
b.ReportError()
|
||||
now = now.Add(time.Second)
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
d := b.ComputeDelayForRequest(now)
|
||||
b.ReportError()
|
||||
if d.Seconds() < 5 {
|
||||
t.Fatalf("unexpected short-delay during all-error phase: %v", d)
|
||||
}
|
||||
t.Logf("error phase delay @%d %v", i, d)
|
||||
now = now.Add(time.Second)
|
||||
}
|
||||
|
||||
// Phase of no errors
|
||||
for i := 0; i < 100; i++ {
|
||||
_ = b.ComputeDelayForRequest(now)
|
||||
now = now.Add(3 * time.Second)
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
d := b.ComputeDelayForRequest(now)
|
||||
if d.Seconds() != 0 {
|
||||
t.Fatalf("unexpected delay during error recovery phase: %v", d)
|
||||
}
|
||||
t.Logf("no-error phase delay @%d %v", i, d)
|
||||
now = now.Add(time.Second)
|
||||
}
|
||||
}
|
146
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/sets_ippermissions.go
generated
vendored
Normal file
146
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/sets_ippermissions.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
Copyright 2016 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 aws
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
)
|
||||
|
||||
type IPPermissionSet map[string]*ec2.IpPermission
|
||||
|
||||
func NewIPPermissionSet(items ...*ec2.IpPermission) IPPermissionSet {
|
||||
s := make(IPPermissionSet)
|
||||
s.Insert(items...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Ungroup splits permissions out into individual permissions
|
||||
// EC2 will combine permissions with the same port but different SourceRanges together, for example
|
||||
// We ungroup them so we can process them
|
||||
func (s IPPermissionSet) Ungroup() IPPermissionSet {
|
||||
l := []*ec2.IpPermission{}
|
||||
for _, p := range s.List() {
|
||||
if len(p.IpRanges) <= 1 {
|
||||
l = append(l, p)
|
||||
continue
|
||||
}
|
||||
for _, ipRange := range p.IpRanges {
|
||||
c := &ec2.IpPermission{}
|
||||
*c = *p
|
||||
c.IpRanges = []*ec2.IpRange{ipRange}
|
||||
l = append(l, c)
|
||||
}
|
||||
}
|
||||
|
||||
l2 := []*ec2.IpPermission{}
|
||||
for _, p := range l {
|
||||
if len(p.UserIdGroupPairs) <= 1 {
|
||||
l2 = append(l2, p)
|
||||
continue
|
||||
}
|
||||
for _, u := range p.UserIdGroupPairs {
|
||||
c := &ec2.IpPermission{}
|
||||
*c = *p
|
||||
c.UserIdGroupPairs = []*ec2.UserIdGroupPair{u}
|
||||
l2 = append(l, c)
|
||||
}
|
||||
}
|
||||
|
||||
l3 := []*ec2.IpPermission{}
|
||||
for _, p := range l2 {
|
||||
if len(p.PrefixListIds) <= 1 {
|
||||
l3 = append(l3, p)
|
||||
continue
|
||||
}
|
||||
for _, v := range p.PrefixListIds {
|
||||
c := &ec2.IpPermission{}
|
||||
*c = *p
|
||||
c.PrefixListIds = []*ec2.PrefixListId{v}
|
||||
l3 = append(l3, c)
|
||||
}
|
||||
}
|
||||
|
||||
return NewIPPermissionSet(l3...)
|
||||
}
|
||||
|
||||
// Insert adds items to the set.
|
||||
func (s IPPermissionSet) Insert(items ...*ec2.IpPermission) {
|
||||
for _, p := range items {
|
||||
k := keyForIPPermission(p)
|
||||
s[k] = p
|
||||
}
|
||||
}
|
||||
|
||||
// List returns the contents as a slice. Order is not defined.
|
||||
func (s IPPermissionSet) List() []*ec2.IpPermission {
|
||||
res := make([]*ec2.IpPermission, 0, len(s))
|
||||
for _, v := range s {
|
||||
res = append(res, v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// IsSuperset returns true if and only if s1 is a superset of s2.
|
||||
func (s1 IPPermissionSet) IsSuperset(s2 IPPermissionSet) bool {
|
||||
for k := range s2 {
|
||||
_, found := s1[k]
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal returns true if and only if s1 is equal (as a set) to s2.
|
||||
// Two sets are equal if their membership is identical.
|
||||
// (In practice, this means same elements, order doesn't matter)
|
||||
func (s1 IPPermissionSet) Equal(s2 IPPermissionSet) bool {
|
||||
return len(s1) == len(s2) && s1.IsSuperset(s2)
|
||||
}
|
||||
|
||||
// Difference returns a set of objects that are not in s2
|
||||
// For example:
|
||||
// s1 = {a1, a2, a3}
|
||||
// s2 = {a1, a2, a4, a5}
|
||||
// s1.Difference(s2) = {a3}
|
||||
// s2.Difference(s1) = {a4, a5}
|
||||
func (s IPPermissionSet) Difference(s2 IPPermissionSet) IPPermissionSet {
|
||||
result := NewIPPermissionSet()
|
||||
for k, v := range s {
|
||||
_, found := s2[k]
|
||||
if !found {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Len returns the size of the set.
|
||||
func (s IPPermissionSet) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func keyForIPPermission(p *ec2.IpPermission) string {
|
||||
v, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error building JSON representation of ec2.IpPermission: %v", err))
|
||||
}
|
||||
return string(v)
|
||||
}
|
282
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/tags.go
generated
vendored
Normal file
282
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/tags.go
generated
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
Copyright 2017 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 aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
// TagNameKubernetesClusterPrefix is the tag name we use to differentiate multiple
|
||||
// logically independent clusters running in the same AZ.
|
||||
// The tag key = TagNameKubernetesClusterPrefix + clusterID
|
||||
// The tag value is an ownership value
|
||||
const TagNameKubernetesClusterPrefix = "kubernetes.io/cluster/"
|
||||
|
||||
// TagNameKubernetesClusterLegacy is the legacy tag name we use to differentiate multiple
|
||||
// logically independent clusters running in the same AZ. The problem with it was that it
|
||||
// did not allow shared resources.
|
||||
const TagNameKubernetesClusterLegacy = "KubernetesCluster"
|
||||
|
||||
type ResourceLifecycle string
|
||||
|
||||
const (
|
||||
// ResourceLifecycleOwned is the value we use when tagging resources to indicate
|
||||
// that the resource is considered owned and managed by the cluster,
|
||||
// and in particular that the lifecycle is tied to the lifecycle of the cluster.
|
||||
ResourceLifecycleOwned = "owned"
|
||||
// ResourceLifecycleShared is the value we use when tagging resources to indicate
|
||||
// that the resource is shared between multiple clusters, and should not be destroyed
|
||||
// if the cluster is destroyed.
|
||||
ResourceLifecycleShared = "shared"
|
||||
)
|
||||
|
||||
type awsTagging struct {
|
||||
// ClusterID is our cluster identifier: we tag AWS resources with this value,
|
||||
// and thus we can run two independent clusters in the same VPC or subnets.
|
||||
// This gives us similar functionality to GCE projects.
|
||||
ClusterID string
|
||||
|
||||
// usesLegacyTags is true if we are using the legacy TagNameKubernetesClusterLegacy tags
|
||||
usesLegacyTags bool
|
||||
}
|
||||
|
||||
func (t *awsTagging) init(legacyClusterID string, clusterID string) error {
|
||||
if legacyClusterID != "" {
|
||||
if clusterID != "" && legacyClusterID != clusterID {
|
||||
return fmt.Errorf("ClusterID tags did not match: %q vs %q", clusterID, legacyClusterID)
|
||||
}
|
||||
t.usesLegacyTags = true
|
||||
clusterID = legacyClusterID
|
||||
}
|
||||
|
||||
t.ClusterID = clusterID
|
||||
|
||||
if clusterID != "" {
|
||||
glog.Infof("AWS cloud filtering on ClusterID: %v", clusterID)
|
||||
} else {
|
||||
return fmt.Errorf("AWS cloud failed to find ClusterID")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extracts a clusterID from the given tags, if one is present
|
||||
// If no clusterID is found, returns "", nil
|
||||
// If multiple (different) clusterIDs are found, returns an error
|
||||
func (t *awsTagging) initFromTags(tags []*ec2.Tag) error {
|
||||
legacyClusterID, newClusterID, err := findClusterIDs(tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if legacyClusterID == "" && newClusterID == "" {
|
||||
glog.Errorf("Tag %q nor %q not found; Kubernetes may behave unexpectedly.", TagNameKubernetesClusterLegacy, TagNameKubernetesClusterPrefix+"...")
|
||||
}
|
||||
|
||||
return t.init(legacyClusterID, newClusterID)
|
||||
}
|
||||
|
||||
// Extracts the legacy & new cluster ids from the given tags, if they are present
|
||||
// If duplicate tags are found, returns an error
|
||||
func findClusterIDs(tags []*ec2.Tag) (string, string, error) {
|
||||
legacyClusterID := ""
|
||||
newClusterID := ""
|
||||
|
||||
for _, tag := range tags {
|
||||
tagKey := aws.StringValue(tag.Key)
|
||||
if strings.HasPrefix(tagKey, TagNameKubernetesClusterPrefix) {
|
||||
id := strings.TrimPrefix(tagKey, TagNameKubernetesClusterPrefix)
|
||||
if newClusterID != "" {
|
||||
return "", "", fmt.Errorf("Found multiple cluster tags with prefix %s (%q and %q)", TagNameKubernetesClusterPrefix, newClusterID, id)
|
||||
}
|
||||
newClusterID = id
|
||||
}
|
||||
|
||||
if tagKey == TagNameKubernetesClusterLegacy {
|
||||
id := aws.StringValue(tag.Value)
|
||||
if legacyClusterID != "" {
|
||||
return "", "", fmt.Errorf("Found multiple %s tags (%q and %q)", TagNameKubernetesClusterLegacy, legacyClusterID, id)
|
||||
}
|
||||
legacyClusterID = id
|
||||
}
|
||||
}
|
||||
|
||||
return legacyClusterID, newClusterID, nil
|
||||
}
|
||||
|
||||
func (t *awsTagging) clusterTagKey() string {
|
||||
return TagNameKubernetesClusterPrefix + t.ClusterID
|
||||
}
|
||||
|
||||
func (t *awsTagging) hasClusterTag(tags []*ec2.Tag) bool {
|
||||
// if the clusterID is not configured -- we consider all instances.
|
||||
if len(t.ClusterID) == 0 {
|
||||
return true
|
||||
}
|
||||
clusterTagKey := t.clusterTagKey()
|
||||
for _, tag := range tags {
|
||||
tagKey := aws.StringValue(tag.Key)
|
||||
// For 1.6, we continue to recognize the legacy tags, for the 1.5 -> 1.6 upgrade
|
||||
// Note that we want to continue traversing tag list if we see a legacy tag with value != ClusterID
|
||||
if (tagKey == TagNameKubernetesClusterLegacy) && (aws.StringValue(tag.Value) == t.ClusterID) {
|
||||
return true
|
||||
}
|
||||
if tagKey == clusterTagKey {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure that a resource has the correct tags
|
||||
// If it has no tags, we assume that this was a problem caused by an error in between creation and tagging,
|
||||
// and we add the tags. If it has a different cluster's tags, that is an error.
|
||||
func (c *awsTagging) readRepairClusterTags(client EC2, resourceID string, lifecycle ResourceLifecycle, additionalTags map[string]string, observedTags []*ec2.Tag) error {
|
||||
actualTagMap := make(map[string]string)
|
||||
for _, tag := range observedTags {
|
||||
actualTagMap[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value)
|
||||
}
|
||||
|
||||
expectedTags := c.buildTags(lifecycle, additionalTags)
|
||||
|
||||
addTags := make(map[string]string)
|
||||
for k, expected := range expectedTags {
|
||||
actual := actualTagMap[k]
|
||||
if actual == expected {
|
||||
continue
|
||||
}
|
||||
if actual == "" {
|
||||
glog.Warningf("Resource %q was missing expected cluster tag %q. Will add (with value %q)", resourceID, k, expected)
|
||||
addTags[k] = expected
|
||||
} else {
|
||||
return fmt.Errorf("resource %q has tag belonging to another cluster: %q=%q (expected %q)", resourceID, k, actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
if len(addTags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.createTags(client, resourceID, lifecycle, addTags); err != nil {
|
||||
return fmt.Errorf("error adding missing tags to resource %q: %q", resourceID, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createTags calls EC2 CreateTags, but adds retry-on-failure logic
|
||||
// We retry mainly because if we create an object, we cannot tag it until it is "fully created" (eventual consistency)
|
||||
// The error code varies though (depending on what we are tagging), so we simply retry on all errors
|
||||
func (t *awsTagging) createTags(client EC2, resourceID string, lifecycle ResourceLifecycle, additionalTags map[string]string) error {
|
||||
tags := t.buildTags(lifecycle, additionalTags)
|
||||
|
||||
if tags == nil || len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var awsTags []*ec2.Tag
|
||||
for k, v := range tags {
|
||||
tag := &ec2.Tag{
|
||||
Key: aws.String(k),
|
||||
Value: aws.String(v),
|
||||
}
|
||||
awsTags = append(awsTags, tag)
|
||||
}
|
||||
|
||||
backoff := wait.Backoff{
|
||||
Duration: createTagInitialDelay,
|
||||
Factor: createTagFactor,
|
||||
Steps: createTagSteps,
|
||||
}
|
||||
request := &ec2.CreateTagsInput{}
|
||||
request.Resources = []*string{&resourceID}
|
||||
request.Tags = awsTags
|
||||
|
||||
var lastErr error
|
||||
err := wait.ExponentialBackoff(backoff, func() (bool, error) {
|
||||
_, err := client.CreateTags(request)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// We could check that the error is retryable, but the error code changes based on what we are tagging
|
||||
// SecurityGroup: InvalidGroup.NotFound
|
||||
glog.V(2).Infof("Failed to create tags; will retry. Error was %q", err)
|
||||
lastErr = err
|
||||
return false, nil
|
||||
})
|
||||
if err == wait.ErrWaitTimeout {
|
||||
// return real CreateTags error instead of timeout
|
||||
err = lastErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Add additional filters, to match on our tags
|
||||
// This lets us run multiple k8s clusters in a single EC2 AZ
|
||||
func (t *awsTagging) addFilters(filters []*ec2.Filter) []*ec2.Filter {
|
||||
// if there are no clusterID configured - no filtering by special tag names
|
||||
// should be applied to revert to legacy behaviour.
|
||||
if len(t.ClusterID) == 0 {
|
||||
if len(filters) == 0 {
|
||||
// We can't pass a zero-length Filters to AWS (it's an error)
|
||||
// So if we end up with no filters; just return nil
|
||||
return nil
|
||||
}
|
||||
return filters
|
||||
}
|
||||
// For 1.6, we always recognize the legacy tag, for the 1.5 -> 1.6 upgrade
|
||||
// There are no "or" filters by key, so we look for both the legacy and new key, and then we have to post-filter
|
||||
f := newEc2Filter("tag-key", TagNameKubernetesClusterLegacy, t.clusterTagKey())
|
||||
|
||||
// We can't pass a zero-length Filters to AWS (it's an error)
|
||||
// So if we end up with no filters; we need to return nil
|
||||
filters = append(filters, f)
|
||||
return filters
|
||||
}
|
||||
|
||||
func (t *awsTagging) buildTags(lifecycle ResourceLifecycle, additionalTags map[string]string) map[string]string {
|
||||
tags := make(map[string]string)
|
||||
for k, v := range additionalTags {
|
||||
tags[k] = v
|
||||
}
|
||||
|
||||
// no clusterID is a sign of misconfigured cluster, but we can't be tagging the resources with empty
|
||||
// strings
|
||||
if len(t.ClusterID) == 0 {
|
||||
return tags
|
||||
}
|
||||
|
||||
// We only create legacy tags if we are using legacy tags, i.e. if we have seen a legacy tag on our instance
|
||||
if t.usesLegacyTags {
|
||||
tags[TagNameKubernetesClusterLegacy] = t.ClusterID
|
||||
}
|
||||
tags[t.clusterTagKey()] = string(lifecycle)
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
func (t *awsTagging) clusterID() string {
|
||||
return t.ClusterID
|
||||
}
|
110
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/tags_test.go
generated
vendored
Normal file
110
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/tags_test.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2014 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 aws
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFilterTags(t *testing.T) {
|
||||
awsServices := NewFakeAWSServices(TestClusterId)
|
||||
c, err := newAWSCloud(CloudConfig{}, awsServices)
|
||||
if err != nil {
|
||||
t.Errorf("Error building aws cloud: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if c.tagging.ClusterID != TestClusterId {
|
||||
t.Errorf("unexpected ClusterID: %v", c.tagging.ClusterID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindClusterID(t *testing.T) {
|
||||
grid := []struct {
|
||||
Tags map[string]string
|
||||
ExpectedNew string
|
||||
ExpectedLegacy string
|
||||
ExpectError bool
|
||||
}{
|
||||
{
|
||||
Tags: map[string]string{},
|
||||
},
|
||||
{
|
||||
Tags: map[string]string{
|
||||
TagNameKubernetesClusterLegacy: "a",
|
||||
},
|
||||
ExpectedLegacy: "a",
|
||||
},
|
||||
{
|
||||
Tags: map[string]string{
|
||||
TagNameKubernetesClusterPrefix + "a": "owned",
|
||||
},
|
||||
ExpectedNew: "a",
|
||||
},
|
||||
{
|
||||
Tags: map[string]string{
|
||||
TagNameKubernetesClusterPrefix + "a": "",
|
||||
},
|
||||
ExpectedNew: "a",
|
||||
},
|
||||
{
|
||||
Tags: map[string]string{
|
||||
TagNameKubernetesClusterLegacy: "a",
|
||||
TagNameKubernetesClusterPrefix + "a": "",
|
||||
},
|
||||
ExpectedLegacy: "a",
|
||||
ExpectedNew: "a",
|
||||
},
|
||||
{
|
||||
Tags: map[string]string{
|
||||
TagNameKubernetesClusterPrefix + "a": "",
|
||||
TagNameKubernetesClusterPrefix + "b": "",
|
||||
},
|
||||
ExpectError: true,
|
||||
},
|
||||
}
|
||||
for _, g := range grid {
|
||||
var ec2Tags []*ec2.Tag
|
||||
for k, v := range g.Tags {
|
||||
ec2Tags = append(ec2Tags, &ec2.Tag{Key: aws.String(k), Value: aws.String(v)})
|
||||
}
|
||||
actualLegacy, actualNew, err := findClusterIDs(ec2Tags)
|
||||
if g.ExpectError {
|
||||
if err == nil {
|
||||
t.Errorf("expected error for tags %v", g.Tags)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error for tags %v: %v", g.Tags, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if g.ExpectedNew != actualNew {
|
||||
t.Errorf("unexpected new clusterid for tags %v: %s vs %s", g.Tags, g.ExpectedNew, actualNew)
|
||||
continue
|
||||
}
|
||||
|
||||
if g.ExpectedLegacy != actualLegacy {
|
||||
t.Errorf("unexpected new clusterid for tags %v: %s vs %s", g.Tags, g.ExpectedLegacy, actualLegacy)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
151
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/volumes.go
generated
vendored
Normal file
151
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/volumes.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright 2016 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 aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// awsVolumeRegMatch represents Regex Match for AWS volume.
|
||||
var awsVolumeRegMatch = regexp.MustCompile("^vol-[^/]*$")
|
||||
|
||||
// awsVolumeID represents the ID of the volume in the AWS API, e.g. vol-12345678
|
||||
// The "traditional" format is "vol-12345678"
|
||||
// A new longer format is also being introduced: "vol-12345678abcdef01"
|
||||
// We should not assume anything about the length or format, though it seems
|
||||
// reasonable to assume that volumes will continue to start with "vol-".
|
||||
type awsVolumeID string
|
||||
|
||||
func (i awsVolumeID) awsString() *string {
|
||||
return aws.String(string(i))
|
||||
}
|
||||
|
||||
// KubernetesVolumeID represents the id for a volume in the kubernetes API;
|
||||
// a few forms are recognized:
|
||||
// * aws://<zone>/<awsVolumeId>
|
||||
// * aws:///<awsVolumeId>
|
||||
// * <awsVolumeId>
|
||||
type KubernetesVolumeID string
|
||||
|
||||
// DiskInfo returns aws disk information in easy to use manner
|
||||
type diskInfo struct {
|
||||
ec2Instance *ec2.Instance
|
||||
nodeName types.NodeName
|
||||
volumeState string
|
||||
attachmentState string
|
||||
hasAttachment bool
|
||||
disk *awsDisk
|
||||
}
|
||||
|
||||
// MapToAWSVolumeID extracts the awsVolumeID from the KubernetesVolumeID
|
||||
func (name KubernetesVolumeID) MapToAWSVolumeID() (awsVolumeID, error) {
|
||||
// name looks like aws://availability-zone/awsVolumeId
|
||||
|
||||
// The original idea of the URL-style name was to put the AZ into the
|
||||
// host, so we could find the AZ immediately from the name without
|
||||
// querying the API. But it turns out we don't actually need it for
|
||||
// multi-AZ clusters, as we put the AZ into the labels on the PV instead.
|
||||
// However, if in future we want to support multi-AZ cluster
|
||||
// volume-awareness without using PersistentVolumes, we likely will
|
||||
// want the AZ in the host.
|
||||
|
||||
s := string(name)
|
||||
|
||||
if !strings.HasPrefix(s, "aws://") {
|
||||
// Assume a bare aws volume id (vol-1234...)
|
||||
// Build a URL with an empty host (AZ)
|
||||
s = "aws://" + "" + "/" + s
|
||||
}
|
||||
url, err := url.Parse(s)
|
||||
if err != nil {
|
||||
// TODO: Maybe we should pass a URL into the Volume functions
|
||||
return "", fmt.Errorf("Invalid disk name (%s): %v", name, err)
|
||||
}
|
||||
if url.Scheme != "aws" {
|
||||
return "", fmt.Errorf("Invalid scheme for AWS volume (%s)", name)
|
||||
}
|
||||
|
||||
awsID := url.Path
|
||||
awsID = strings.Trim(awsID, "/")
|
||||
|
||||
// We sanity check the resulting volume; the two known formats are
|
||||
// vol-12345678 and vol-12345678abcdef01
|
||||
if !awsVolumeRegMatch.MatchString(awsID) {
|
||||
return "", fmt.Errorf("Invalid format for AWS volume (%s)", name)
|
||||
}
|
||||
|
||||
return awsVolumeID(awsID), nil
|
||||
}
|
||||
|
||||
func GetAWSVolumeID(kubeVolumeID string) (string, error) {
|
||||
kid := KubernetesVolumeID(kubeVolumeID)
|
||||
awsID, err := kid.MapToAWSVolumeID()
|
||||
return string(awsID), err
|
||||
}
|
||||
|
||||
func (c *Cloud) checkIfAttachedToNode(diskName KubernetesVolumeID, nodeName types.NodeName) (*diskInfo, bool, error) {
|
||||
disk, err := newAWSDisk(c, diskName)
|
||||
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
awsDiskInfo := &diskInfo{
|
||||
disk: disk,
|
||||
}
|
||||
|
||||
info, err := disk.describeVolume()
|
||||
|
||||
if err != nil {
|
||||
glog.Warning("Error describing volume %s with %v", diskName, err)
|
||||
awsDiskInfo.volumeState = "unknown"
|
||||
return awsDiskInfo, false, err
|
||||
}
|
||||
|
||||
awsDiskInfo.volumeState = aws.StringValue(info.State)
|
||||
|
||||
if len(info.Attachments) > 0 {
|
||||
attachment := info.Attachments[0]
|
||||
awsDiskInfo.attachmentState = aws.StringValue(attachment.State)
|
||||
instanceID := aws.StringValue(attachment.InstanceId)
|
||||
instanceInfo, err := c.getInstanceByID(instanceID)
|
||||
|
||||
// This should never happen but if it does it could mean there was a race and instance
|
||||
// has been deleted
|
||||
if err != nil {
|
||||
fetchErr := fmt.Errorf("Error fetching instance %s for volume %s", instanceID, diskName)
|
||||
glog.Warning(fetchErr)
|
||||
return awsDiskInfo, false, fetchErr
|
||||
}
|
||||
|
||||
awsDiskInfo.ec2Instance = instanceInfo
|
||||
awsDiskInfo.nodeName = mapInstanceToNodeName(instanceInfo)
|
||||
awsDiskInfo.hasAttachment = true
|
||||
if awsDiskInfo.nodeName == nodeName {
|
||||
return awsDiskInfo, true, nil
|
||||
}
|
||||
}
|
||||
return awsDiskInfo, false, nil
|
||||
}
|
Reference in New Issue
Block a user