~juju-qa/ubuntu/yakkety/juju/2.0-rc3-again

« back to all changes in this revision

Viewing changes to src/launchpad.net/goamz/ec2/ec2.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-04-24 22:34:47 UTC
  • Revision ID: package-import@ubuntu.com-20130424223447-f0qdji7ubnyo0s71
Tags: upstream-1.10.0.1
ImportĀ upstreamĀ versionĀ 1.10.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// goamz - Go packages to interact with the Amazon Web Services.
 
3
//
 
4
//   https://wiki.ubuntu.com/goamz
 
5
//
 
6
// Copyright (c) 2011 Canonical Ltd.
 
7
//
 
8
// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
 
9
//
 
10
 
 
11
package ec2
 
12
 
 
13
import (
 
14
        "crypto/rand"
 
15
        "encoding/hex"
 
16
        "encoding/xml"
 
17
        "fmt"
 
18
        "launchpad.net/goamz/aws"
 
19
        "log"
 
20
        "net/http"
 
21
        "net/http/httputil"
 
22
        "net/url"
 
23
        "sort"
 
24
        "strconv"
 
25
        "time"
 
26
)
 
27
 
 
28
const debug = false
 
29
 
 
30
// The EC2 type encapsulates operations with a specific EC2 region.
 
31
type EC2 struct {
 
32
        aws.Auth
 
33
        aws.Region
 
34
        private byte // Reserve the right of using private data.
 
35
}
 
36
 
 
37
// New creates a new EC2.
 
38
func New(auth aws.Auth, region aws.Region) *EC2 {
 
39
        return &EC2{auth, region, 0}
 
40
}
 
41
 
 
42
// ----------------------------------------------------------------------------
 
43
// Filtering helper.
 
44
 
 
45
// Filter builds filtering parameters to be used in an EC2 query which supports
 
46
// filtering.  For example:
 
47
//
 
48
//     filter := NewFilter()
 
49
//     filter.Add("architecture", "i386")
 
50
//     filter.Add("launch-index", "0")
 
51
//     resp, err := ec2.Instances(nil, filter)
 
52
//
 
53
type Filter struct {
 
54
        m map[string][]string
 
55
}
 
56
 
 
57
// NewFilter creates a new Filter.
 
58
func NewFilter() *Filter {
 
59
        return &Filter{make(map[string][]string)}
 
60
}
 
61
 
 
62
// Add appends a filtering parameter with the given name and value(s).
 
63
func (f *Filter) Add(name string, value ...string) {
 
64
        f.m[name] = append(f.m[name], value...)
 
65
}
 
66
 
 
67
func (f *Filter) addParams(params map[string]string) {
 
68
        if f != nil {
 
69
                a := make([]string, len(f.m))
 
70
                i := 0
 
71
                for k := range f.m {
 
72
                        a[i] = k
 
73
                        i++
 
74
                }
 
75
                sort.StringSlice(a).Sort()
 
76
                for i, k := range a {
 
77
                        prefix := "Filter." + strconv.Itoa(i+1)
 
78
                        params[prefix+".Name"] = k
 
79
                        for j, v := range f.m[k] {
 
80
                                params[prefix+".Value."+strconv.Itoa(j+1)] = v
 
81
                        }
 
82
                }
 
83
        }
 
84
}
 
85
 
 
86
// ----------------------------------------------------------------------------
 
87
// Request dispatching logic.
 
88
 
 
89
// Error encapsulates an error returned by EC2.
 
90
//
 
91
// See http://goo.gl/VZGuC for more details.
 
92
type Error struct {
 
93
        // HTTP status code (200, 403, ...)
 
94
        StatusCode int
 
95
        // EC2 error code ("UnsupportedOperation", ...)
 
96
        Code string
 
97
        // The human-oriented error message
 
98
        Message   string
 
99
        RequestId string `xml:"RequestID"`
 
100
}
 
101
 
 
102
func (err *Error) Error() string {
 
103
        if err.Code == "" {
 
104
                return err.Message
 
105
        }
 
106
 
 
107
        return fmt.Sprintf("%s (%s)", err.Message, err.Code)
 
108
}
 
109
 
 
110
// For now a single error inst is being exposed. In the future it may be useful
 
111
// to provide access to all of them, but rather than doing it as an array/slice,
 
112
// use a *next pointer, so that it's backward compatible and it continues to be
 
113
// easy to handle the first error, which is what most people will want.
 
114
type xmlErrors struct {
 
115
        RequestId string  `xml:"RequestID"`
 
116
        Errors    []Error `xml:"Errors>Error"`
 
117
}
 
118
 
 
119
var timeNow = time.Now
 
120
 
 
121
func (ec2 *EC2) query(params map[string]string, resp interface{}) error {
 
122
        params["Version"] = "2011-12-15"
 
123
        params["Timestamp"] = timeNow().In(time.UTC).Format(time.RFC3339)
 
124
        endpoint, err := url.Parse(ec2.Region.EC2Endpoint)
 
125
        if err != nil {
 
126
                return err
 
127
        }
 
128
        if endpoint.Path == "" {
 
129
                endpoint.Path = "/"
 
130
        }
 
131
        sign(ec2.Auth, "GET", endpoint.Path, params, endpoint.Host)
 
132
        endpoint.RawQuery = multimap(params).Encode()
 
133
        if debug {
 
134
                log.Printf("get { %v } -> {\n", endpoint.String())
 
135
        }
 
136
        r, err := http.Get(endpoint.String())
 
137
        if err != nil {
 
138
                return err
 
139
        }
 
140
        defer r.Body.Close()
 
141
 
 
142
        if debug {
 
143
                dump, _ := httputil.DumpResponse(r, true)
 
144
                log.Printf("response:\n")
 
145
                log.Printf("%v\n}\n", string(dump))
 
146
        }
 
147
        if r.StatusCode != 200 {
 
148
                return buildError(r)
 
149
        }
 
150
        err = xml.NewDecoder(r.Body).Decode(resp)
 
151
        return err
 
152
}
 
153
 
 
154
func multimap(p map[string]string) url.Values {
 
155
        q := make(url.Values, len(p))
 
156
        for k, v := range p {
 
157
                q[k] = []string{v}
 
158
        }
 
159
        return q
 
160
}
 
161
 
 
162
func buildError(r *http.Response) error {
 
163
        errors := xmlErrors{}
 
164
        xml.NewDecoder(r.Body).Decode(&errors)
 
165
        var err Error
 
166
        if len(errors.Errors) > 0 {
 
167
                err = errors.Errors[0]
 
168
        }
 
169
        err.RequestId = errors.RequestId
 
170
        err.StatusCode = r.StatusCode
 
171
        if err.Message == "" {
 
172
                err.Message = r.Status
 
173
        }
 
174
        return &err
 
175
}
 
176
 
 
177
func makeParams(action string) map[string]string {
 
178
        params := make(map[string]string)
 
179
        params["Action"] = action
 
180
        return params
 
181
}
 
182
 
 
183
func addParamsList(params map[string]string, label string, ids []string) {
 
184
        for i, id := range ids {
 
185
                params[label+"."+strconv.Itoa(i+1)] = id
 
186
        }
 
187
}
 
188
 
 
189
// ----------------------------------------------------------------------------
 
190
// Instance management functions and types.
 
191
 
 
192
// The RunInstances type encapsulates options for the respective request in EC2.
 
193
//
 
194
// See http://goo.gl/Mcm3b for more details.
 
195
type RunInstances struct {
 
196
        ImageId               string
 
197
        MinCount              int
 
198
        MaxCount              int
 
199
        KeyName               string
 
200
        InstanceType          string
 
201
        SecurityGroups        []SecurityGroup
 
202
        KernelId              string
 
203
        RamdiskId             string
 
204
        UserData              []byte
 
205
        AvailZone             string
 
206
        PlacementGroupName    string
 
207
        Monitoring            bool
 
208
        SubnetId              string
 
209
        DisableAPITermination bool
 
210
        ShutdownBehavior      string
 
211
        PrivateIPAddress      string
 
212
}
 
213
 
 
214
// Response to a RunInstances request.
 
215
//
 
216
// See http://goo.gl/Mcm3b for more details.
 
217
type RunInstancesResp struct {
 
218
        RequestId      string          `xml:"requestId"`
 
219
        ReservationId  string          `xml:"reservationId"`
 
220
        OwnerId        string          `xml:"ownerId"`
 
221
        SecurityGroups []SecurityGroup `xml:"groupSet>item"`
 
222
        Instances      []Instance      `xml:"instancesSet>item"`
 
223
}
 
224
 
 
225
// Instance encapsulates a running instance in EC2.
 
226
//
 
227
// See http://goo.gl/OCH8a for more details.
 
228
type Instance struct {
 
229
        InstanceId         string        `xml:"instanceId"`
 
230
        InstanceType       string        `xml:"instanceType"`
 
231
        ImageId            string        `xml:"imageId"`
 
232
        PrivateDNSName     string        `xml:"privateDnsName"`
 
233
        DNSName            string        `xml:"dnsName"`
 
234
        KeyName            string        `xml:"keyName"`
 
235
        AMILaunchIndex     int           `xml:"amiLaunchIndex"`
 
236
        Hypervisor         string        `xml:"hypervisor"`
 
237
        VirtType           string        `xml:"virtualizationType"`
 
238
        Monitoring         string        `xml:"monitoring>state"`
 
239
        AvailZone          string        `xml:"placement>availabilityZone"`
 
240
        PlacementGroupName string        `xml:"placement>groupName"`
 
241
        State              InstanceState `xml:"instanceState"`
 
242
        Tags               []Tag         `xml:"tagSet>item"`
 
243
}
 
244
 
 
245
// RunInstances starts new instances in EC2.
 
246
// If options.MinCount and options.MaxCount are both zero, a single instance
 
247
// will be started; otherwise if options.MaxCount is zero, options.MinCount
 
248
// will be used insteead.
 
249
//
 
250
// See http://goo.gl/Mcm3b for more details.
 
251
func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err error) {
 
252
        params := makeParams("RunInstances")
 
253
        params["ImageId"] = options.ImageId
 
254
        params["InstanceType"] = options.InstanceType
 
255
        var min, max int
 
256
        if options.MinCount == 0 && options.MaxCount == 0 {
 
257
                min = 1
 
258
                max = 1
 
259
        } else if options.MaxCount == 0 {
 
260
                min = options.MinCount
 
261
                max = min
 
262
        } else {
 
263
                min = options.MinCount
 
264
                max = options.MaxCount
 
265
        }
 
266
        params["MinCount"] = strconv.Itoa(min)
 
267
        params["MaxCount"] = strconv.Itoa(max)
 
268
        i, j := 1, 1
 
269
        for _, g := range options.SecurityGroups {
 
270
                if g.Id != "" {
 
271
                        params["SecurityGroupId."+strconv.Itoa(i)] = g.Id
 
272
                        i++
 
273
                } else {
 
274
                        params["SecurityGroup."+strconv.Itoa(j)] = g.Name
 
275
                        j++
 
276
                }
 
277
        }
 
278
        token, err := clientToken()
 
279
        if err != nil {
 
280
                return nil, err
 
281
        }
 
282
        params["ClientToken"] = token
 
283
 
 
284
        if options.KeyName != "" {
 
285
                params["KeyName"] = options.KeyName
 
286
        }
 
287
        if options.KernelId != "" {
 
288
                params["KernelId"] = options.KernelId
 
289
        }
 
290
        if options.RamdiskId != "" {
 
291
                params["RamdiskId"] = options.RamdiskId
 
292
        }
 
293
        if options.UserData != nil {
 
294
                userData := make([]byte, b64.EncodedLen(len(options.UserData)))
 
295
                b64.Encode(userData, options.UserData)
 
296
                params["UserData"] = string(userData)
 
297
        }
 
298
        if options.AvailZone != "" {
 
299
                params["Placement.AvailabilityZone"] = options.AvailZone
 
300
        }
 
301
        if options.PlacementGroupName != "" {
 
302
                params["Placement.GroupName"] = options.PlacementGroupName
 
303
        }
 
304
        if options.Monitoring {
 
305
                params["Monitoring.Enabled"] = "true"
 
306
        }
 
307
        if options.SubnetId != "" {
 
308
                params["SubnetId"] = options.SubnetId
 
309
        }
 
310
        if options.DisableAPITermination {
 
311
                params["DisableApiTermination"] = "true"
 
312
        }
 
313
        if options.ShutdownBehavior != "" {
 
314
                params["InstanceInitiatedShutdownBehavior"] = options.ShutdownBehavior
 
315
        }
 
316
        if options.PrivateIPAddress != "" {
 
317
                params["PrivateIpAddress"] = options.PrivateIPAddress
 
318
        }
 
319
 
 
320
        resp = &RunInstancesResp{}
 
321
        err = ec2.query(params, resp)
 
322
        if err != nil {
 
323
                return nil, err
 
324
        }
 
325
        return
 
326
}
 
327
 
 
328
func clientToken() (string, error) {
 
329
        // Maximum EC2 client token size is 64 bytes.
 
330
        // Each byte expands to two when hex encoded.
 
331
        buf := make([]byte, 32)
 
332
        _, err := rand.Read(buf)
 
333
        if err != nil {
 
334
                return "", err
 
335
        }
 
336
        return hex.EncodeToString(buf), nil
 
337
}
 
338
 
 
339
// Response to a TerminateInstances request.
 
340
//
 
341
// See http://goo.gl/3BKHj for more details.
 
342
type TerminateInstancesResp struct {
 
343
        RequestId    string                `xml:"requestId"`
 
344
        StateChanges []InstanceStateChange `xml:"instancesSet>item"`
 
345
}
 
346
 
 
347
// InstanceState encapsulates the state of an instance in EC2.
 
348
//
 
349
// See http://goo.gl/y3ZBq for more details.
 
350
type InstanceState struct {
 
351
        Code int    `xml:"code"` // Watch out, bits 15-8 have unpublished meaning.
 
352
        Name string `xml:"name"`
 
353
}
 
354
 
 
355
// InstanceStateChange informs of the previous and current states
 
356
// for an instance when a state change is requested.
 
357
type InstanceStateChange struct {
 
358
        InstanceId    string        `xml:"instanceId"`
 
359
        CurrentState  InstanceState `xml:"currentState"`
 
360
        PreviousState InstanceState `xml:"previousState"`
 
361
}
 
362
 
 
363
// TerminateInstances requests the termination of instances when the given ids.
 
364
//
 
365
// See http://goo.gl/3BKHj for more details.
 
366
func (ec2 *EC2) TerminateInstances(instIds []string) (resp *TerminateInstancesResp, err error) {
 
367
        params := makeParams("TerminateInstances")
 
368
        addParamsList(params, "InstanceId", instIds)
 
369
        resp = &TerminateInstancesResp{}
 
370
        err = ec2.query(params, resp)
 
371
        if err != nil {
 
372
                return nil, err
 
373
        }
 
374
        return
 
375
}
 
376
 
 
377
// Response to a DescribeInstances request.
 
378
//
 
379
// See http://goo.gl/mLbmw for more details.
 
380
type InstancesResp struct {
 
381
        RequestId    string        `xml:"requestId"`
 
382
        Reservations []Reservation `xml:"reservationSet>item"`
 
383
}
 
384
 
 
385
// Reservation represents details about a reservation in EC2.
 
386
//
 
387
// See http://goo.gl/0ItPT for more details.
 
388
type Reservation struct {
 
389
        ReservationId  string          `xml:"reservationId"`
 
390
        OwnerId        string          `xml:"ownerId"`
 
391
        RequesterId    string          `xml:"requesterId"`
 
392
        SecurityGroups []SecurityGroup `xml:"groupSet>item"`
 
393
        Instances      []Instance      `xml:"instancesSet>item"`
 
394
}
 
395
 
 
396
// Instances returns details about instances in EC2.  Both parameters
 
397
// are optional, and if provided will limit the instances returned to those
 
398
// matching the given instance ids or filtering rules.
 
399
//
 
400
// See http://goo.gl/4No7c for more details.
 
401
func (ec2 *EC2) Instances(instIds []string, filter *Filter) (resp *InstancesResp, err error) {
 
402
        params := makeParams("DescribeInstances")
 
403
        addParamsList(params, "InstanceId", instIds)
 
404
        filter.addParams(params)
 
405
        resp = &InstancesResp{}
 
406
        err = ec2.query(params, resp)
 
407
        if err != nil {
 
408
                return nil, err
 
409
        }
 
410
        return
 
411
}
 
412
 
 
413
// ----------------------------------------------------------------------------
 
414
// Image and snapshot management functions and types.
 
415
 
 
416
// Response to a DescribeImages request.
 
417
//
 
418
// See http://goo.gl/hLnyg for more details.
 
419
type ImagesResp struct {
 
420
        RequestId string  `xml:"requestId"`
 
421
        Images    []Image `xml:"imagesSet>item"`
 
422
}
 
423
 
 
424
// BlockDeviceMapping represents the association of a block device with an image.
 
425
//
 
426
// See http://goo.gl/wnDBf for more details.
 
427
type BlockDeviceMapping struct {
 
428
        DeviceName          string `xml:"deviceName"`
 
429
        VirtualName         string `xml:"virtualName"`
 
430
        SnapshotId          string `xml:"ebs>snapshotId"`
 
431
        VolumeType          string `xml:"ebs>volumeType"`
 
432
        VolumeSize          int64  `xml:"ebs>volumeSize"`
 
433
        DeleteOnTermination bool   `xml:"ebs>deleteOnTermination"`
 
434
 
 
435
        // The number of I/O operations per second (IOPS) that the volume supports.
 
436
        IOPS int64 `xml:"ebs>iops"`
 
437
}
 
438
 
 
439
// Image represents details about an image.
 
440
//
 
441
// See http://goo.gl/iSqJG for more details.
 
442
type Image struct {
 
443
        Id                 string               `xml:"imageId"`
 
444
        Name               string               `xml:"name"`
 
445
        Description        string               `xml:"description"`
 
446
        Type               string               `xml:"imageType"`
 
447
        State              string               `xml:"imageState"`
 
448
        Location           string               `xml:"imageLocation"`
 
449
        Public             bool                 `xml:"isPublic"`
 
450
        Architecture       string               `xml:"architecture"`
 
451
        Platform           string               `xml:"platform"`
 
452
        ProductCodes       []string             `xml:"productCode>item>productCode"`
 
453
        KernelId           string               `xml:"kernelId"`
 
454
        RamdiskId          string               `xml:"ramdiskId"`
 
455
        StateReason        string               `xml:"stateReason"`
 
456
        OwnerId            string               `xml:"imageOwnerId"`
 
457
        OwnerAlias         string               `xml:"imageOwnerAlias"`
 
458
        RootDeviceType     string               `xml:"rootDeviceType"`
 
459
        RootDeviceName     string               `xml:"rootDeviceName"`
 
460
        VirtualizationType string               `xml:"virtualizationType"`
 
461
        Hypervisor         string               `xml:"hypervisor"`
 
462
        BlockDevices       []BlockDeviceMapping `xml:"blockDeviceMapping>item"`
 
463
}
 
464
 
 
465
// Images returns details about available images.
 
466
// The ids and filter parameters, if provided, will limit the images returned.
 
467
// For example, to get all the private images associated with this account set
 
468
// the boolean filter "is-private" to true.
 
469
//
 
470
// Note: calling this function with nil ids and filter parameters will result in
 
471
// a very large number of images being returned.
 
472
//
 
473
// See http://goo.gl/SRBhW for more details.
 
474
func (ec2 *EC2) Images(ids []string, filter *Filter) (resp *ImagesResp, err error) {
 
475
        params := makeParams("DescribeImages")
 
476
        for i, id := range ids {
 
477
                params["ImageId."+strconv.Itoa(i+1)] = id
 
478
        }
 
479
        filter.addParams(params)
 
480
 
 
481
        resp = &ImagesResp{}
 
482
        err = ec2.query(params, resp)
 
483
        if err != nil {
 
484
                return nil, err
 
485
        }
 
486
        return
 
487
}
 
488
 
 
489
// Response to a CreateSnapshot request.
 
490
//
 
491
// See http://goo.gl/ttcda for more details.
 
492
type CreateSnapshotResp struct {
 
493
        RequestId string `xml:"requestId"`
 
494
        Snapshot
 
495
}
 
496
 
 
497
// CreateSnapshot creates a volume snapshot and stores it in S3.
 
498
//
 
499
// See http://goo.gl/ttcda for more details.
 
500
func (ec2 *EC2) CreateSnapshot(volumeId, description string) (resp *CreateSnapshotResp, err error) {
 
501
        params := makeParams("CreateSnapshot")
 
502
        params["VolumeId"] = volumeId
 
503
        params["Description"] = description
 
504
 
 
505
        resp = &CreateSnapshotResp{}
 
506
        err = ec2.query(params, resp)
 
507
        if err != nil {
 
508
                return nil, err
 
509
        }
 
510
        return
 
511
}
 
512
 
 
513
// DeleteSnapshots deletes the volume snapshots with the given ids.
 
514
//
 
515
// Note: If you make periodic snapshots of a volume, the snapshots are
 
516
// incremental so that only the blocks on the device that have changed
 
517
// since your last snapshot are incrementally saved in the new snapshot.
 
518
// Even though snapshots are saved incrementally, the snapshot deletion
 
519
// process is designed so that you need to retain only the most recent
 
520
// snapshot in order to restore the volume.
 
521
//
 
522
// See http://goo.gl/vwU1y for more details.
 
523
func (ec2 *EC2) DeleteSnapshots(ids []string) (resp *SimpleResp, err error) {
 
524
        params := makeParams("DeleteSnapshot")
 
525
        for i, id := range ids {
 
526
                params["SnapshotId."+strconv.Itoa(i+1)] = id
 
527
        }
 
528
 
 
529
        resp = &SimpleResp{}
 
530
        err = ec2.query(params, resp)
 
531
        if err != nil {
 
532
                return nil, err
 
533
        }
 
534
        return
 
535
}
 
536
 
 
537
// Response to a DescribeSnapshots request.
 
538
//
 
539
// See http://goo.gl/nClDT for more details.
 
540
type SnapshotsResp struct {
 
541
        RequestId string     `xml:"requestId"`
 
542
        Snapshots []Snapshot `xml:"snapshotSet>item"`
 
543
}
 
544
 
 
545
// Snapshot represents details about a volume snapshot.
 
546
//
 
547
// See http://goo.gl/nkovs for more details.
 
548
type Snapshot struct {
 
549
        Id          string `xml:"snapshotId"`
 
550
        VolumeId    string `xml:"volumeId"`
 
551
        VolumeSize  string `xml:"volumeSize"`
 
552
        Status      string `xml:"status"`
 
553
        StartTime   string `xml:"startTime"`
 
554
        Description string `xml:"description"`
 
555
        Progress    string `xml:"progress"`
 
556
        OwnerId     string `xml:"ownerId"`
 
557
        OwnerAlias  string `xml:"ownerAlias"`
 
558
        Tags        []Tag  `xml:"tagSet>item"`
 
559
}
 
560
 
 
561
// Snapshots returns details about volume snapshots available to the user.
 
562
// The ids and filter parameters, if provided, limit the snapshots returned.
 
563
//
 
564
// See http://goo.gl/ogJL4 for more details.
 
565
func (ec2 *EC2) Snapshots(ids []string, filter *Filter) (resp *SnapshotsResp, err error) {
 
566
        params := makeParams("DescribeSnapshots")
 
567
        for i, id := range ids {
 
568
                params["SnapshotId."+strconv.Itoa(i+1)] = id
 
569
        }
 
570
        filter.addParams(params)
 
571
 
 
572
        resp = &SnapshotsResp{}
 
573
        err = ec2.query(params, resp)
 
574
        if err != nil {
 
575
                return nil, err
 
576
        }
 
577
        return
 
578
}
 
579
 
 
580
// ----------------------------------------------------------------------------
 
581
// Security group management functions and types.
 
582
 
 
583
// SimpleResp represents a response to an EC2 request which on success will
 
584
// return no other information besides a request id.
 
585
type SimpleResp struct {
 
586
        XMLName   xml.Name
 
587
        RequestId string `xml:"requestId"`
 
588
}
 
589
 
 
590
// CreateSecurityGroupResp represents a response to a CreateSecurityGroup request.
 
591
type CreateSecurityGroupResp struct {
 
592
        SecurityGroup
 
593
        RequestId string `xml:"requestId"`
 
594
}
 
595
 
 
596
// CreateSecurityGroup run a CreateSecurityGroup request in EC2, with the provided
 
597
// name and description.
 
598
//
 
599
// See http://goo.gl/Eo7Yl for more details.
 
600
func (ec2 *EC2) CreateSecurityGroup(name, description string) (resp *CreateSecurityGroupResp, err error) {
 
601
        params := makeParams("CreateSecurityGroup")
 
602
        params["GroupName"] = name
 
603
        params["GroupDescription"] = description
 
604
 
 
605
        resp = &CreateSecurityGroupResp{}
 
606
        err = ec2.query(params, resp)
 
607
        if err != nil {
 
608
                return nil, err
 
609
        }
 
610
        resp.Name = name
 
611
        return resp, nil
 
612
}
 
613
 
 
614
// SecurityGroupsResp represents a response to a DescribeSecurityGroups
 
615
// request in EC2.
 
616
//
 
617
// See http://goo.gl/k12Uy for more details.
 
618
type SecurityGroupsResp struct {
 
619
        RequestId string              `xml:"requestId"`
 
620
        Groups    []SecurityGroupInfo `xml:"securityGroupInfo>item"`
 
621
}
 
622
 
 
623
// SecurityGroup encapsulates details for a security group in EC2.
 
624
//
 
625
// See http://goo.gl/CIdyP for more details.
 
626
type SecurityGroupInfo struct {
 
627
        SecurityGroup
 
628
        OwnerId     string   `xml:"ownerId"`
 
629
        Description string   `xml:"groupDescription"`
 
630
        IPPerms     []IPPerm `xml:"ipPermissions>item"`
 
631
}
 
632
 
 
633
// IPPerm represents an allowance within an EC2 security group.
 
634
//
 
635
// See http://goo.gl/4oTxv for more details.
 
636
type IPPerm struct {
 
637
        Protocol     string              `xml:"ipProtocol"`
 
638
        FromPort     int                 `xml:"fromPort"`
 
639
        ToPort       int                 `xml:"toPort"`
 
640
        SourceIPs    []string            `xml:"ipRanges>item>cidrIp"`
 
641
        SourceGroups []UserSecurityGroup `xml:"groups>item"`
 
642
}
 
643
 
 
644
// UserSecurityGroup holds a security group and the owner
 
645
// of that group.
 
646
type UserSecurityGroup struct {
 
647
        Id      string `xml:"groupId"`
 
648
        Name    string `xml:"groupName"`
 
649
        OwnerId string `xml:"userId"`
 
650
}
 
651
 
 
652
// SecurityGroup represents an EC2 security group.
 
653
// If SecurityGroup is used as a parameter, then one of Id or Name
 
654
// may be empty. If both are set, then Id is used.
 
655
type SecurityGroup struct {
 
656
        Id   string `xml:"groupId"`
 
657
        Name string `xml:"groupName"`
 
658
}
 
659
 
 
660
// SecurityGroupNames is a convenience function that
 
661
// returns a slice of security groups with the given names.
 
662
func SecurityGroupNames(names ...string) []SecurityGroup {
 
663
        g := make([]SecurityGroup, len(names))
 
664
        for i, name := range names {
 
665
                g[i] = SecurityGroup{Name: name}
 
666
        }
 
667
        return g
 
668
}
 
669
 
 
670
// SecurityGroupNames is a convenience function that
 
671
// returns a slice of security groups with the given ids.
 
672
func SecurityGroupIds(ids ...string) []SecurityGroup {
 
673
        g := make([]SecurityGroup, len(ids))
 
674
        for i, id := range ids {
 
675
                g[i] = SecurityGroup{Id: id}
 
676
        }
 
677
        return g
 
678
}
 
679
 
 
680
// SecurityGroups returns details about security groups in EC2.  Both parameters
 
681
// are optional, and if provided will limit the security groups returned to those
 
682
// matching the given groups or filtering rules.
 
683
//
 
684
// See http://goo.gl/k12Uy for more details.
 
685
func (ec2 *EC2) SecurityGroups(groups []SecurityGroup, filter *Filter) (resp *SecurityGroupsResp, err error) {
 
686
        params := makeParams("DescribeSecurityGroups")
 
687
        i, j := 1, 1
 
688
        for _, g := range groups {
 
689
                if g.Id != "" {
 
690
                        params["GroupId."+strconv.Itoa(i)] = g.Id
 
691
                        i++
 
692
                } else {
 
693
                        params["GroupName."+strconv.Itoa(j)] = g.Name
 
694
                        j++
 
695
                }
 
696
        }
 
697
        filter.addParams(params)
 
698
 
 
699
        resp = &SecurityGroupsResp{}
 
700
        err = ec2.query(params, resp)
 
701
        if err != nil {
 
702
                return nil, err
 
703
        }
 
704
        return resp, nil
 
705
}
 
706
 
 
707
// DeleteSecurityGroup removes the given security group in EC2.
 
708
//
 
709
// See http://goo.gl/QJJDO for more details.
 
710
func (ec2 *EC2) DeleteSecurityGroup(group SecurityGroup) (resp *SimpleResp, err error) {
 
711
        params := makeParams("DeleteSecurityGroup")
 
712
        if group.Id != "" {
 
713
                params["GroupId"] = group.Id
 
714
        } else {
 
715
                params["GroupName"] = group.Name
 
716
        }
 
717
 
 
718
        resp = &SimpleResp{}
 
719
        err = ec2.query(params, resp)
 
720
        if err != nil {
 
721
                return nil, err
 
722
        }
 
723
        return resp, nil
 
724
}
 
725
 
 
726
// AuthorizeSecurityGroup creates an allowance for clients matching the provided
 
727
// rules to access instances within the given security group.
 
728
//
 
729
// See http://goo.gl/u2sDJ for more details.
 
730
func (ec2 *EC2) AuthorizeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
 
731
        return ec2.authOrRevoke("AuthorizeSecurityGroupIngress", group, perms)
 
732
}
 
733
 
 
734
// RevokeSecurityGroup revokes permissions from a group.
 
735
//
 
736
// See http://goo.gl/ZgdxA for more details.
 
737
func (ec2 *EC2) RevokeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
 
738
        return ec2.authOrRevoke("RevokeSecurityGroupIngress", group, perms)
 
739
}
 
740
 
 
741
func (ec2 *EC2) authOrRevoke(op string, group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
 
742
        params := makeParams(op)
 
743
        if group.Id != "" {
 
744
                params["GroupId"] = group.Id
 
745
        } else {
 
746
                params["GroupName"] = group.Name
 
747
        }
 
748
 
 
749
        for i, perm := range perms {
 
750
                prefix := "IpPermissions." + strconv.Itoa(i+1)
 
751
                params[prefix+".IpProtocol"] = perm.Protocol
 
752
                params[prefix+".FromPort"] = strconv.Itoa(perm.FromPort)
 
753
                params[prefix+".ToPort"] = strconv.Itoa(perm.ToPort)
 
754
                for j, ip := range perm.SourceIPs {
 
755
                        params[prefix+".IpRanges."+strconv.Itoa(j+1)+".CidrIp"] = ip
 
756
                }
 
757
                for j, g := range perm.SourceGroups {
 
758
                        subprefix := prefix + ".Groups." + strconv.Itoa(j+1)
 
759
                        if g.OwnerId != "" {
 
760
                                params[subprefix+".UserId"] = g.OwnerId
 
761
                        }
 
762
                        if g.Id != "" {
 
763
                                params[subprefix+".GroupId"] = g.Id
 
764
                        } else {
 
765
                                params[subprefix+".GroupName"] = g.Name
 
766
                        }
 
767
                }
 
768
        }
 
769
 
 
770
        resp = &SimpleResp{}
 
771
        err = ec2.query(params, resp)
 
772
        if err != nil {
 
773
                return nil, err
 
774
        }
 
775
        return resp, nil
 
776
}
 
777
 
 
778
// ResourceTag represents key-value metadata used to classify and organize
 
779
// EC2 instances.
 
780
//
 
781
// See http://goo.gl/bncl3 for more details
 
782
type Tag struct {
 
783
        Key   string `xml:"key"`
 
784
        Value string `xml:"value"`
 
785
}
 
786
 
 
787
// CreateTags adds or overwrites one or more tags for the specified instance ids.
 
788
//
 
789
// See http://goo.gl/Vmkqc for more details
 
790
func (ec2 *EC2) CreateTags(instIds []string, tags []Tag) (resp *SimpleResp, err error) {
 
791
        params := makeParams("CreateTags")
 
792
        addParamsList(params, "ResourceId", instIds)
 
793
 
 
794
        for j, tag := range tags {
 
795
                params["Tag."+strconv.Itoa(j+1)+".Key"] = tag.Key
 
796
                params["Tag."+strconv.Itoa(j+1)+".Value"] = tag.Value
 
797
        }
 
798
 
 
799
        resp = &SimpleResp{}
 
800
        err = ec2.query(params, resp)
 
801
        if err != nil {
 
802
                return nil, err
 
803
        }
 
804
        return resp, nil
 
805
}
 
806
 
 
807
// Response to a StartInstances request.
 
808
//
 
809
// See http://goo.gl/awKeF for more details.
 
810
type StartInstanceResp struct {
 
811
        RequestId    string                `xml:"requestId"`
 
812
        StateChanges []InstanceStateChange `xml:"instancesSet>item"`
 
813
}
 
814
 
 
815
// Response to a StopInstances request.
 
816
//
 
817
// See http://goo.gl/436dJ for more details.
 
818
type StopInstanceResp struct {
 
819
        RequestId    string                `xml:"requestId"`
 
820
        StateChanges []InstanceStateChange `xml:"instancesSet>item"`
 
821
}
 
822
 
 
823
// StartInstances starts an Amazon EBS-backed AMI that you've previously stopped.
 
824
//
 
825
// See http://goo.gl/awKeF for more details.
 
826
func (ec2 *EC2) StartInstances(ids ...string) (resp *StartInstanceResp, err error) {
 
827
        params := makeParams("StartInstances")
 
828
        addParamsList(params, "InstanceId", ids)
 
829
        resp = &StartInstanceResp{}
 
830
        err = ec2.query(params, resp)
 
831
        if err != nil {
 
832
                return nil, err
 
833
        }
 
834
        return resp, nil
 
835
}
 
836
 
 
837
// StopInstances requests stopping one or more Amazon EBS-backed instances.
 
838
//
 
839
// See http://goo.gl/436dJ for more details.
 
840
func (ec2 *EC2) StopInstances(ids ...string) (resp *StopInstanceResp, err error) {
 
841
        params := makeParams("StopInstances")
 
842
        addParamsList(params, "InstanceId", ids)
 
843
        resp = &StopInstanceResp{}
 
844
        err = ec2.query(params, resp)
 
845
        if err != nil {
 
846
                return nil, err
 
847
        }
 
848
        return resp, nil
 
849
}
 
850
 
 
851
// RebootInstance requests a reboot of one or more instances. This operation is asynchronous;
 
852
// it only queues a request to reboot the specified instance(s). The operation will succeed
 
853
// if the instances are valid and belong to you.
 
854
//
 
855
// Requests to reboot terminated instances are ignored.
 
856
//
 
857
// See http://goo.gl/baoUf for more details.
 
858
func (ec2 *EC2) RebootInstances(ids ...string) (resp *SimpleResp, err error) {
 
859
        params := makeParams("RebootInstances")
 
860
        addParamsList(params, "InstanceId", ids)
 
861
        resp = &SimpleResp{}
 
862
        err = ec2.query(params, resp)
 
863
        if err != nil {
 
864
                return nil, err
 
865
        }
 
866
        return resp, nil
 
867
}