Skip to content

Commit a22f644

Browse files
committed
zones: initial implementation of Zones interface
1 parent 805fde8 commit a22f644

File tree

4 files changed

+424
-16
lines changed

4 files changed

+424
-16
lines changed

Diff for: pkg/providers/v2/cloud.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type cloud struct {
5353
region string
5454
ec2 EC2
5555
metadata EC2Metadata
56+
zones cloudprovider.Zones
5657
}
5758

5859
// EC2Metadata is an abstraction over the AWS metadata service.
@@ -117,6 +118,11 @@ func newCloud() (cloudprovider.Interface, error) {
117118
return nil, err
118119
}
119120

121+
zones, err := newZones(az, creds)
122+
if err != nil {
123+
return nil, err
124+
}
125+
120126
ec2Sess, err := session.NewSession(&aws.Config{
121127
Region: aws.String(region),
122128
Credentials: creds,
@@ -136,6 +142,7 @@ func newCloud() (cloudprovider.Interface, error) {
136142
region: region,
137143
metadata: metadataClient,
138144
ec2: ec2Service,
145+
zones: zones,
139146
}
140147

141148
return awsCloud, nil
@@ -167,7 +174,7 @@ func (c *cloud) Instances() (cloudprovider.Instances, bool) {
167174

168175
// Zones returns an implementation of Zones for Amazon Web Services.
169176
func (c *cloud) Zones() (cloudprovider.Zones, bool) {
170-
return nil, false
177+
return c.zones, true
171178
}
172179

173180
// Routes returns an implementation of Routes for Amazon Web Services.

Diff for: pkg/providers/v2/instances.go

+63-15
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/aws/aws-sdk-go/service/ec2"
3131

3232
v1 "k8s.io/api/core/v1"
33+
"k8s.io/apimachinery/pkg/types"
3334
cloudprovider "k8s.io/cloud-provider"
3435
)
3536

@@ -138,32 +139,79 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud
138139
// getInstance returns the instance if the instance with the given node info still exists.
139140
// If false an error will be returned, the instance will be immediately deleted by the cloud controller manager.
140141
func (i *instances) getInstance(ctx context.Context, node *v1.Node) (*ec2.Instance, error) {
141-
var request *ec2.DescribeInstancesInput
142142
if node.Spec.ProviderID == "" {
143-
// get Instance by private DNS name
144-
request = &ec2.DescribeInstancesInput{
145-
Filters: []*ec2.Filter{
146-
newEc2Filter("private-dns-name", node.Name),
147-
},
148-
}
143+
// get Instance by node name
149144
klog.V(4).Infof("looking for node by private DNS name %v", node.Name)
150-
} else {
151-
// get Instance by provider ID
152-
instanceID, err := parseInstanceIDFromProviderID(node.Spec.ProviderID)
145+
return getInstanceByPrivateDNSName(ctx, types.NodeName(node.Name), i.ec2)
146+
}
147+
148+
// get Instance by provider ID
149+
klog.V(4).Infof("looking for node by provider ID %v", node.Spec.ProviderID)
150+
return getInstanceByProviderID(ctx, node.Spec.ProviderID, i.ec2)
151+
}
152+
153+
// getInstanceByPrivateDNSName returns the instance if the instance with the given nodeName exists.
154+
// If false an error will be returned, the instance will be immediately deleted by the cloud controller manager.
155+
func getInstanceByPrivateDNSName(ctx context.Context, nodeName types.NodeName, ec2Client EC2) (*ec2.Instance, error) {
156+
request := &ec2.DescribeInstancesInput{
157+
Filters: []*ec2.Filter{
158+
newEc2Filter("private-dns-name", string(nodeName)),
159+
},
160+
}
161+
klog.V(4).Infof("looking for node by private DNS name %v", nodeName)
162+
163+
instances := []*ec2.Instance{}
164+
var nextToken *string
165+
for {
166+
response, err := ec2Client.DescribeInstances(request)
153167
if err != nil {
154-
return nil, err
168+
return nil, fmt.Errorf("error describing ec2 instances: %v", err)
169+
}
170+
171+
for _, reservation := range response.Reservations {
172+
instances = append(instances, reservation.Instances...)
155173
}
156174

157-
request = &ec2.DescribeInstancesInput{
158-
InstanceIds: []*string{aws.String(instanceID)},
175+
nextToken = response.NextToken
176+
if aws.StringValue(nextToken) == "" {
177+
break
159178
}
160-
klog.V(4).Infof("looking for node by provider ID %v", node.Spec.ProviderID)
179+
request.NextToken = nextToken
180+
}
181+
182+
if len(instances) == 0 {
183+
return nil, cloudprovider.InstanceNotFound
184+
}
185+
186+
if len(instances) > 1 {
187+
return nil, fmt.Errorf("getInstance: multiple instances found")
188+
}
189+
190+
state := instances[0].State.Name
191+
if *state == ec2.InstanceStateNameTerminated {
192+
return nil, fmt.Errorf("instance %v is terminated", instances[0].InstanceId)
193+
}
194+
195+
return instances[0], nil
196+
}
197+
198+
// getInstanceByProviderID returns the instance if the instance with the given providerID exists.
199+
// If false an error will be returned, the instance will be immediately deleted by the cloud controller manager.
200+
func getInstanceByProviderID(ctx context.Context, providerID string, ec2Client EC2) (*ec2.Instance, error) {
201+
instanceID, err := parseInstanceIDFromProviderID(providerID)
202+
if err != nil {
203+
return nil, err
204+
}
205+
206+
request := &ec2.DescribeInstancesInput{
207+
InstanceIds: []*string{aws.String(instanceID)},
161208
}
209+
klog.V(4).Infof("looking for node by provider ID %v", providerID)
162210

163211
instances := []*ec2.Instance{}
164212
var nextToken *string
165213
for {
166-
response, err := i.ec2.DescribeInstances(request)
214+
response, err := ec2Client.DescribeInstances(request)
167215
if err != nil {
168216
return nil, fmt.Errorf("error describing ec2 instances: %v", err)
169217
}

Diff for: pkg/providers/v2/zones.go

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package v2 is an out-of-tree only implementation of the AWS cloud provider.
18+
// It is not compatible with v1 and should only be used on new clusters.
19+
package v2
20+
21+
import (
22+
"context"
23+
"fmt"
24+
25+
"github.com/aws/aws-sdk-go/aws"
26+
"github.com/aws/aws-sdk-go/aws/credentials"
27+
"github.com/aws/aws-sdk-go/aws/session"
28+
"github.com/aws/aws-sdk-go/service/ec2"
29+
"k8s.io/apimachinery/pkg/types"
30+
cloudprovider "k8s.io/cloud-provider"
31+
)
32+
33+
// newZones returns an implementation of cloudprovider.Zones
34+
func newZones(az string, creds *credentials.Credentials) (cloudprovider.Zones, error) {
35+
region, err := azToRegion(az)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
awsConfig := &aws.Config{
41+
Region: aws.String(region),
42+
Credentials: creds,
43+
}
44+
awsConfig = awsConfig.WithCredentialsChainVerboseErrors(true)
45+
46+
sess, err := session.NewSession(awsConfig)
47+
if err != nil {
48+
return nil, fmt.Errorf("error creating new session: %v", err)
49+
}
50+
ec2Service := ec2.New(sess)
51+
52+
return &zones{
53+
availabilityZone: az,
54+
ec2: ec2Service,
55+
region: region,
56+
}, nil
57+
}
58+
59+
// zones is an implementation of cloudprovider.Zones
60+
type zones struct {
61+
availabilityZone string
62+
ec2 EC2
63+
region string
64+
}
65+
66+
// GetZone returns the Zone containing the current failure zone and locality region that the program is running in
67+
func (z *zones) GetZone(ctx context.Context) (cloudprovider.Zone, error) {
68+
return cloudprovider.Zone{
69+
FailureDomain: z.availabilityZone,
70+
Region: z.region,
71+
}, nil
72+
}
73+
74+
// GetZoneByProviderID returns the Zone containing the current zone and locality region of the node specified by providerID
75+
func (z *zones) GetZoneByProviderID(ctx context.Context, providerID string) (cloudprovider.Zone, error) {
76+
instance, err := getInstanceByProviderID(ctx, providerID, z.ec2)
77+
if err != nil {
78+
return cloudprovider.Zone{}, err
79+
}
80+
81+
az := instance.Placement.AvailabilityZone
82+
regionName, err := azToRegion(*az)
83+
if err != nil {
84+
return cloudprovider.Zone{}, err
85+
}
86+
87+
return cloudprovider.Zone{
88+
FailureDomain: *az,
89+
Region: regionName,
90+
}, nil
91+
}
92+
93+
// GetZoneByNodeName returns the Zone containing the current zone and locality region of the node specified by node name
94+
func (z *zones) GetZoneByNodeName(ctx context.Context, nodeName types.NodeName) (cloudprovider.Zone, error) {
95+
instance, err := getInstanceByPrivateDNSName(ctx, nodeName, z.ec2)
96+
if err != nil {
97+
return cloudprovider.Zone{}, err
98+
}
99+
100+
az := instance.Placement.AvailabilityZone
101+
regionName, err := azToRegion(*az)
102+
if err != nil {
103+
return cloudprovider.Zone{}, err
104+
}
105+
106+
return cloudprovider.Zone{
107+
FailureDomain: *az,
108+
Region: regionName,
109+
}, nil
110+
}

0 commit comments

Comments
 (0)