~prudhvikrishna/sqs/trunk

« back to all changes in this revision

Viewing changes to sqs.go

  • Committer: Prudhvi Krishna Surapaneni
  • Date: 2012-05-24 21:06:40 UTC
  • Revision ID: me@prudhvi.net-20120524210640-rt0mcxlp42v5kwze
Import SQS

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) 2012 Memeo Inc.
 
7
//
 
8
// Written by Prudhvi Krishna Surapaneni <me@prudhvi.net>
 
9
//
 
10
package sqs
 
11
 
 
12
import (
 
13
        "encoding/xml"
 
14
        "fmt"
 
15
        "launchpad.net/goamz/aws"
 
16
        "log"
 
17
        "net/http"
 
18
        "net/http/httputil"
 
19
        "net/url"
 
20
        "strconv"
 
21
        "time"
 
22
)
 
23
 
 
24
// The SQS type encapsulates operations with specific SQS region
 
25
type SQS struct {
 
26
        aws.Auth
 
27
        aws.Region
 
28
        private byte // Reserve the right of using private data.
 
29
}
 
30
 
 
31
const debug = false
 
32
 
 
33
// New creates a new SQS handle
 
34
func New(auth aws.Auth, region aws.Region) *SQS {
 
35
        return &SQS{auth, region, 0}
 
36
}
 
37
 
 
38
// Queue type encapsulates operations on a SQS Queue
 
39
type Queue struct {
 
40
        *SQS
 
41
        Url string
 
42
}
 
43
 
 
44
// Response to a CreateQueue request.
 
45
//
 
46
// See http://goo.gl/sVUjF for more details
 
47
type CreateQueueResponse struct {
 
48
        QueueUrl string `xml:"CreateQueueResult>QueueUrl"`
 
49
        ResponseMetadata
 
50
}
 
51
 
 
52
// Response to a ListQueues request.
 
53
//
 
54
// See http://goo.gl/RPRWr for more details
 
55
type ListQueuesResponse struct {
 
56
        QueueUrl []string `xml:"ListQueuesResult>QueueUrl"`
 
57
        ResponseMetadata
 
58
}
 
59
 
 
60
// Response to a GetQueueUrl request.
 
61
//
 
62
// See http://goo.gl/hk7Iu for more details
 
63
type GetQueueUrlResponse struct {
 
64
        QueueUrl string `xml:"GetQueueUrlResult>QueueUrl"`
 
65
        ResponseMetadata
 
66
}
 
67
 
 
68
// Response to a ChangeMessageVisibility request.
 
69
//
 
70
// See http://goo.gl/EyJKF for more details
 
71
type ChangeMessageVisibilityResponse struct {
 
72
        ResponseMetadata
 
73
}
 
74
 
 
75
// See http://goo.gl/XTo0s for more details 
 
76
type ResponseMetadata struct {
 
77
        RequestId string  `xml:"ResponseMetadata>RequestId"`
 
78
}
 
79
 
 
80
// Error represents an error in an operation with SQS
 
81
type Error struct {
 
82
        StatusCode int    // HTTP Status Code (200, 403, ... )
 
83
        Code       string // SQS Error Code
 
84
        Message    string // The human-oriented error message
 
85
        RequestId  string `xml:"RequestID"`
 
86
}
 
87
 
 
88
func (err *Error) Error() string {
 
89
        if err.Code != "" {
 
90
                return err.Message
 
91
        }
 
92
 
 
93
        return fmt.Sprintf("%s (%s)", err.Message, err.Code)
 
94
}
 
95
 
 
96
// For now a single error inst is being exposed. In the future it may be useful
 
97
// to provide access to all of them, but rather than doing it as an array/slice,
 
98
// use a *next pointer, so that it's backward compatible and it continues to be
 
99
// easy to handle the first error, which is what most people will want.
 
100
type xmlErrors struct {
 
101
        RequestId string
 
102
        Errors    []Error `xml:"Errors>Error"`
 
103
}
 
104
 
 
105
// Attribute represents an instance of a SQS Queue Attribute.
 
106
type Attribute struct {
 
107
        Name  string
 
108
        Value string
 
109
}
 
110
 
 
111
// See http://goo.gl/pgffn for more details.
 
112
type ChangeMessageVisibilityBatchEntry struct {
 
113
        Id                string
 
114
        ReceiptHandle     string
 
115
        VisibilityTimeout int
 
116
}
 
117
 
 
118
// ReceiveMessageResponse holds the results of ReceiveMessage
 
119
type ReceiveMessageResponse struct {
 
120
        Messages []Message `xml:"ReceiveMessageResult>Message"`
 
121
        ResponseMetadata
 
122
}
 
123
 
 
124
type DeleteMessageResponse struct {
 
125
        ResponseMetadata
 
126
}
 
127
 
 
128
type SendMessageResponse struct {
 
129
        SendMessageResult
 
130
        ResponseMetadata
 
131
}
 
132
 
 
133
type SendMessageBatchResponse struct {
 
134
        SendMessageBatchResult
 
135
        ResponseMetadata
 
136
}
 
137
 
 
138
// SendMessageBatchResult holds the results of SendMessageBatch
 
139
type SendMessageBatchResult struct {
 
140
        Entries []SendMessageBatchResultEntry `xml:"SendMessageBatchResult>SendMessageBatchResultEntry"`
 
141
}
 
142
 
 
143
type SendMessageBatchResultEntry struct {
 
144
        MD5OfMessageBody string `xml:"MD5OfMessageBody"`
 
145
        MessageId        string `xml:"MessageId"`
 
146
        Id               string `xml:"Id"`
 
147
}
 
148
 
 
149
type SendMessageBatchRequestEntry struct {
 
150
        Id           string
 
151
        MessageBody  string
 
152
        DelaySeconds int
 
153
}
 
154
 
 
155
type SendMessageResult struct {
 
156
        MD5OfMessageBody string `xml:"SendMessageResult>MD5OfMessageBody"`
 
157
        MessageId        string `xml:"SendMessageResult>MessageId"`
 
158
}
 
159
 
 
160
// Represents an instance of a SQS Message
 
161
type Message struct {
 
162
        MessageId     string      `xml:"MessageId"`
 
163
        Body          string      `xml:"Body"`
 
164
        MD5OfBody     string      `xml:"MD5OfBody"`
 
165
        ReceiptHandle string      `xml:"ReceiptHandle"`
 
166
        Attribute     []Attribute `xml:"Attribute"`
 
167
}
 
168
 
 
169
type ChangeMessageVisibilityBatchResponse struct {
 
170
        Id []string `xml:"ChangeMessageVisibilityBatchResult>ChangeMessageVisibilityBatchResultEntry>Id"`
 
171
        ResponseMetadata
 
172
}
 
173
 
 
174
type GetQueueAttributesResponse struct {
 
175
        Attributes []Attribute `xml:"GetQueueAttributesResult>Attribute"`
 
176
        ResponseMetadata
 
177
}
 
178
 
 
179
type DeleteMessageBatchResult struct {
 
180
        Ids []string `xml:"DeleteMessageBatchResult>DeleteMessageBatchResultEntry>Id"`
 
181
}
 
182
 
 
183
type DeleteMessageBatchResponse struct {
 
184
        DeleteMessageBatchResult `xml:"DeleteMessageBatchResponse>DeleteMessageBatchResult"`
 
185
        ResponseMetadata
 
186
}
 
187
 
 
188
type DeleteMessageBatch struct {
 
189
        Id            string
 
190
        ReceiptHandle string
 
191
}
 
192
 
 
193
type AccountPermission struct {
 
194
        AWSAccountId string
 
195
        ActionName   string
 
196
}
 
197
 
 
198
type AddPermissionResponse struct {
 
199
        ResponseMetadata
 
200
}
 
201
 
 
202
type RemovePermissionResponse struct {
 
203
        ResponseMetadata
 
204
}
 
205
 
 
206
type SetQueueAttributesResponse struct {
 
207
        ResponseMetadata
 
208
}
 
209
 
 
210
type DeleteQueueResponse struct {
 
211
        ResponseMetadata
 
212
}
 
213
 
 
214
// CreateQueue action creates a new queue.
 
215
//
 
216
// See http://goo.gl/sVUjF for more details
 
217
func (s *SQS) CreateQueue(name string, attributes []Attribute) (queue *Queue, err error) {
 
218
        resp := &CreateQueueResponse{}
 
219
        params := makeParams("CreateQueue")
 
220
        queue = nil
 
221
 
 
222
        for i, attribute := range attributes {
 
223
                params["Attribute."+strconv.Itoa(i+1)+".Name"] = attribute.Name
 
224
                params["Attribute."+strconv.Itoa(i+1)+".Value"] = attribute.Value
 
225
        }
 
226
 
 
227
        params["QueueName"] = name
 
228
        err = s.query("", params, resp)
 
229
        if err != nil {
 
230
                return nil, err
 
231
        }
 
232
        queue = &Queue{s, resp.QueueUrl}
 
233
        return queue, err
 
234
}
 
235
 
 
236
// AddPermission action adds a permission to a queue for a specific principal.
 
237
//
 
238
// See http://goo.gl/8WBp8 for more details
 
239
func (q *Queue) AddPermission(label string, accountPermissions []AccountPermission) (resp *AddPermissionResponse, err error) {
 
240
        resp = &AddPermissionResponse{}
 
241
        params := makeParams("AddPermission")
 
242
 
 
243
        params["Label"] = label
 
244
        for i, accountPermission := range accountPermissions {
 
245
                params["AWSAccountId."+strconv.Itoa(i+1)] = accountPermission.AWSAccountId
 
246
                params["ActionName."+strconv.Itoa(i+1)] = accountPermission.ActionName
 
247
        }
 
248
 
 
249
        err = q.SQS.query(q.Url, params, resp)
 
250
        return
 
251
}
 
252
 
 
253
// RemovePermission action revokes any permissions in the queue policy that matches the Label parameter.
 
254
//
 
255
// See http://goo.gl/YLOe8 for more details
 
256
func (q *Queue) RemovePermission(label string) (resp *RemovePermissionResponse, err error) {
 
257
        resp = &RemovePermissionResponse{}
 
258
        params := makeParams("RemovePermission")
 
259
 
 
260
        params["Label"] = label
 
261
        err = q.SQS.query(q.Url, params, resp)
 
262
        return
 
263
}
 
264
 
 
265
// GetQueueAttributes action returns one or all attributes of a queue.
 
266
//
 
267
// See http://goo.gl/WejDu for more details
 
268
func (q *Queue) GetQueueAttributes(attributes []string) (resp *GetQueueAttributesResponse, err error) {
 
269
        resp = &GetQueueAttributesResponse{}
 
270
        params := makeParams("GetQueueAttributes")
 
271
 
 
272
        for i, attribute := range attributes {
 
273
                params["Attribute."+strconv.Itoa(i+1)] = attribute
 
274
        }
 
275
 
 
276
        err = q.SQS.query(q.Url, params, resp)
 
277
        return
 
278
}
 
279
 
 
280
// ChangeMessageVisibility action changes the visibility timeout of a specified message in a queue to a new value.
 
281
//
 
282
// See http://goo.gl/EyJKF for more details
 
283
func (q *Queue) ChangeMessageVisibility(receiptHandle string, visibilityTimeout int) (resp *ChangeMessageVisibilityResponse, err error) {
 
284
        resp = &ChangeMessageVisibilityResponse{}
 
285
        params := makeParams("ChangeMessageVisibility")
 
286
 
 
287
        params["VisibilityTimeout"] = strconv.Itoa(visibilityTimeout)
 
288
        params["ReceiptHandle"] = receiptHandle
 
289
 
 
290
        err = q.SQS.query(q.Url, params, resp)
 
291
        return
 
292
}
 
293
 
 
294
// ChangeMessageVisibilityBatch action is a batch version of the ChangeMessageVisibility action.
 
295
//
 
296
// See http://goo.gl/pgffn for more details
 
297
func (q *Queue) ChangeMessageVisibilityBatch(messageVisibilityBatch []ChangeMessageVisibilityBatchEntry) (resp *ChangeMessageVisibilityBatchResponse, err error) {
 
298
        resp = &ChangeMessageVisibilityBatchResponse{}
 
299
        params := makeParams("ChangeMessageVisibilityBatch")
 
300
 
 
301
        for i, messageVisibility := range messageVisibilityBatch {
 
302
                params["ChangeMessageVisibilityBatchRequestEntry."+strconv.Itoa(i+1)+".Id"] = messageVisibility.Id
 
303
                params["ChangeMessageVisibilityBatchRequestEntry."+strconv.Itoa(i+1)+".ReceiptHandle"] = messageVisibility.ReceiptHandle
 
304
                params["ChangeMessageVisibilityBatchRequestEntry."+strconv.Itoa(i+1)+".VisibilityTimeout"] = strconv.Itoa(messageVisibility.VisibilityTimeout)
 
305
        }
 
306
 
 
307
        err = q.SQS.query(q.Url, params, resp)
 
308
        return
 
309
}
 
310
 
 
311
// ReceiveMessage action retrieves one or more messages from the specified queue.
 
312
//
 
313
// See http://goo.gl/ThPrF for more details
 
314
func (q *Queue) ReceiveMessage(attributes []string, maxNumberOfMessages int, visibilityTimeout int) (resp *ReceiveMessageResponse, err error) {
 
315
        resp = &ReceiveMessageResponse{}
 
316
        params := makeParams("ReceiveMessage")
 
317
 
 
318
        for i, attribute := range attributes {
 
319
                params["Attribute."+strconv.Itoa(i+1)] = attribute
 
320
        }
 
321
 
 
322
        params["MaxNumberOfMessages"] = strconv.Itoa(maxNumberOfMessages)
 
323
        params["VisibilityTimeout"] = strconv.Itoa(visibilityTimeout)
 
324
 
 
325
        err = q.SQS.query(q.Url, params, resp)
 
326
        return
 
327
}
 
328
 
 
329
// DeleteMessage action deletes the specified message from the specified queue.
 
330
//
 
331
// See http://goo.gl/6XBv7 for more details
 
332
func (q *Queue) DeleteMessage(receiptHandle string) (resp *DeleteMessageResponse, err error) {
 
333
        resp = &DeleteMessageResponse{}
 
334
        params := makeParams("DeleteMessage")
 
335
 
 
336
        params["ReceiptHandle"] = receiptHandle
 
337
 
 
338
        err = q.SQS.query(q.Url, params, resp)
 
339
        return
 
340
}
 
341
 
 
342
// DeleteMessageBatch action is a batch version of the DeleteMessage action.
 
343
//
 
344
// See http://goo.gl/y1ehG for more details
 
345
func (q *Queue) DeleteMessageBatch(deleteMessageBatch []DeleteMessageBatch) (resp *DeleteMessageBatchResponse, err error) {
 
346
        resp = &DeleteMessageBatchResponse{}
 
347
        params := makeParams("DeleteMessageBatch")
 
348
 
 
349
        for i, deleteMessage := range deleteMessageBatch {
 
350
                params["DeleteMessageBatchRequestEntry."+strconv.Itoa(i+1)+".Id"] = deleteMessage.Id
 
351
                params["DeleteMessageBatchRequestEntry."+strconv.Itoa(i+1)+".ReceiptHandle"] = deleteMessage.ReceiptHandle
 
352
        }
 
353
 
 
354
        err = q.SQS.query(q.Url, params, resp)
 
355
        return
 
356
}
 
357
 
 
358
// SendMessage action delivers a message to the specified queue.
 
359
// The maximum allowed size is 64KB
 
360
//
 
361
// See http://goo.gl/7OnPb for more details
 
362
func (q *Queue) SendMessage(messageBody string) (resp *SendMessageResponse, err error) {
 
363
        resp = &SendMessageResponse{}
 
364
        params := makeParams("SendMessage")
 
365
 
 
366
        params["MessageBody"] = messageBody
 
367
        err = q.SQS.query(q.Url, params, resp)
 
368
        return
 
369
}
 
370
 
 
371
// SendMessageWithDelay is a helper function for SendMessage action which delivers a message to the specified queue
 
372
// with a delay.
 
373
//
 
374
// See http://goo.gl/7OnPb for more details
 
375
func (q *Queue) SendMessageWithDelay(messageBody string, delaySeconds int) (resp *SendMessageResponse, err error) {
 
376
        resp = &SendMessageResponse{}
 
377
        params := makeParams("SendMessage")
 
378
 
 
379
        params["MessageBody"] = messageBody
 
380
        params["DelaySeconds"] = strconv.Itoa(delaySeconds)
 
381
        err = q.SQS.query(q.Url, params, resp)
 
382
        return
 
383
}
 
384
 
 
385
// SendMessageBatch action delivers up to ten messages to the specified queue.
 
386
//
 
387
// See http://goo.gl/mNytv for more details
 
388
func (q *Queue) SendMessageBatch(sendMessageBatchRequests []SendMessageBatchRequestEntry) (resp *SendMessageBatchResponse, err error) {
 
389
        resp = &SendMessageBatchResponse{}
 
390
        params := makeParams("SendMessageBatch")
 
391
 
 
392
        for i, sendMessageBatchRequest := range sendMessageBatchRequests {
 
393
                params["SendMessageBatchRequestEntry."+strconv.Itoa(i+1)+".Id"] = sendMessageBatchRequest.Id
 
394
                params["SendMessageBatchRequestEntry."+strconv.Itoa(i+1)+".MessageBody"] = sendMessageBatchRequest.MessageBody
 
395
                params["SendMessageBatchRequestEntry."+strconv.Itoa(i+1)+".DelaySeconds"] = strconv.Itoa(sendMessageBatchRequest.DelaySeconds)
 
396
        }
 
397
 
 
398
        err = q.SQS.query(q.Url, params, resp)
 
399
        return
 
400
}
 
401
 
 
402
// Delete action deletes the queue specified by the queue URL, regardless of whether the queue is empty.
 
403
//
 
404
// See http://goo.gl/c3YCr for more details
 
405
func (q *Queue) Delete() (resp *DeleteQueueResponse, err error) {
 
406
        resp = &DeleteQueueResponse{}
 
407
        params := makeParams("Delete")
 
408
 
 
409
        err = q.SQS.query(q.Url, params, resp)
 
410
        return
 
411
}
 
412
 
 
413
// SetQueueAttributes action sets one attribute of a queue per request.
 
414
//
 
415
// See http://goo.gl/LyZnj for more details
 
416
func (q *Queue) SetQueueAttributes(attribute Attribute) (resp *SetQueueAttributesResponse, err error) {
 
417
        resp = &SetQueueAttributesResponse{}
 
418
        params := makeParams("SetQueueAttributes")
 
419
 
 
420
        params["Attribute.Name"] = attribute.Name
 
421
        params["Attribute.Value"] = attribute.Value
 
422
 
 
423
        err = q.SQS.query(q.Url, params, resp)
 
424
        return
 
425
}
 
426
 
 
427
// ListQueues  action returns a list of your queues.
 
428
//
 
429
// See http://goo.gl/RPRWr for more details
 
430
 
 
431
func (s *SQS) ListQueues() (resp *ListQueuesResponse, err error) {
 
432
        resp = &ListQueuesResponse{}
 
433
        params := makeParams("ListQueues")
 
434
 
 
435
        err = s.query("", params, resp)
 
436
        return
 
437
}
 
438
 
 
439
// ListQueuesWithPrefix action returns only a list of queues with a name beginning with the specified value are returned
 
440
//
 
441
// See http://goo.gl/RPRWr for more details
 
442
func (s *SQS) ListQueuesWithPrefix(queueNamePrefix string) (resp *ListQueuesResponse, err error) {
 
443
        resp = &ListQueuesResponse{}
 
444
        params := makeParams("ListQueues")
 
445
 
 
446
        if queueNamePrefix != "" {
 
447
                params["QueueNamePrefix"] = queueNamePrefix
 
448
        }
 
449
 
 
450
        err = s.query("", params, resp)
 
451
        return
 
452
}
 
453
 
 
454
// GetQueue is a helper function for GetQueueUrl action that returns an instance of a queue with specified name.
 
455
//
 
456
// See http://goo.gl/hk7Iu for more details
 
457
func (s *SQS) GetQueue(queueName string) (queue *Queue, err error) {
 
458
        resp, err := s.GetQueueUrl(queueName)
 
459
        if err != nil {
 
460
                return nil, err
 
461
        }
 
462
 
 
463
        queue = &Queue{s, resp.QueueUrl}
 
464
        return
 
465
}
 
466
 
 
467
// GetQueueOfOwner is a helper function for GetQueueUrl action that returns an instance of a queue with specified name
 
468
// and belongs to the specified AWS Account Id.
 
469
//
 
470
// See http://goo.gl/hk7Iu for more details
 
471
func (s *SQS) GetQueueOfOwner(queueName, queueOwnerAWSAccountId string) (queue *Queue, err error) {
 
472
        resp, err := s.GetQueueUrlOfOwner(queueName, queueOwnerAWSAccountId)
 
473
        if err != nil {
 
474
                return nil, err
 
475
        }
 
476
 
 
477
        queue = &Queue{s, resp.QueueUrl}
 
478
        return
 
479
 
 
480
}
 
481
 
 
482
// GetQueueUrl action returns the Uniform Resource Locater (URL) of a queue.
 
483
//
 
484
// See http://goo.gl/hk7Iu for more details
 
485
func (s *SQS) GetQueueUrl(queueName string) (resp *GetQueueUrlResponse, err error) {
 
486
        resp = &GetQueueUrlResponse{}
 
487
        params := makeParams("GetQueueUrl")
 
488
 
 
489
        params["QueueName"] = queueName
 
490
 
 
491
        err = s.query("", params, resp)
 
492
        return
 
493
}
 
494
 
 
495
// GetQueueUrlOfOwner is a helper function for GetQueueUrl action that returns the URL of a queue with specified name
 
496
// and belongs to the specified AWS Account Id.
 
497
//
 
498
// See http://goo.gl/hk7Iu for more details for more details
 
499
func (s *SQS) GetQueueUrlOfOwner(queueName, queueOwnerAWSAccountId string) (resp *GetQueueUrlResponse, err error) {
 
500
        resp = &GetQueueUrlResponse{}
 
501
        params := makeParams("GetQueueUrl")
 
502
 
 
503
        params["QueueName"] = queueName
 
504
 
 
505
        if queueOwnerAWSAccountId != "" {
 
506
                params["QueueOwnerAWSAccountId"] = queueOwnerAWSAccountId
 
507
        }
 
508
 
 
509
        err = s.query("", params, resp)
 
510
        return
 
511
}
 
512
 
 
513
func (s *SQS) query(queueUrl string, params map[string]string, resp interface{}) error {
 
514
        params["Version"] = "2011-10-01"
 
515
        params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339)
 
516
        endpoint, err := url.Parse(s.Region.SQSEndpoint)
 
517
        if err != nil {
 
518
                return err
 
519
        }
 
520
        var path string
 
521
        if queueUrl != "" {
 
522
                path = queueUrl[len(s.Region.SQSEndpoint):]
 
523
        } else {
 
524
                path = "/"
 
525
        }
 
526
 
 
527
        sign(s.Auth, "GET", path, params, endpoint.Host)
 
528
        endpoint.RawQuery = multimap(params).Encode()
 
529
        if debug {
 
530
                log.Printf("get { %v } -> {\n", endpoint.String())
 
531
        }
 
532
 
 
533
        r, err := http.Get(endpoint.String())
 
534
        if err != nil {
 
535
                return err
 
536
        }
 
537
        defer r.Body.Close()
 
538
 
 
539
        if debug {
 
540
                dump, _ := httputil.DumpResponse(r, true)
 
541
                log.Printf("response:\n")
 
542
                log.Printf("%v\n}\n", string(dump))
 
543
        }
 
544
        if r.StatusCode != 200 {
 
545
                return buildError(r)
 
546
        }
 
547
        err = xml.NewDecoder(r.Body).Decode(resp)
 
548
        return err
 
549
}
 
550
 
 
551
func multimap(p map[string]string) url.Values {
 
552
        q := make(url.Values, len(p))
 
553
        for k, v := range p {
 
554
                q[k] = []string{v}
 
555
        }
 
556
        return q
 
557
}
 
558
 
 
559
func buildError(r *http.Response) error {
 
560
        errors := xmlErrors{}
 
561
        xml.NewDecoder(r.Body).Decode(&errors)
 
562
        var err Error
 
563
        if len(errors.Errors) > 0 {
 
564
                err = errors.Errors[0]
 
565
        }
 
566
        err.RequestId = errors.RequestId
 
567
        err.StatusCode = r.StatusCode
 
568
        if err.Message == "" {
 
569
                err.Message = r.Status
 
570
        }
 
571
        return &err
 
572
}
 
573
 
 
574
func makeParams(action string) map[string]string {
 
575
        params := make(map[string]string)
 
576
        params["Action"] = action
 
577
        return params
 
578
}