diff --git a/pkg/cloudprovider/providers/aws/BUILD b/pkg/cloudprovider/providers/aws/BUILD index eba94c8da41..8d9a8da5b40 100644 --- a/pkg/cloudprovider/providers/aws/BUILD +++ b/pkg/cloudprovider/providers/aws/BUILD @@ -18,6 +18,7 @@ go_library( "aws_routes.go", "aws_utils.go", "device_allocator.go", + "instances.go", "log_handler.go", "regions.go", "retry_handler.go", @@ -58,6 +59,7 @@ go_test( srcs = [ "aws_test.go", "device_allocator_test.go", + "instances_test.go", "regions_test.go", "retry_handler_test.go", "tags_test.go", diff --git a/pkg/cloudprovider/providers/aws/instances.go b/pkg/cloudprovider/providers/aws/instances.go new file mode 100644 index 00000000000..01830b65e39 --- /dev/null +++ b/pkg/cloudprovider/providers/aws/instances.go @@ -0,0 +1,80 @@ +/* +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" +) + +// 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://// +// * aws://// +// * +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 +} diff --git a/pkg/cloudprovider/providers/aws/instances_test.go b/pkg/cloudprovider/providers/aws/instances_test.go new file mode 100644 index 00000000000..79637ad91d5 --- /dev/null +++ b/pkg/cloudprovider/providers/aws/instances_test.go @@ -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) + } + } + } +} diff --git a/pkg/cloudprovider/providers/aws/volumes.go b/pkg/cloudprovider/providers/aws/volumes.go index c9d11ef8a7b..8ff342199d8 100644 --- a/pkg/cloudprovider/providers/aws/volumes.go +++ b/pkg/cloudprovider/providers/aws/volumes.go @@ -24,7 +24,7 @@ import ( "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" // A new longer format is also being introduced: "vol-12345678abcdef01" // We should not assume anything about the length or format, though it seems