mirror of https://github.com/k3s-io/k3s
Merge pull request #47395 from justinsb/followup_47215_2
Automatic merge from submit-queue AWS cleanup Rationalize the existing code. ```release-note NONE ``` Issue #47394pull/6/head
commit
4d31eca42d
|
@ -18,6 +18,7 @@ go_library(
|
||||||
"aws_routes.go",
|
"aws_routes.go",
|
||||||
"aws_utils.go",
|
"aws_utils.go",
|
||||||
"device_allocator.go",
|
"device_allocator.go",
|
||||||
|
"instances.go",
|
||||||
"log_handler.go",
|
"log_handler.go",
|
||||||
"regions.go",
|
"regions.go",
|
||||||
"retry_handler.go",
|
"retry_handler.go",
|
||||||
|
@ -58,6 +59,7 @@ go_test(
|
||||||
srcs = [
|
srcs = [
|
||||||
"aws_test.go",
|
"aws_test.go",
|
||||||
"device_allocator_test.go",
|
"device_allocator_test.go",
|
||||||
|
"instances_test.go",
|
||||||
"regions_test.go",
|
"regions_test.go",
|
||||||
"retry_handler_test.go",
|
"retry_handler_test.go",
|
||||||
"tags_test.go",
|
"tags_test.go",
|
||||||
|
|
|
@ -204,7 +204,6 @@ type Services interface {
|
||||||
type EC2 interface {
|
type EC2 interface {
|
||||||
// Query EC2 for instances matching the filter
|
// Query EC2 for instances matching the filter
|
||||||
DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error)
|
DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error)
|
||||||
DescribeAddresses(request *ec2.DescribeAddressesInput) ([]*ec2.Address, error)
|
|
||||||
|
|
||||||
// Attach a volume to an instance
|
// Attach a volume to an instance
|
||||||
AttachVolume(*ec2.AttachVolumeInput) (*ec2.VolumeAttachment, error)
|
AttachVolume(*ec2.AttachVolumeInput) (*ec2.VolumeAttachment, error)
|
||||||
|
@ -609,20 +608,6 @@ func (s *awsSdkEC2) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*e
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of EC2.DescribeAddresses
|
|
||||||
func (s *awsSdkEC2) DescribeAddresses(request *ec2.DescribeAddressesInput) ([]*ec2.Address, error) {
|
|
||||||
requestTime := time.Now()
|
|
||||||
response, err := s.ec2.DescribeAddresses(request)
|
|
||||||
if err != nil {
|
|
||||||
recordAwsMetric("describe_address", 0, err)
|
|
||||||
return nil, fmt.Errorf("error listing AWS addresses: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
timeTaken := time.Since(requestTime).Seconds()
|
|
||||||
recordAwsMetric("describe_address", timeTaken, nil)
|
|
||||||
return response.Addresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements EC2.DescribeSecurityGroups
|
// Implements EC2.DescribeSecurityGroups
|
||||||
func (s *awsSdkEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) {
|
func (s *awsSdkEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) {
|
||||||
// Security groups are not paged
|
// Security groups are not paged
|
||||||
|
@ -996,38 +981,51 @@ func (c *Cloud) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) {
|
||||||
|
|
||||||
return addresses, nil
|
return addresses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
instance, err := c.getInstanceByNodeName(name)
|
instance, err := c.getInstanceByNodeName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getInstanceByNodeName failed for %q with %v", name, err)
|
return nil, fmt.Errorf("getInstanceByNodeName failed for %q with %v", name, err)
|
||||||
}
|
}
|
||||||
|
return extractNodeAddresses(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractNodeAddresses maps the instance information from EC2 to an array of NodeAddresses
|
||||||
|
func extractNodeAddresses(instance *ec2.Instance) ([]v1.NodeAddress, error) {
|
||||||
|
// Not clear if the order matters here, but we might as well indicate a sensible preference order
|
||||||
|
|
||||||
|
if instance == nil {
|
||||||
|
return nil, fmt.Errorf("nil instance passed to extractNodeAddresses")
|
||||||
|
}
|
||||||
|
|
||||||
addresses := []v1.NodeAddress{}
|
addresses := []v1.NodeAddress{}
|
||||||
|
|
||||||
if !isNilOrEmpty(instance.PrivateIpAddress) {
|
privateIPAddress := aws.StringValue(instance.PrivateIpAddress)
|
||||||
ipAddress := *instance.PrivateIpAddress
|
if privateIPAddress != "" {
|
||||||
ip := net.ParseIP(ipAddress)
|
ip := net.ParseIP(privateIPAddress)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil, fmt.Errorf("EC2 instance had invalid private address: %s (%s)", orEmpty(instance.InstanceId), ipAddress)
|
return nil, fmt.Errorf("EC2 instance had invalid private address: %s (%s)", aws.StringValue(instance.InstanceId), privateIPAddress)
|
||||||
}
|
}
|
||||||
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ip.String()})
|
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ip.String()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Other IP addresses (multiple ips)?
|
// TODO: Other IP addresses (multiple ips)?
|
||||||
if !isNilOrEmpty(instance.PublicIpAddress) {
|
publicIPAddress := aws.StringValue(instance.PublicIpAddress)
|
||||||
ipAddress := *instance.PublicIpAddress
|
if publicIPAddress != "" {
|
||||||
ip := net.ParseIP(ipAddress)
|
ip := net.ParseIP(publicIPAddress)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil, fmt.Errorf("EC2 instance had invalid public address: %s (%s)", orEmpty(instance.InstanceId), ipAddress)
|
return nil, fmt.Errorf("EC2 instance had invalid public address: %s (%s)", aws.StringValue(instance.InstanceId), publicIPAddress)
|
||||||
}
|
}
|
||||||
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeExternalIP, Address: ip.String()})
|
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeExternalIP, Address: ip.String()})
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isNilOrEmpty(instance.PrivateDnsName) {
|
privateDNSName := aws.StringValue(instance.PrivateDnsName)
|
||||||
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalDNS, Address: *instance.PrivateDnsName})
|
if privateDNSName != "" {
|
||||||
|
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalDNS, Address: privateDNSName})
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isNilOrEmpty(instance.PublicDnsName) {
|
publicDNSName := aws.StringValue(instance.PublicDnsName)
|
||||||
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeExternalDNS, Address: *instance.PublicDnsName})
|
if publicDNSName != "" {
|
||||||
|
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeExternalDNS, Address: publicDNSName})
|
||||||
}
|
}
|
||||||
|
|
||||||
return addresses, nil
|
return addresses, nil
|
||||||
|
@ -1037,45 +1035,17 @@ func (c *Cloud) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) {
|
||||||
// This method will not be called from the node that is requesting this ID. i.e. metadata service
|
// This method will not be called from the node that is requesting this ID. i.e. metadata service
|
||||||
// and other local methods cannot be used here
|
// and other local methods cannot be used here
|
||||||
func (c *Cloud) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
|
func (c *Cloud) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
|
||||||
instanceID, error := instanceIDFromProviderID(providerID)
|
instanceID, err := kubernetesInstanceID(providerID).mapToAWSInstanceID()
|
||||||
|
if err != nil {
|
||||||
if error != nil {
|
return nil, err
|
||||||
return nil, error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addresses, error := c.describeAddressesByInstanceID(instanceID)
|
instance, err := describeInstance(c.ec2, instanceID)
|
||||||
|
if err != nil {
|
||||||
if error != nil {
|
return nil, err
|
||||||
return nil, error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instances, error := c.describeInstancesByInstanceID(instanceID)
|
return extractNodeAddresses(instance)
|
||||||
|
|
||||||
if error != nil {
|
|
||||||
return nil, error
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeAddresses := []v1.NodeAddress{}
|
|
||||||
|
|
||||||
for _, address := range addresses {
|
|
||||||
convertedAddress, error := convertAwsAddress(address)
|
|
||||||
if error != nil {
|
|
||||||
return nil, error
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeAddresses = append(nodeAddresses, convertedAddress...)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, instance := range instances {
|
|
||||||
addresses, error := instanceAddresses(instance)
|
|
||||||
if error != nil {
|
|
||||||
return nil, error
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeAddresses = append(nodeAddresses, addresses...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeAddresses, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExternalID returns the cloud provider ID of the node with the specified nodeName (deprecated).
|
// ExternalID returns the cloud provider ID of the node with the specified nodeName (deprecated).
|
||||||
|
@ -1114,16 +1084,14 @@ func (c *Cloud) InstanceID(nodeName types.NodeName) (string, error) {
|
||||||
// This method will not be called from the node that is requesting this ID. i.e. metadata service
|
// This method will not be called from the node that is requesting this ID. i.e. metadata service
|
||||||
// and other local methods cannot be used here
|
// and other local methods cannot be used here
|
||||||
func (c *Cloud) InstanceTypeByProviderID(providerID string) (string, error) {
|
func (c *Cloud) InstanceTypeByProviderID(providerID string) (string, error) {
|
||||||
instanceID, error := instanceIDFromProviderID(providerID)
|
instanceID, err := kubernetesInstanceID(providerID).mapToAWSInstanceID()
|
||||||
|
if err != nil {
|
||||||
if error != nil {
|
return "", err
|
||||||
return "", error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instance, error := c.describeInstanceByInstanceID(instanceID)
|
instance, err := describeInstance(c.ec2, instanceID)
|
||||||
|
if err != nil {
|
||||||
if error != nil {
|
return "", err
|
||||||
return "", error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return aws.StringValue(instance.InstanceType), nil
|
return aws.StringValue(instance.InstanceType), nil
|
||||||
|
@ -1138,7 +1106,7 @@ func (c *Cloud) InstanceType(nodeName types.NodeName) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("getInstanceByNodeName failed for %q with %v", nodeName, err)
|
return "", fmt.Errorf("getInstanceByNodeName failed for %q with %v", nodeName, err)
|
||||||
}
|
}
|
||||||
return orEmpty(inst.InstanceType), nil
|
return aws.StringValue(inst.InstanceType), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a list of instances matching regex string.
|
// Return a list of instances matching regex string.
|
||||||
|
@ -1307,22 +1275,7 @@ func (i *awsInstance) getInstanceType() *awsInstanceType {
|
||||||
|
|
||||||
// Gets the full information about this instance from the EC2 API
|
// Gets the full information about this instance from the EC2 API
|
||||||
func (i *awsInstance) describeInstance() (*ec2.Instance, error) {
|
func (i *awsInstance) describeInstance() (*ec2.Instance, error) {
|
||||||
instanceID := i.awsID
|
return describeInstance(i.ec2, awsInstanceID(i.awsID))
|
||||||
request := &ec2.DescribeInstancesInput{
|
|
||||||
InstanceIds: []*string{&instanceID},
|
|
||||||
}
|
|
||||||
|
|
||||||
instances, err := i.ec2.DescribeInstances(request)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(instances) == 0 {
|
|
||||||
return nil, fmt.Errorf("no instances found for instance: %s", i.awsID)
|
|
||||||
}
|
|
||||||
if len(instances) > 1 {
|
|
||||||
return nil, fmt.Errorf("multiple instances found for instance: %s", i.awsID)
|
|
||||||
}
|
|
||||||
return instances[0], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the mountDevice already assigned to the volume, or assigns an unused mountDevice.
|
// Gets the mountDevice already assigned to the volume, or assigns an unused mountDevice.
|
||||||
|
@ -3407,25 +3360,6 @@ func (c *Cloud) getInstancesByNodeNames(nodeNames []string, states ...string) ([
|
||||||
return instances, nil
|
return instances, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cloud) describeInstancesByInstanceID(instanceID string) ([]*ec2.Instance, error) {
|
|
||||||
filters := []*ec2.Filter{newEc2Filter("instance-id", instanceID)}
|
|
||||||
return c.describeInstances(filters)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cloud) describeInstanceByInstanceID(instanceID string) (*ec2.Instance, error) {
|
|
||||||
filters := []*ec2.Filter{newEc2Filter("instance-id", instanceID)}
|
|
||||||
instances, err := c.describeInstances(filters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(instances) != 1 {
|
|
||||||
return nil, fmt.Errorf("expected 1 instance, found %d for instanceID %s", len(instances), instanceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return instances[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cloud) describeInstances(filters []*ec2.Filter) ([]*ec2.Instance, error) {
|
func (c *Cloud) describeInstances(filters []*ec2.Filter) ([]*ec2.Instance, error) {
|
||||||
filters = c.tagging.addFilters(filters)
|
filters = c.tagging.addFilters(filters)
|
||||||
request := &ec2.DescribeInstancesInput{
|
request := &ec2.DescribeInstancesInput{
|
||||||
|
@ -3446,21 +3380,6 @@ func (c *Cloud) describeInstances(filters []*ec2.Filter) ([]*ec2.Instance, error
|
||||||
return matches, nil
|
return matches, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cloud) describeAddressesByInstanceID(instanceID string) ([]*ec2.Address, error) {
|
|
||||||
filters := []*ec2.Filter{newEc2Filter("instance-id", instanceID)}
|
|
||||||
params := &ec2.DescribeAddressesInput{
|
|
||||||
Filters: filters,
|
|
||||||
}
|
|
||||||
|
|
||||||
addresses, error := c.ec2.DescribeAddresses(params)
|
|
||||||
|
|
||||||
if error != nil {
|
|
||||||
return nil, error
|
|
||||||
}
|
|
||||||
|
|
||||||
return addresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// mapNodeNameToPrivateDNSName maps a k8s NodeName to an AWS Instance PrivateDNSName
|
// mapNodeNameToPrivateDNSName maps a k8s NodeName to an AWS Instance PrivateDNSName
|
||||||
// This is a simple string cast
|
// This is a simple string cast
|
||||||
func mapNodeNameToPrivateDNSName(nodeName types.NodeName) string {
|
func mapNodeNameToPrivateDNSName(nodeName types.NodeName) string {
|
||||||
|
@ -3518,78 +3437,6 @@ func (c *Cloud) getFullInstance(nodeName types.NodeName) (*awsInstance, *ec2.Ins
|
||||||
return awsInstance, instance, err
|
return awsInstance, instance, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func instanceAddresses(instance *ec2.Instance) ([]v1.NodeAddress, error) {
|
|
||||||
addresses := []v1.NodeAddress{}
|
|
||||||
privateDNSName := aws.StringValue(instance.PrivateDnsName)
|
|
||||||
unsafePrivateIP := aws.StringValue(instance.PrivateIpAddress)
|
|
||||||
publicDNSName := aws.StringValue(instance.PublicDnsName)
|
|
||||||
unsafePublicIP := aws.StringValue(instance.PublicIpAddress)
|
|
||||||
|
|
||||||
if privateDNSName != "" {
|
|
||||||
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalDNS, Address: privateDNSName})
|
|
||||||
}
|
|
||||||
|
|
||||||
if unsafePrivateIP != "" {
|
|
||||||
ip := net.ParseIP(unsafePrivateIP)
|
|
||||||
if ip != nil {
|
|
||||||
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ip.String()})
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("EC2 address had invalid private IP: %s", unsafePrivateIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if publicDNSName != "" {
|
|
||||||
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeExternalDNS, Address: publicDNSName})
|
|
||||||
}
|
|
||||||
|
|
||||||
if unsafePublicIP != "" {
|
|
||||||
ip := net.ParseIP(unsafePublicIP)
|
|
||||||
if ip != nil {
|
|
||||||
addresses = append(addresses, v1.NodeAddress{Type: v1.NodeExternalIP, Address: ip.String()})
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("EC2 address had invalid public IP: %s", unsafePublicIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return addresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAwsAddress(address *ec2.Address) ([]v1.NodeAddress, error) {
|
|
||||||
nodeAddresses := []v1.NodeAddress{}
|
|
||||||
if aws.StringValue(address.PrivateIpAddress) != "" {
|
|
||||||
unsafeIP := *address.PrivateIpAddress
|
|
||||||
ip := net.ParseIP(unsafeIP)
|
|
||||||
if ip != nil {
|
|
||||||
nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ip.String()})
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("EC2 address had invalid private IP: %s", unsafeIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if aws.StringValue(address.PublicIp) != "" {
|
|
||||||
unsafeIP := *address.PublicIp
|
|
||||||
ip := net.ParseIP(unsafeIP)
|
|
||||||
if ip != nil {
|
|
||||||
nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeExternalIP, Address: ip.String()})
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("EC2 address had invalid public IP: %s", unsafeIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeAddresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var providerIDRegexp = regexp.MustCompile(`^aws://([^/]+)$`)
|
|
||||||
|
|
||||||
func instanceIDFromProviderID(providerID string) (instanceID string, err error) {
|
|
||||||
matches := providerIDRegexp.FindStringSubmatch(providerID)
|
|
||||||
if len(matches) != 2 {
|
|
||||||
return "", fmt.Errorf("ProviderID \"%s\" didn't match expected format \"aws://InstanceID\"", providerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return matches[1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setNodeDisk(
|
func setNodeDisk(
|
||||||
nodeDiskMap map[types.NodeName]map[KubernetesVolumeID]bool,
|
nodeDiskMap map[types.NodeName]map[KubernetesVolumeID]bool,
|
||||||
volumeID KubernetesVolumeID,
|
volumeID KubernetesVolumeID,
|
||||||
|
|
|
@ -322,12 +322,6 @@ func (self *FakeEC2) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*
|
||||||
return matches, nil
|
return matches, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FakeEC2) DescribeAddresses(request *ec2.DescribeAddressesInput) ([]*ec2.Address, error) {
|
|
||||||
addresses := []*ec2.Address{}
|
|
||||||
|
|
||||||
return addresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type FakeMetadata struct {
|
type FakeMetadata struct {
|
||||||
aws *FakeAWSServices
|
aws *FakeAWSServices
|
||||||
}
|
}
|
||||||
|
@ -1356,37 +1350,3 @@ func TestGetLoadBalancerAdditionalTags(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInstanceIDFromProviderID(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
providerID string
|
|
||||||
instanceID string
|
|
||||||
fail bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
providerID: "aws://i-0194bbdb81a49b169",
|
|
||||||
instanceID: "i-0194bbdb81a49b169",
|
|
||||||
fail: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
providerID: "i-0194bbdb81a49b169",
|
|
||||||
instanceID: "",
|
|
||||||
fail: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
instanceID, err := instanceIDFromProviderID(test.providerID)
|
|
||||||
if (err != nil) != test.fail {
|
|
||||||
t.Errorf("%s yielded `err != nil` as %t. expected %t", test.providerID, (err != nil), test.fail)
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.fail {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if instanceID != test.instanceID {
|
|
||||||
t.Errorf("%s yielded %s. expected %s", test.providerID, instanceID, test.instanceID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// TODO: Regex match?
|
||||||
|
if awsID == "" || strings.Contains(awsID, "/") || !strings.HasPrefix(awsID, "i-") {
|
||||||
|
return "", fmt.Errorf("Invalid format for AWS instance (%s)", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return awsInstanceID(awsID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
)
|
)
|
||||||
|
|
||||||
// awsVolumeID represents the ID of the volume in the AWS API, e.g. vol-12345678a
|
// awsVolumeID represents the ID of the volume in the AWS API, e.g. vol-12345678
|
||||||
// The "traditional" format is "vol-12345678"
|
// The "traditional" format is "vol-12345678"
|
||||||
// A new longer format is also being introduced: "vol-12345678abcdef01"
|
// A new longer format is also being introduced: "vol-12345678abcdef01"
|
||||||
// We should not assume anything about the length or format, though it seems
|
// We should not assume anything about the length or format, though it seems
|
||||||
|
|
Loading…
Reference in New Issue