@@ -19,8 +19,10 @@ limitations under the License.
19
19
package v2
20
20
21
21
import (
22
+ "errors"
22
23
"fmt"
23
24
"io"
25
+ "io/ioutil"
24
26
"regexp"
25
27
26
28
"github.com/aws/aws-sdk-go/aws"
@@ -30,12 +32,26 @@ import (
30
32
"github.com/aws/aws-sdk-go/aws/session"
31
33
"github.com/aws/aws-sdk-go/service/ec2"
32
34
35
+ "k8s.io/apimachinery/pkg/util/validation/field"
33
36
cloudprovider "k8s.io/cloud-provider"
37
+ awsconfigv1alpha1 "k8s.io/cloud-provider-aws/pkg/apis/config/v1alpha1"
38
+ "k8s.io/klog/v2"
39
+ "sigs.k8s.io/yaml"
34
40
)
35
41
36
42
func init () {
37
43
cloudprovider .RegisterCloudProvider (ProviderName , func (config io.Reader ) (cloudprovider.Interface , error ) {
38
- return newCloud ()
44
+ cfg , err := readAWSCloudConfig (config )
45
+ if err != nil {
46
+ return nil , fmt .Errorf ("failed to read AWS cloud provider config file: %v" , err )
47
+ }
48
+
49
+ errs := validateAWSCloudConfig (cfg )
50
+ if len (errs ) > 0 {
51
+ return nil , fmt .Errorf ("failed to validate AWS cloud config: %v" , errs .ToAggregate ())
52
+ }
53
+
54
+ return newCloud (cfg )
39
55
})
40
56
}
41
57
@@ -53,6 +69,14 @@ type cloud struct {
53
69
region string
54
70
ec2 EC2
55
71
metadata EC2Metadata
72
+ tags awsTagging
73
+ }
74
+
75
+ // EC2 is an interface defining only the methods we call from the AWS EC2 SDK.
76
+ type EC2 interface {
77
+ DescribeInstances (request * ec2.DescribeInstancesInput ) (* ec2.DescribeInstancesOutput , error )
78
+
79
+ CreateTags (* ec2.CreateTagsInput ) (* ec2.CreateTagsOutput , error )
56
80
}
57
81
58
82
// EC2Metadata is an abstraction over the AWS metadata service.
@@ -61,6 +85,48 @@ type EC2Metadata interface {
61
85
GetMetadata (path string ) (string , error )
62
86
}
63
87
88
+ func readAWSCloudConfig (config io.Reader ) (* awsconfigv1alpha1.AWSCloudConfig , error ) {
89
+ if config == nil {
90
+ return nil , errors .New ("no AWS cloud provider config file given" )
91
+ }
92
+
93
+ // read the config file
94
+ data , err := ioutil .ReadAll (config )
95
+ if err != nil {
96
+ return nil , fmt .Errorf ("unable to read cloud configuration from %q [%v]" , config , err )
97
+ }
98
+
99
+ var cfg awsconfigv1alpha1.AWSCloudConfig
100
+ err = yaml .Unmarshal (data , & cfg )
101
+ if err != nil {
102
+ // we got an error where the decode wasn't related to a missing type
103
+ return nil , err
104
+ }
105
+
106
+ return & cfg , nil
107
+ }
108
+
109
+ // validateAWSCloudConfig validates AWSCloudConfig
110
+ // clusterName is required
111
+ func validateAWSCloudConfig (config * awsconfigv1alpha1.AWSCloudConfig ) field.ErrorList {
112
+ allErrs := field.ErrorList {}
113
+
114
+ if config .Kind != "AWSCloudConfig" {
115
+ allErrs = append (allErrs , field .Invalid (field .NewPath ("kind" ), "invalid kind for cloud config: %q" , config .Kind ))
116
+ }
117
+
118
+ if config .APIVersion != awsconfigv1alpha1 .SchemeGroupVersion .String () {
119
+ allErrs = append (allErrs , field .Invalid (field .NewPath ("apiVersion" ), "invalid apiVersion for cloud config: %q" , config .APIVersion ))
120
+ }
121
+
122
+ fieldPath := field .NewPath ("config" )
123
+ if len (config .Config .ClusterName ) == 0 {
124
+ allErrs = append (allErrs , field .Required (fieldPath .Child ("clusterName" ), "cluster name cannot be empty" ))
125
+ }
126
+
127
+ return allErrs
128
+ }
129
+
64
130
func getAvailabilityZone (metadata EC2Metadata ) (string , error ) {
65
131
return metadata .GetMetadata ("placement/availability-zone" )
66
132
}
@@ -82,7 +148,7 @@ func azToRegion(az string) (string, error) {
82
148
}
83
149
84
150
// newCloud creates a new instance of AWSCloud.
85
- func newCloud () (cloudprovider.Interface , error ) {
151
+ func newCloud (cfg * awsconfigv1alpha1. AWSCloudConfig ) (cloudprovider.Interface , error ) {
86
152
sess , err := session .NewSession (& aws.Config {})
87
153
if err != nil {
88
154
return nil , fmt .Errorf ("unable to initialize AWS session: %v" , err )
@@ -112,11 +178,6 @@ func newCloud() (cloudprovider.Interface, error) {
112
178
return nil , err
113
179
}
114
180
115
- instances , err := newInstances (az , creds )
116
- if err != nil {
117
- return nil , err
118
- }
119
-
120
181
ec2Sess , err := session .NewSession (& aws.Config {
121
182
Region : aws .String (region ),
122
183
Credentials : creds ,
@@ -130,15 +191,29 @@ func newCloud() (cloudprovider.Interface, error) {
130
191
return nil , fmt .Errorf ("error creating AWS ec2 client: %q" , err )
131
192
}
132
193
133
- awsCloud := & cloud {
194
+ var tags awsTagging
195
+ if cfg .Config .ClusterName != "" {
196
+ tags , err = newAWSTags (cfg .Config .ClusterName )
197
+ if err != nil {
198
+ return nil , err
199
+ }
200
+ } else {
201
+ klog .Warning ("misconfigured cluster: no clusterName" )
202
+ }
203
+
204
+ instances , err := newInstances (az , creds , tags )
205
+ if err != nil {
206
+ return nil , err
207
+ }
208
+
209
+ return & cloud {
134
210
creds : creds ,
135
211
instances : instances ,
136
212
region : region ,
137
213
metadata : metadataClient ,
138
214
ec2 : ec2Service ,
139
- }
140
-
141
- return awsCloud , nil
215
+ tags : tags ,
216
+ }, nil
142
217
}
143
218
144
219
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
@@ -177,7 +252,7 @@ func (c *cloud) Routes() (cloudprovider.Routes, bool) {
177
252
178
253
// HasClusterID returns true if the cluster has a clusterID
179
254
func (c * cloud ) HasClusterID () bool {
180
- return false
255
+ return len ( c . tags . clusterName ()) > 0
181
256
}
182
257
183
258
// InstancesV2 is an implementation for instances and should only be implemented by external cloud providers.
0 commit comments