@@ -18,12 +18,15 @@ import (
18
18
"github.com/aws/aws-sdk-go/service/elb"
19
19
"github.com/aws/aws-sdk-go/service/route53"
20
20
21
+ "github.com/aws/aws-sdk-go/service/s3"
21
22
"github.com/coreos/kube-aws/config"
22
23
)
23
24
24
25
// VERSION set by build script
25
26
var VERSION = "UNKNOWN"
26
27
28
+ var CFN_TEMPLATE_SIZE_LIMIT = 51200
29
+
27
30
type Info struct {
28
31
Name string
29
32
ControllerHost string
@@ -61,9 +64,34 @@ type Cluster struct {
61
64
session * session.Session
62
65
}
63
66
64
- func (c * Cluster ) ValidateStack (stackBody string ) (string , error ) {
65
- validateInput := cloudformation.ValidateTemplateInput {
66
- TemplateBody : & stackBody ,
67
+ func (c * Cluster ) uploadTemplateIfNecessary (s3Svc s3ObjectPutterService , stackBody string , s3URI string ) (* string , error ) {
68
+ if len (stackBody ) > CFN_TEMPLATE_SIZE_LIMIT {
69
+ if s3URI == "" {
70
+ return nil , fmt .Errorf ("stack template's size(=%d) exceeds the 51200 bytes limit of cloudformation. `--s3-uri s3://<bucket>/path/to/dir` must be specified to upload it to S3 beforehand" , len (stackBody ))
71
+ }
72
+
73
+ templateURL , err := c .uploadTemplate (s3Svc , s3URI , stackBody )
74
+ if err != nil {
75
+ return nil , fmt .Errorf ("Template upload failed: %v" , err )
76
+ }
77
+
78
+ return & templateURL , nil
79
+ }
80
+
81
+ return nil , nil
82
+ }
83
+
84
+ func (c * Cluster ) ValidateStack (stackBody string , s3URI string ) (string , error ) {
85
+ validateInput := cloudformation.ValidateTemplateInput {}
86
+
87
+ templateURL , uploadErr := c .uploadTemplateIfNecessary (s3 .New (c .session ), stackBody , s3URI )
88
+
89
+ if uploadErr != nil {
90
+ return "" , fmt .Errorf ("template upload failed: %v" , uploadErr )
91
+ } else if templateURL != nil {
92
+ validateInput .TemplateURL = templateURL
93
+ } else {
94
+ validateInput .TemplateBody = aws .String (stackBody )
67
95
}
68
96
69
97
cfSvc := cloudformation .New (c .session )
@@ -140,7 +168,29 @@ func (c *Cluster) validateExistingVPCState(ec2Svc ec2Service) error {
140
168
return nil
141
169
}
142
170
143
- func (c * Cluster ) Create (stackBody string ) error {
171
+ func (c * Cluster ) createStack (cfSvc * cloudformation.CloudFormation , s3Svc s3ObjectPutterService , stackBody string , s3URI string ) (* cloudformation.CreateStackOutput , error ) {
172
+ templateURL , uploadErr := c .uploadTemplateIfNecessary (s3Svc , stackBody , s3URI )
173
+
174
+ if uploadErr != nil {
175
+ return nil , fmt .Errorf ("template upload failed: %v" , uploadErr )
176
+ } else if templateURL != nil {
177
+ resp , err := c .createStackFromTemplateURL (cfSvc , * templateURL )
178
+ if err != nil {
179
+ return nil , fmt .Errorf ("stack creation failed: %v" , err )
180
+ }
181
+
182
+ return resp , nil
183
+ } else {
184
+ resp , err := c .createStackFromTemplateBody (cfSvc , stackBody )
185
+ if err != nil {
186
+ return nil , fmt .Errorf ("stack creation failed: %v" , err )
187
+ }
188
+
189
+ return resp , nil
190
+ }
191
+ }
192
+
193
+ func (c * Cluster ) Create (stackBody string , s3URI string ) error {
144
194
r53Svc := route53 .New (c .session )
145
195
if err := c .validateDNSConfig (r53Svc ); err != nil {
146
196
return err
@@ -164,7 +214,9 @@ func (c *Cluster) Create(stackBody string) error {
164
214
}
165
215
166
216
cfSvc := cloudformation .New (c .session )
167
- resp , err := c .createStack (cfSvc , stackBody )
217
+ s3Svc := s3 .New (c .session )
218
+
219
+ resp , err := c .createStack (cfSvc , s3Svc , stackBody , s3URI )
168
220
if err != nil {
169
221
return err
170
222
}
@@ -211,24 +263,73 @@ func (c *Cluster) Create(stackBody string) error {
211
263
}
212
264
}
213
265
214
- type cloudformationService interface {
266
+ type cloudformationStackCreationService interface {
215
267
CreateStack (* cloudformation.CreateStackInput ) (* cloudformation.CreateStackOutput , error )
216
268
}
217
269
218
- func (c * Cluster ) createStack (cfSvc cloudformationService , stackBody string ) (* cloudformation.CreateStackOutput , error ) {
270
+ type cloudformationStackUpdateService interface {
271
+ UpdateStack (input * cloudformation.UpdateStackInput ) (* cloudformation.UpdateStackOutput , error )
272
+ }
273
+
274
+ type s3ObjectPutterService interface {
275
+ PutObject (input * s3.PutObjectInput ) (* s3.PutObjectOutput , error )
276
+ }
277
+
278
+ func (c * Cluster ) uploadTemplate (s3Svc s3ObjectPutterService , s3URI string , stackBody string ) (string , error ) {
279
+ re := regexp .MustCompile ("s3://(?P<bucket>[^/]+)/(?P<directory>.+[^/])/*$" )
280
+ matches := re .FindStringSubmatch (s3URI )
281
+
282
+ var bucket string
283
+ var key string
284
+ if len (matches ) == 3 {
285
+ directory := matches [2 ]
286
+
287
+ bucket = matches [1 ]
288
+ key = fmt .Sprintf ("%s/%s/stack.json" , directory , c .ClusterName )
289
+ } else {
290
+ re := regexp .MustCompile ("s3://(?P<bucket>[^/]+)/*$" )
291
+ matches := re .FindStringSubmatch (s3URI )
292
+
293
+ if len (matches ) == 2 {
294
+ bucket = matches [1 ]
295
+ key = fmt .Sprintf ("%s/stack.json" , c .ClusterName )
296
+ } else {
297
+ return "" , fmt .Errorf ("failed to parse s3 uri(=%s): The valid uri pattern for it is s3://mybucket/mydir or s3://mybucket" , s3URI )
298
+ }
299
+ }
300
+
301
+ contentLength := int64 (len (stackBody ))
302
+ body := strings .NewReader (stackBody )
303
+
304
+ _ , err := s3Svc .PutObject (& s3.PutObjectInput {
305
+ Bucket : aws .String (bucket ),
306
+ Key : aws .String (key ),
307
+ Body : body ,
308
+ ContentLength : aws .Int64 (contentLength ),
309
+ ContentType : aws .String ("application/json" ),
310
+ })
311
+
312
+ if err != nil {
313
+ return "" , err
314
+ }
315
+
316
+ templateURL := fmt .Sprintf ("https://s3.amazonaws.com/%s/%s" , bucket , key )
317
+
318
+ return templateURL , nil
319
+ }
219
320
321
+ func (c * Cluster ) baseCreateStackInput () * cloudformation.CreateStackInput {
220
322
var tags []* cloudformation.Tag
221
323
for k , v := range c .StackTags {
222
324
key := k
223
325
value := v
224
326
tags = append (tags , & cloudformation.Tag {Key : & key , Value : & value })
225
327
}
226
328
227
- creq := & cloudformation.CreateStackInput {
329
+ return & cloudformation.CreateStackInput {
228
330
StackName : aws .String (c .ClusterName ),
229
331
OnFailure : aws .String (cloudformation .OnFailureDoNothing ),
230
332
Capabilities : []* string {aws .String (cloudformation .CapabilityCapabilityIam )},
231
- TemplateBody : & stackBody ,
232
333
Tags : tags ,
233
334
StackPolicyBody : aws .String (`{
234
335
"Statement" : [
@@ -248,8 +349,18 @@ func (c *Cluster) createStack(cfSvc cloudformationService, stackBody string) (*c
248
349
}
249
350
` ),
250
351
}
352
+ }
251
353
252
- return cfSvc .CreateStack (creq )
354
+ func (c * Cluster ) createStackFromTemplateBody (cfSvc cloudformationStackCreationService , stackBody string ) (* cloudformation.CreateStackOutput , error ) {
355
+ input := c .baseCreateStackInput ()
356
+ input .TemplateBody = & stackBody
357
+ return cfSvc .CreateStack (input )
358
+ }
359
+
360
+ func (c * Cluster ) createStackFromTemplateURL (cfSvc cloudformationStackCreationService , stackTemplateURL string ) (* cloudformation.CreateStackOutput , error ) {
361
+ input := c .baseCreateStackInput ()
362
+ input .TemplateURL = & stackTemplateURL
363
+ return cfSvc .CreateStack (input )
253
364
}
254
365
255
366
/*
@@ -320,20 +431,57 @@ func (c *Cluster) lockEtcdResources(cfSvc *cloudformation.CloudFormation, stackB
320
431
return buf .String (), nil
321
432
}
322
433
323
- func (c * Cluster ) Update (stackBody string ) (string , error ) {
434
+ func (c * Cluster ) baseUpdateStackInput () * cloudformation.UpdateStackInput {
435
+ return & cloudformation.UpdateStackInput {
436
+ Capabilities : []* string {aws .String (cloudformation .CapabilityCapabilityIam )},
437
+ StackName : aws .String (c .ClusterName ),
438
+ }
439
+ }
440
+
441
+ func (c * Cluster ) updateStackWithTemplateBody (cfSvc cloudformationStackUpdateService , stackBody string ) (* cloudformation.UpdateStackOutput , error ) {
442
+ input := c .baseUpdateStackInput ()
443
+ input .TemplateBody = aws .String (stackBody )
444
+ return cfSvc .UpdateStack (input )
445
+ }
446
+
447
+ func (c * Cluster ) updateStackWithTemplateURL (cfSvc cloudformationStackUpdateService , templateURL string ) (* cloudformation.UpdateStackOutput , error ) {
448
+ input := c .baseUpdateStackInput ()
449
+ input .TemplateURL = aws .String (templateURL )
450
+ return cfSvc .UpdateStack (input )
451
+ }
452
+
453
+ func (c * Cluster ) updateStack (cfSvc cloudformationStackUpdateService , s3Svc s3ObjectPutterService , stackBody string , s3URI string ) (* cloudformation.UpdateStackOutput , error ) {
454
+ templateURL , uploadErr := c .uploadTemplateIfNecessary (s3Svc , stackBody , s3URI )
455
+
456
+ if uploadErr != nil {
457
+ return nil , fmt .Errorf ("template upload failed: %v" , uploadErr )
458
+ } else if templateURL != nil {
459
+ resp , err := c .updateStackWithTemplateURL (cfSvc , * templateURL )
460
+ if err != nil {
461
+ return nil , fmt .Errorf ("stack update failed: %v" , err )
462
+ }
463
+
464
+ return resp , nil
465
+ } else {
466
+ resp , err := c .updateStackWithTemplateBody (cfSvc , stackBody )
467
+ if err != nil {
468
+ return nil , fmt .Errorf ("stack update failed: %v" , err )
469
+ }
470
+
471
+ return resp , nil
472
+ }
473
+ }
324
474
475
+ func (c * Cluster ) Update (stackBody string , s3URI string ) (string , error ) {
325
476
cfSvc := cloudformation .New (c .session )
477
+ s3Svc := s3 .New (c .session )
478
+
326
479
var err error
327
480
if stackBody , err = c .lockEtcdResources (cfSvc , stackBody ); err != nil {
328
481
return "" , err
329
482
}
330
- input := & cloudformation.UpdateStackInput {
331
- Capabilities : []* string {aws .String (cloudformation .CapabilityCapabilityIam )},
332
- StackName : aws .String (c .ClusterName ),
333
- TemplateBody : aws .String (stackBody ),
334
- }
335
483
336
- updateOutput , err := cfSvc . UpdateStack ( input )
484
+ updateOutput , err := c . updateStack ( cfSvc , s3Svc , stackBody , s3URI )
337
485
if err != nil {
338
486
return "" , fmt .Errorf ("error updating cloudformation stack: %v" , err )
339
487
}
0 commit comments