Removing goamz (#5753)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3b081bda08
Коммит
24496cd0b2
195
NOTICE.txt
195
NOTICE.txt
@@ -3554,201 +3554,6 @@ the property of their respective owners.
|
||||
|
||||
---
|
||||
|
||||
This product contains a modified portion of 'goamz', which enables Go programs to interact with Amazon Web Services.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/goamz/goamz
|
||||
|
||||
* LICENSE:
|
||||
|
||||
This software is licensed under the LGPLv3, included below.
|
||||
|
||||
As a special exception to the GNU Lesser General Public License version 3
|
||||
("LGPL3"), the copyright holders of this Library give you permission to
|
||||
convey to a third party a Combined Work that links statically or dynamically
|
||||
to this Library without providing any Minimal Corresponding Source or
|
||||
Minimal Application Code as set out in 4d or providing the installation
|
||||
information set out in section 4e, provided that you comply with the other
|
||||
provisions of LGPL3 and provided that you meet, for the Application the
|
||||
terms and conditions of the license(s) which apply to the Application.
|
||||
|
||||
Except as stated in this special exception, the provisions of LGPL3 will
|
||||
continue to comply in full to this Library. If you modify this Library, you
|
||||
may apply this exception to your version of this Library, but you are not
|
||||
obliged to do so. If you do not wish to do so, delete this exception
|
||||
statement from your version. This exception does not (and cannot) modify any
|
||||
license terms which apply to the Application, with which you must still
|
||||
comply.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
||||
---
|
||||
|
||||
This product contains a modified portion of 'pdfjs-dist', is a Portable Document Format (PDF) library that is built with HTML5 built by mozilla.
|
||||
|
||||
* HOMEPAGE:
|
||||
|
||||
9
glide.lock
сгенерированный
9
glide.lock
сгенерированный
@@ -1,5 +1,5 @@
|
||||
hash: 978c0474f3432a26942e213ca1ba8e957fa5a34ee4652d736dae152fbd6d5384
|
||||
updated: 2017-03-13T11:12:52.056509274-04:00
|
||||
hash: cd23f3578895da94079e79ec355cca1eb64867c5b58f02fc5ec00dd88576d575
|
||||
updated: 2017-03-13T15:29:09.097298322-04:00
|
||||
imports:
|
||||
- name: github.com/alecthomas/log4go
|
||||
version: e5dc62318d9bd58682f1dceb53a4b24e8253682f
|
||||
@@ -21,11 +21,6 @@ imports:
|
||||
version: 8168ee085ee43257585e50c6441aadf54ecb2c9f
|
||||
- name: github.com/go-sql-driver/mysql
|
||||
version: a0583e0143b1624142adab07e0e97fe106d99561
|
||||
- name: github.com/goamz/goamz
|
||||
version: c35091c30f44b7f151ec9028b895465a191d1ea7
|
||||
subpackages:
|
||||
- aws
|
||||
- s3
|
||||
- name: github.com/golang/freetype
|
||||
version: d9be45aaf7452cc30c0ceb1b1bf7efe1d17b7c87
|
||||
subpackages:
|
||||
|
||||
@@ -11,10 +11,6 @@ import:
|
||||
version: v2.5.0
|
||||
- package: github.com/go-sql-driver/mysql
|
||||
version: v1.3
|
||||
- package: github.com/goamz/goamz
|
||||
subpackages:
|
||||
- aws
|
||||
- s3
|
||||
- package: github.com/golang/freetype
|
||||
- package: github.com/gorilla/handlers
|
||||
version: v1.2
|
||||
|
||||
1
vendor/github.com/goamz/goamz/.gitignore
сгенерированный
поставляемый
1
vendor/github.com/goamz/goamz/.gitignore
сгенерированный
поставляемый
@@ -1 +0,0 @@
|
||||
*.swp
|
||||
1
vendor/github.com/goamz/goamz/.lbox
сгенерированный
поставляемый
1
vendor/github.com/goamz/goamz/.lbox
сгенерированный
поставляемый
@@ -1 +0,0 @@
|
||||
propose -for=lp:goamz -cr
|
||||
32
vendor/github.com/goamz/goamz/.travis.yml
сгенерированный
поставляемый
32
vendor/github.com/goamz/goamz/.travis.yml
сгенерированный
поставляемый
@@ -1,32 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- tip
|
||||
|
||||
before_script:
|
||||
- FIXED=$(go fmt ./... | wc -l); if [ $FIXED -gt 0 ]; then echo "gofmt - $FIXED file(s) not formatted correctly, please run gofmt to fix this." && exit 1; fi
|
||||
|
||||
script:
|
||||
- go test -v ./autoscaling/
|
||||
- go test -v ./aws/
|
||||
- go test -v ./cloudformation/
|
||||
- go test -v ./cloudfront/
|
||||
- go test -v ./cloudwatch/
|
||||
- go test -v ./dynamodb/
|
||||
- go test -v ./ec2/
|
||||
- go test -v ./ecs/
|
||||
- go test -v ./elb/
|
||||
- go test -v ./iam/
|
||||
- go test -v ./rds/
|
||||
- go test -v ./route53/
|
||||
- go test -v ./s3/
|
||||
- go test -v ./sqs/
|
||||
- go test -v ./sts/
|
||||
- go test -v ./exp/mturk/
|
||||
- go test -v ./exp/sdb/
|
||||
# - go test -v ./exp/ses/
|
||||
# - go test -v ./exp/sns/
|
||||
5
vendor/github.com/goamz/goamz/CHANGES.md
сгенерированный
поставляемый
5
vendor/github.com/goamz/goamz/CHANGES.md
сгенерированный
поставляемый
@@ -1,5 +0,0 @@
|
||||
# Changes in this fork
|
||||
|
||||
* Added CNNorth Region
|
||||
* Change SignV2 to SignV4
|
||||
* V4Signer.canonicalQueryString empty value must append "="
|
||||
185
vendor/github.com/goamz/goamz/LICENSE
сгенерированный
поставляемый
185
vendor/github.com/goamz/goamz/LICENSE
сгенерированный
поставляемый
@@ -1,185 +0,0 @@
|
||||
This software is licensed under the LGPLv3, included below.
|
||||
|
||||
As a special exception to the GNU Lesser General Public License version 3
|
||||
("LGPL3"), the copyright holders of this Library give you permission to
|
||||
convey to a third party a Combined Work that links statically or dynamically
|
||||
to this Library without providing any Minimal Corresponding Source or
|
||||
Minimal Application Code as set out in 4d or providing the installation
|
||||
information set out in section 4e, provided that you comply with the other
|
||||
provisions of LGPL3 and provided that you meet, for the Application the
|
||||
terms and conditions of the license(s) which apply to the Application.
|
||||
|
||||
Except as stated in this special exception, the provisions of LGPL3 will
|
||||
continue to comply in full to this Library. If you modify this Library, you
|
||||
may apply this exception to your version of this Library, but you are not
|
||||
obliged to do so. If you do not wish to do so, delete this exception
|
||||
statement from your version. This exception does not (and cannot) modify any
|
||||
license terms which apply to the Application, with which you must still
|
||||
comply.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
64
vendor/github.com/goamz/goamz/README.md
сгенерированный
поставляемый
64
vendor/github.com/goamz/goamz/README.md
сгенерированный
поставляемый
@@ -1,64 +0,0 @@
|
||||
# goamz - An Amazon Library for Go
|
||||
|
||||
[](https://travis-ci.org/goamz/goamz)
|
||||
|
||||
The _goamz_ package enables Go programs to interact with Amazon Web Services.
|
||||
|
||||
This is a fork of the version [developed within Canonical](https://wiki.ubuntu.com/goamz) with additional functionality and services from [a number of contributors](https://github.com/goamz/goamz/contributors)!
|
||||
|
||||
The API of AWS is very comprehensive, though, and goamz doesn't even scratch the surface of it. That said, it's fairly well tested, and is the foundation in which further calls can easily be integrated. We'll continue extending the API as necessary - Pull Requests are _very_ welcome!
|
||||
|
||||
The following packages are available at the moment:
|
||||
|
||||
```
|
||||
github.com/goamz/goamz/autoscaling
|
||||
github.com/goamz/goamz/aws
|
||||
github.com/goamz/goamz/cloudformation
|
||||
github.com/goamz/goamz/cloudfront
|
||||
github.com/goamz/goamz/cloudwatch
|
||||
github.com/goamz/goamz/dynamodb
|
||||
github.com/goamz/goamz/ecs
|
||||
github.com/goamz/goamz/ec2
|
||||
github.com/goamz/goamz/elb
|
||||
github.com/goamz/goamz/iam
|
||||
github.com/goamz/goamz/rds
|
||||
github.com/goamz/goamz/route53
|
||||
github.com/goamz/goamz/s3
|
||||
github.com/goamz/goamz/sqs
|
||||
github.com/goamz/goamz/sts
|
||||
|
||||
github.com/goamz/goamz/exp/mturk
|
||||
github.com/goamz/goamz/exp/sdb
|
||||
github.com/goamz/goamz/exp/sns
|
||||
```
|
||||
|
||||
Packages under `exp/` are still in an experimental or unfinished/unpolished state.
|
||||
|
||||
## API documentation
|
||||
|
||||
The API documentation is currently available at:
|
||||
|
||||
[http://godoc.org/github.com/goamz/goamz](http://godoc.org/github.com/goamz/goamz)
|
||||
|
||||
## How to build and install goamz
|
||||
|
||||
Just use `go get` with any of the available packages. For example:
|
||||
|
||||
* `$ go get github.com/goamz/goamz/ec2`
|
||||
* `$ go get github.com/goamz/goamz/s3`
|
||||
|
||||
## Running tests
|
||||
|
||||
To run tests, first install gocheck with:
|
||||
|
||||
`$ go get gopkg.in/check.v1`
|
||||
|
||||
Then run go test as usual:
|
||||
|
||||
`$ go test github.com/goamz/goamz/...`
|
||||
|
||||
_Note:_ running all tests with the command `go test ./...` will currently fail as tests do not tear down their HTTP listeners.
|
||||
|
||||
If you want to run integration tests (costs money), set up the EC2 environment variables as usual, and run:
|
||||
|
||||
$ gotest -i
|
||||
1768
vendor/github.com/goamz/goamz/autoscaling/autoscaling.go
сгенерированный
поставляемый
1768
vendor/github.com/goamz/goamz/autoscaling/autoscaling.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1180
vendor/github.com/goamz/goamz/autoscaling/autoscaling_test.go
сгенерированный
поставляемый
1180
vendor/github.com/goamz/goamz/autoscaling/autoscaling_test.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
627
vendor/github.com/goamz/goamz/autoscaling/responses_test.go
сгенерированный
поставляемый
627
vendor/github.com/goamz/goamz/autoscaling/responses_test.go
сгенерированный
поставляемый
@@ -1,627 +0,0 @@
|
||||
package autoscaling
|
||||
|
||||
var AttachInstancesResponse = `
|
||||
<AttachInstancesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</AttachInstancesResponse>
|
||||
`
|
||||
var BasicGroupResponse = `
|
||||
<DescribeAutoScalingGroupsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeAutoScalingGroupsResult>
|
||||
<AutoScalingGroups/>
|
||||
</DescribeAutoScalingGroupsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>08c3bedc-8421-11e3-9bb5-bfa219b29cce</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeAutoScalingGroupsResponse>
|
||||
`
|
||||
var CreateAutoScalingGroupResponse = `
|
||||
<CreateAutoScalingGroupResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateAutoScalingGroupResponse>
|
||||
`
|
||||
var CreateLaunchConfigurationResponse = `
|
||||
<CreateLaunchConfigurationResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>7c6e177f-f082-11e1-ac58-3714bEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateLaunchConfigurationResponse>
|
||||
`
|
||||
var CreateOrUpdateTagsResponse = `
|
||||
<CreateOrUpdateTagsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>b0203919-bf1b-11e2-8a01-13263EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateOrUpdateTagsResponse>
|
||||
`
|
||||
var DeleteAutoScalingGroupResponse = `
|
||||
<DeleteAutoScalingGroupResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>70a76d42-9665-11e2-9fdf-211deEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteAutoScalingGroupResponse>
|
||||
`
|
||||
var DeleteAutoScalingGroupErrorResponse = `
|
||||
<ErrorResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>ResourceInUse</Code>
|
||||
<Message>You cannot delete an AutoScalingGroup while there are instances or pending Spot instance request(s) still in the group.</Message>
|
||||
</Error>
|
||||
<RequestId>70a76d42-9665-11e2-9fdf-211deEXAMPLE</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
var DeleteLaunchConfigurationResponse = `
|
||||
<DeleteLaunchConfigurationResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>7347261f-97df-11e2-8756-35eEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteLaunchConfigurationResponse>
|
||||
`
|
||||
var DeleteLaunchConfigurationInUseResponse = `
|
||||
<ErrorResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>ResourceInUse</Code>
|
||||
<Message>Cannot delete launch configuration my-test-lc because it is attached to AutoScalingGroup test</Message>
|
||||
</Error>
|
||||
<RequestId>7347261f-97df-11e2-8756-35eEXAMPLE</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
var DeleteScheduledActionResponse = `
|
||||
<DeleteScheduledActionResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>0f38bb02-8421-11e3-9bb5-bfa219b29cce</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteScheduledActionResponse>
|
||||
`
|
||||
var DeleteTagsResponse = `
|
||||
<CreateOrUpdateTagsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>b0203919-bf1b-11e2-8a01-13263EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateOrUpdateTagsResponse>
|
||||
`
|
||||
var DescribeAccountLimitsResponse = `
|
||||
<DescribeAccountLimitsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeAccountLimitsResult>
|
||||
<MaxNumberOfLaunchConfigurations>100</MaxNumberOfLaunchConfigurations>
|
||||
<MaxNumberOfAutoScalingGroups>20</MaxNumberOfAutoScalingGroups>
|
||||
</DescribeAccountLimitsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>a32bd184-519d-11e3-a8a4-c1c467cbcc3b</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeAccountLimitsResponse>
|
||||
`
|
||||
var DescribeAdjustmentTypesResponse = `
|
||||
<DescribeAdjustmentTypesResponse xmlns="http://autoscaling.amazonaws.com/doc/201-01-01/">
|
||||
<DescribeAdjustmentTypesResult>
|
||||
<AdjustmentTypes>
|
||||
<member>
|
||||
<AdjustmentType>ChangeInCapacity</AdjustmentType>
|
||||
</member>
|
||||
<member>
|
||||
<AdjustmentType>ExactCapacity</AdjustmentType>
|
||||
</member>
|
||||
<member>
|
||||
<AdjustmentType>PercentChangeInCapacity</AdjustmentType>
|
||||
</member>
|
||||
</AdjustmentTypes>
|
||||
</DescribeAdjustmentTypesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>cc5f0337-b694-11e2-afc0-6544dEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeAdjustmentTypesResponse>
|
||||
`
|
||||
var DescribeAutoScalingGroupsResponse = `
|
||||
<DescribeAutoScalingGroupsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeAutoScalingGroupsResult>
|
||||
<AutoScalingGroups>
|
||||
<member>
|
||||
<Tags>
|
||||
<member>
|
||||
<ResourceId>my-test-asg-lbs</ResourceId>
|
||||
<PropagateAtLaunch>true</PropagateAtLaunch>
|
||||
<Value>bar</Value>
|
||||
<Key>foo</Key>
|
||||
<ResourceType>auto-scaling-group</ResourceType>
|
||||
</member>
|
||||
<member>
|
||||
<ResourceId>my-test-asg-lbs</ResourceId>
|
||||
<PropagateAtLaunch>true</PropagateAtLaunch>
|
||||
<Value>qux</Value>
|
||||
<Key>baz</Key>
|
||||
<ResourceType>auto-scaling-group</ResourceType>
|
||||
</member>
|
||||
</Tags>
|
||||
<SuspendedProcesses/>
|
||||
<AutoScalingGroupName>my-test-asg-lbs</AutoScalingGroupName>
|
||||
<HealthCheckType>ELB</HealthCheckType>
|
||||
<CreatedTime>2013-05-06T17:47:15.107Z</CreatedTime>
|
||||
<EnabledMetrics/>
|
||||
<LaunchConfigurationName>my-test-lc</LaunchConfigurationName>
|
||||
<Instances>
|
||||
<member>
|
||||
<HealthStatus>Healthy</HealthStatus>
|
||||
<AvailabilityZone>us-east-1b</AvailabilityZone>
|
||||
<InstanceId>i-zb1f313</InstanceId>
|
||||
<LaunchConfigurationName>my-test-lc</LaunchConfigurationName>
|
||||
<LifecycleState>InService</LifecycleState>
|
||||
</member>
|
||||
<member>
|
||||
<HealthStatus>Healthy</HealthStatus>
|
||||
<AvailabilityZone>us-east-1a</AvailabilityZone>
|
||||
<InstanceId>i-90123adv</InstanceId>
|
||||
<LaunchConfigurationName>my-test-lc</LaunchConfigurationName>
|
||||
<LifecycleState>InService</LifecycleState>
|
||||
</member>
|
||||
</Instances>
|
||||
<DesiredCapacity>2</DesiredCapacity>
|
||||
<AvailabilityZones>
|
||||
<member>us-east-1b</member>
|
||||
<member>us-east-1a</member>
|
||||
</AvailabilityZones>
|
||||
<LoadBalancerNames>
|
||||
<member>my-test-asg-loadbalancer</member>
|
||||
</LoadBalancerNames>
|
||||
<MinSize>2</MinSize>
|
||||
<VPCZoneIdentifier>subnet-32131da1,subnet-1312dad2</VPCZoneIdentifier>
|
||||
<HealthCheckGracePeriod>120</HealthCheckGracePeriod>
|
||||
<DefaultCooldown>300</DefaultCooldown>
|
||||
<AutoScalingGroupARN>arn:aws:autoscaling:us-east-1:803981987763:autoScalingGroup:ca861182-c8f9-4ca7-b1eb-cd35505f5ebb:autoScalingGroupName/my-test-asg-lbs</AutoScalingGroupARN>
|
||||
<TerminationPolicies>
|
||||
<member>Default</member>
|
||||
</TerminationPolicies>
|
||||
<MaxSize>10</MaxSize>
|
||||
</member>
|
||||
</AutoScalingGroups>
|
||||
</DescribeAutoScalingGroupsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>0f02a07d-b677-11e2-9eb0-dd50EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeAutoScalingGroupsResponse>
|
||||
`
|
||||
var DescribeAutoScalingInstancesResponse = `
|
||||
<DescribeAutoScalingInstancesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeAutoScalingInstancesResult>
|
||||
<AutoScalingInstances>
|
||||
<member>
|
||||
<HealthStatus>Healthy</HealthStatus>
|
||||
<AutoScalingGroupName>my-test-asg</AutoScalingGroupName>
|
||||
<AvailabilityZone>us-east-1a</AvailabilityZone>
|
||||
<InstanceId>i-78e0d40b</InstanceId>
|
||||
<LaunchConfigurationName>my-test-lc</LaunchConfigurationName>
|
||||
<LifecycleState>InService</LifecycleState>
|
||||
</member>
|
||||
</AutoScalingInstances>
|
||||
</DescribeAutoScalingInstancesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>df992dc3-b72f-11e2-81e1-750aa6EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeAutoScalingInstancesResponse>
|
||||
`
|
||||
var DescribeLaunchConfigurationsResponse = `
|
||||
<DescribeLaunchConfigurationsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeLaunchConfigurationsResult>
|
||||
<LaunchConfigurations>
|
||||
<member>
|
||||
<AssociatePublicIpAddress>true</AssociatePublicIpAddress>
|
||||
<SecurityGroups/>
|
||||
<CreatedTime>2013-01-21T23:04:42.200Z</CreatedTime>
|
||||
<KernelId/>
|
||||
<LaunchConfigurationName>my-test-lc</LaunchConfigurationName>
|
||||
<UserData/>
|
||||
<InstanceType>m1.small</InstanceType>
|
||||
<LaunchConfigurationARN>arn:aws:autoscaling:us-east-1:803981987763:launchConfiguration:9dbbbf87-6141-428a-a409-0752edbe6cad:launchConfigurationName/my-test-lc</LaunchConfigurationARN>
|
||||
<BlockDeviceMappings>
|
||||
<member>
|
||||
<VirtualName>ephemeral0</VirtualName>
|
||||
<DeviceName>/dev/sdb</DeviceName>
|
||||
</member>
|
||||
<member>
|
||||
<Ebs>
|
||||
<SnapshotId>snap-XXXXYYY</SnapshotId>
|
||||
<VolumeSize>100</VolumeSize>
|
||||
<Iops>50</Iops>
|
||||
<VolumeType>io1</VolumeType>
|
||||
<DeleteOnTermination>true</DeleteOnTermination>
|
||||
</Ebs>
|
||||
<DeviceName>/dev/sdf</DeviceName>
|
||||
</member>
|
||||
</BlockDeviceMappings>
|
||||
<ImageId>ami-514ac838</ImageId>
|
||||
<KeyName/>
|
||||
<RamdiskId/>
|
||||
<InstanceMonitoring>
|
||||
<Enabled>true</Enabled>
|
||||
</InstanceMonitoring>
|
||||
<EbsOptimized>false</EbsOptimized>
|
||||
</member>
|
||||
</LaunchConfigurations>
|
||||
</DescribeLaunchConfigurationsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>d05a22f8-b690-11e2-bf8e-2113fEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeLaunchConfigurationsResponse>
|
||||
`
|
||||
var DescribeMetricCollectionTypesResponse = `
|
||||
<DescribeMetricCollectionTypesResponse xmlns="http://autoscaling.amazonaws.co
|
||||
oc/2011-01-01/">
|
||||
<DescribeMetricCollectionTypesResult>
|
||||
<Metrics>
|
||||
<member>
|
||||
<Metric>GroupMinSize</Metric>
|
||||
</member>
|
||||
<member>
|
||||
<Metric>GroupMaxSize</Metric>
|
||||
</member>
|
||||
<member>
|
||||
<Metric>GroupDesiredCapacity</Metric>
|
||||
</member>
|
||||
<member>
|
||||
<Metric>GroupInServiceInstances</Metric>
|
||||
</member>
|
||||
<member>
|
||||
<Metric>GroupPendingInstances</Metric>
|
||||
</member>
|
||||
<member>
|
||||
<Metric>GroupTerminatingInstances</Metric>
|
||||
</member>
|
||||
<member>
|
||||
<Metric>GroupTotalInstances</Metric>
|
||||
</member>
|
||||
</Metrics>
|
||||
<Granularities>
|
||||
<member>
|
||||
<Granularity>1Minute</Granularity>
|
||||
</member>
|
||||
</Granularities>
|
||||
</DescribeMetricCollectionTypesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>07f3fea2-bf3c-11e2-9b6f-f3cdbb80c073</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeMetricCollectionTypesResponse>
|
||||
`
|
||||
var DescribeNotificationConfigurationsResponse = `
|
||||
<DescribeNotificationConfigurationsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeNotificationConfigurationsResult>
|
||||
<NotificationConfigurations>
|
||||
<member>
|
||||
<AutoScalingGroupName>my-test-asg</AutoScalingGroupName>
|
||||
<NotificationType>autoscaling: EC2_INSTANCE_LAUNCH</NotificationType>
|
||||
<TopicARN>vajdoafj231j41231/topic</TopicARN>
|
||||
</member>
|
||||
</NotificationConfigurations>
|
||||
</DescribeNotificationConfigurationsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>07f3fea2-bf3c-11e2-9b6f-f3cdbb80c073</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeNotificationConfigurationsResponse>
|
||||
`
|
||||
var DescribePoliciesResponse = `
|
||||
<DescribePoliciesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribePoliciesResult>
|
||||
<NextToken>3ef417fe-9202-12-8ddd-d13e1313413</NextToken>
|
||||
<ScalingPolicies>
|
||||
<member>
|
||||
<PolicyARN>arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/my-test-asg:policyName/MyScaleDownPolicy</PolicyARN>
|
||||
<AdjustmentType>ChangeInCapacity</AdjustmentType>
|
||||
<ScalingAdjustment>-1</ScalingAdjustment>
|
||||
<PolicyName>MyScaleDownPolicy</PolicyName>
|
||||
<AutoScalingGroupName>my-test-asg</AutoScalingGroupName>
|
||||
<Cooldown>60</Cooldown>
|
||||
<Alarms>
|
||||
<member>
|
||||
<AlarmName>TestQueue</AlarmName>
|
||||
<AlarmARN>arn:aws:cloudwatch:us-east-1:803981987763:alarm:TestQueue</AlarmARN>
|
||||
</member>
|
||||
</Alarms>
|
||||
</member>
|
||||
<member>
|
||||
<PolicyARN>arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:c55a5cdd-9be0-435b-b60b-a8dd313159f5:autoScalingGroupName/my-test-asg:policyName/MyScaleUpPolicy</PolicyARN>
|
||||
<AdjustmentType>ChangeInCapacity</AdjustmentType>
|
||||
<ScalingAdjustment>1</ScalingAdjustment>
|
||||
<PolicyName>MyScaleUpPolicy</PolicyName>
|
||||
<AutoScalingGroupName>my-test-asg</AutoScalingGroupName>
|
||||
<Cooldown>60</Cooldown>
|
||||
<Alarms>
|
||||
<member>
|
||||
<AlarmName>TestQueue</AlarmName>
|
||||
<AlarmARN>arn:aws:cloudwatch:us-east-1:803981987763:alarm:TestQueue</AlarmARN>
|
||||
</member>
|
||||
</Alarms>
|
||||
</member>
|
||||
</ScalingPolicies>
|
||||
</DescribePoliciesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>ec3bffad-b739-11e2-b38d-15fbEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribePoliciesResponse>
|
||||
`
|
||||
var DescribeScalingActivitiesResponse = `
|
||||
<DescribeScalingActivitiesResponse xmlns="http://ec2.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeScalingActivitiesResult>
|
||||
<NextToken>3ef417fe-9202-12-8ddd-d13e1313413</NextToken>
|
||||
<Activities>
|
||||
<member>
|
||||
<StatusCode>Failed</StatusCode>
|
||||
<Progress>0</Progress>
|
||||
<ActivityId>063308ae-aa22-4a9b-94f4-9faeEXAMPLE</ActivityId>
|
||||
<StartTime>2012-04-12T17:32:07.882Z</StartTime>
|
||||
<AutoScalingGroupName>my-test-asg</AutoScalingGroupName>
|
||||
<Cause>At 2012-04-12T17:31:30Z a user request created an AutoScalingGroup changing the desired capacity from 0 to 1. At 2012-04-12T17:32:07Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 1.</Cause>
|
||||
<Details>{}</Details>
|
||||
<Description>Launching a new EC2 instance. Status Reason: The image id 'ami-4edb0327' does not exist. Launching EC2 instance failed.</Description>
|
||||
<EndTime>2012-04-12T17:32:08Z</EndTime>
|
||||
<StatusMessage>The image id 'ami-4edb0327' does not exist. Launching EC2 instance failed.</StatusMessage>
|
||||
</member>
|
||||
</Activities>
|
||||
</DescribeScalingActivitiesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>7a641adc-84c5-11e1-a8a5-217ebEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeScalingActivitiesResponse>
|
||||
`
|
||||
var DescribeScalingProcessTypesResponse = `
|
||||
<DescribeScalingProcessTypesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeScalingProcessTypesResult>
|
||||
<Processes>
|
||||
<member>
|
||||
<ProcessName>AZRebalance</ProcessName>
|
||||
</member>
|
||||
<member>
|
||||
<ProcessName>AddToLoadBalancer</ProcessName>
|
||||
</member>
|
||||
<member>
|
||||
<ProcessName>AlarmNotification</ProcessName>
|
||||
</member>
|
||||
<member>
|
||||
<ProcessName>HealthCheck</ProcessName>
|
||||
</member>
|
||||
<member>
|
||||
<ProcessName>Launch</ProcessName>
|
||||
</member>
|
||||
<member>
|
||||
<ProcessName>ReplaceUnhealthy</ProcessName>
|
||||
</member>
|
||||
<member>
|
||||
<ProcessName>ScheduledActions</ProcessName>
|
||||
</member>
|
||||
<member>
|
||||
<ProcessName>Terminate</ProcessName>
|
||||
</member>
|
||||
</Processes>
|
||||
</DescribeScalingProcessTypesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>27f2eacc-b73f-11e2-ad99-c7aba3a9c963</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeScalingProcessTypesResponse>
|
||||
`
|
||||
var DescribeScheduledActionsResponse = `
|
||||
<DescribeScheduledActionsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeScheduledActionsResult>
|
||||
<ScheduledUpdateGroupActions>
|
||||
<member>
|
||||
<ScheduledActionName>SATest1</ScheduledActionName>
|
||||
<StartTime>2014-06-01T00:30:00Z</StartTime>
|
||||
<Time>2014-06-01T00:30:00Z</Time>
|
||||
<ScheduledActionARN>arn:aws:autoscaling:us-west-2:193024542802:scheduledUpdateGroupAction:61f68b2c-bde3-4316-9a81-eb95dc246509:autoScalingGroupName/ASGTest1:scheduledActionName/SATest1</ScheduledActionARN>
|
||||
<AutoScalingGroupName>ASGTest1</AutoScalingGroupName>
|
||||
<Recurrence>30 0 1 1,6,12 *</Recurrence>
|
||||
<MaxSize>4</MaxSize>
|
||||
</member>
|
||||
</ScheduledUpdateGroupActions>
|
||||
</DescribeScheduledActionsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>0eb4217f-8421-11e3-9233-7100ef811766</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeScheduledActionsResponse>
|
||||
`
|
||||
|
||||
var DescribeTerminationPolicyTypesResponse = `
|
||||
<DescribeTerminationPolicyTypesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeTerminationPolicyTypesResult>
|
||||
<TerminationPolicyTypes>
|
||||
<member>ClosestToNextInstanceHour</member>
|
||||
<member>Default</member>
|
||||
<member>NewestInstance</member>
|
||||
<member>OldestInstance</member>
|
||||
<member>OldestLaunchConfiguration</member>
|
||||
</TerminationPolicyTypes>
|
||||
</DescribeTerminationPolicyTypesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>d9a05827-b735-11e2-a40c-c79a5EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeTerminationPolicyTypesResponse>
|
||||
`
|
||||
var DescribeTagsResponse = `
|
||||
<DescribeTagsResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DescribeTagsResult>
|
||||
<Tags>
|
||||
<member>
|
||||
<ResourceId>my-test-asg</ResourceId>
|
||||
<PropagateAtLaunch>true</PropagateAtLaunch>
|
||||
<Value>1.0</Value>
|
||||
<Key>version</Key>
|
||||
<ResourceType>auto-scaling-group</ResourceType>
|
||||
</member>
|
||||
</Tags>
|
||||
</DescribeTagsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>086265fd-bf3e-11e2-85fc-fbb1EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeTagsResponse>
|
||||
`
|
||||
var DetachInstancesResponse = `
|
||||
<DetachInstancesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<DetachInstancesResult>
|
||||
<Activities>
|
||||
<member>
|
||||
<ActivityId>e54ff599-bf05-4076-8b95-a0f090ed90bb</ActivityId>
|
||||
<Progress>50</Progress>
|
||||
<StatusCode>InProgress</StatusCode>
|
||||
<StartTime>2014-06-14T00:07:30.280Z</StartTime>
|
||||
<Cause>At 2014-06-14T00:07:30Z instance i-5f2e8a0d was detached in response to a user request, shrinking the capacity from 4 to 3.</Cause>
|
||||
<AutoScalingGroupName>my-asg</AutoScalingGroupName>
|
||||
<Details>{"Availability Zone":"us-east-1a"}</Details>
|
||||
<Description>Detaching EC2 instance: i-5f2e8a0d</Description>
|
||||
</member>
|
||||
</Activities>
|
||||
</DetachInstancesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>e04f3b11-f357-11e3-a434-7f10009d5849</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DetachInstancesResponse>
|
||||
`
|
||||
var DisableMetricsCollectionResponse = `
|
||||
<DisableMetricsCollectionResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DisableMetricsCollectionResponse>
|
||||
`
|
||||
var EnableMetricsCollectionResponse = `
|
||||
<EnableMetricsCollectionResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</EnableMetricsCollectionResponse>
|
||||
`
|
||||
var EnterStandbyResponse = `
|
||||
<EnterStandbyResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<EnterStandbyResult>
|
||||
<Activities>
|
||||
<member>
|
||||
<ActivityId>462b4bc3-ad3b-4e67-a58d-96cd00f02f9e</ActivityId>
|
||||
<Progress>50</Progress>
|
||||
<StatusCode>InProgress</StatusCode>
|
||||
<StartTime>2014-06-13T22:35:50.884Z</StartTime>
|
||||
<Cause>At 2014-06-13T22:35:50Z instance i-5b73d709 was moved to standby in response to a user request, shrinking the capacity from 4 to 3.</Cause>
|
||||
<AutoScalingGroupName>my-asg</AutoScalingGroupName>
|
||||
<Details>{"Availability Zone":"us-east-1a"}</Details>
|
||||
<Description>Moving EC2 instance to Standby: i-5b73d709</Description>
|
||||
</member>
|
||||
</Activities>
|
||||
</EnterStandbyResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>126f2f31-f34b-11e3-bc51-b35178f0274f</RequestId>
|
||||
</ResponseMetadata>
|
||||
</EnterStandbyResponse>
|
||||
`
|
||||
var ExecutePolicyResponse = `
|
||||
<EnableMetricsCollectionResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</EnableMetricsCollectionResponse>
|
||||
`
|
||||
var ExitStandbyResponse = `
|
||||
<ExitStandbyResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ExitStandbyResult>
|
||||
<Activities>
|
||||
<member>
|
||||
<ActivityId>dca4efcf-eea6-4844-8064-cab1fecd1aa2</ActivityId>
|
||||
<Progress>30</Progress>
|
||||
<StatusCode>PreInService</StatusCode>
|
||||
<StartTime>2014-06-13T22:43:53.523Z</StartTime>
|
||||
<Cause>At 2014-06-13T22:43:53Z instance i-5b73d709 was moved out of standby in response to a user request, increasing the capacity from 3 to 4.</Cause>
|
||||
<AutoScalingGroupName>my-asg</AutoScalingGroupName>
|
||||
<Details>{"Availability Zone":"us-east-1a"}</Details>
|
||||
<Description>Moving EC2 instance out of Standby: i-5b73d709</Description>
|
||||
</member>
|
||||
</Activities>
|
||||
</ExitStandbyResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>321a11c8-f34c-11e3-a434-7f10009d5849</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ExitStandbyResponse>
|
||||
`
|
||||
var PutLifecycleHookResponse = `
|
||||
<PutLifecycleHookResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<PutLifecycleHookResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>1952f458-f645-11e3-bc51-b35178f0274f</RequestId>
|
||||
</ResponseMetadata>
|
||||
</PutLifecycleHookResponse>
|
||||
`
|
||||
var PutNotificationConfigurationResponse = `
|
||||
<EnableMetricsCollectionResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</EnableMetricsCollectionResponse>
|
||||
`
|
||||
var PutScalingPolicyResponse = `
|
||||
<PutScalingPolicyResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<PutScalingPolicyResult>
|
||||
<PolicyARN>arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:b0dcf5e8-02e6-4e31-9719-0675d0dc31ae:autoScalingGroupName/my-test-asg:policyName/my-scaleout-policy</PolicyARN>
|
||||
</PutScalingPolicyResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>3cfc6fef-c08b-11e2-a697-2922EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</PutScalingPolicyResponse>
|
||||
`
|
||||
var PutScheduledUpdateGroupActionResponse = `
|
||||
<PutScheduledUpdateGroupActionResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>3bc8c9bc-6a62-11e2-8a51-4b8a1EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</PutScheduledUpdateGroupActionResponse>
|
||||
`
|
||||
var ResumeProcessesResponse = `
|
||||
<ResumeProcessesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ResumeProcessesResponse>
|
||||
`
|
||||
var SetDesiredCapacityResponse = `
|
||||
<SetDesiredCapacityResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>9fb7e2db-6998-11e2-a985-57c82EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</SetDesiredCapacityResponse>
|
||||
`
|
||||
var SetInstanceHealthResponse = `
|
||||
<SetInstanceHealthResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>9fb7e2db-6998-11e2-a985-57c82EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</SetInstanceHealthResponse>
|
||||
`
|
||||
var SuspendProcessesResponse = `
|
||||
<SuspendProcessesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</SuspendProcessesResponse>
|
||||
`
|
||||
var TerminateInstanceInAutoScalingGroupResponse = `
|
||||
<TerminateInstanceInAutoScalingGroupResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<TerminateInstanceInAutoScalingGroupResult>
|
||||
<Activity>
|
||||
<StatusCode>InProgress</StatusCode>
|
||||
<ActivityId>cczc44a87-7d04-dsa15-31-d27c219864c5</ActivityId>
|
||||
<Progress>0</Progress>
|
||||
<StartTime>2014-01-26T14:08:30.560Z</StartTime>
|
||||
<Cause>At 2014-01-26T14:08:30Z instance i-br234123 was taken out of service in response to a user request.</Cause>
|
||||
<Details>{"Availability Zone":"us-east-1b"}</Details>
|
||||
<Description>Terminating EC2 instance: i-br234123</Description>
|
||||
</Activity>
|
||||
</TerminateInstanceInAutoScalingGroupResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</TerminateInstanceInAutoScalingGroupResponse>
|
||||
`
|
||||
var UpdateAutoScalingGroupResponse = `
|
||||
<UpdateAutoScalingGroupResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</UpdateAutoScalingGroupResponse>
|
||||
`
|
||||
74
vendor/github.com/goamz/goamz/aws/attempt.go
сгенерированный
поставляемый
74
vendor/github.com/goamz/goamz/aws/attempt.go
сгенерированный
поставляемый
@@ -1,74 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// AttemptStrategy represents a strategy for waiting for an action
|
||||
// to complete successfully. This is an internal type used by the
|
||||
// implementation of other goamz packages.
|
||||
type AttemptStrategy struct {
|
||||
Total time.Duration // total duration of attempt.
|
||||
Delay time.Duration // interval between each try in the burst.
|
||||
Min int // minimum number of retries; overrides Total
|
||||
}
|
||||
|
||||
type Attempt struct {
|
||||
strategy AttemptStrategy
|
||||
last time.Time
|
||||
end time.Time
|
||||
force bool
|
||||
count int
|
||||
}
|
||||
|
||||
// Start begins a new sequence of attempts for the given strategy.
|
||||
func (s AttemptStrategy) Start() *Attempt {
|
||||
now := time.Now()
|
||||
return &Attempt{
|
||||
strategy: s,
|
||||
last: now,
|
||||
end: now.Add(s.Total),
|
||||
force: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Next waits until it is time to perform the next attempt or returns
|
||||
// false if it is time to stop trying.
|
||||
func (a *Attempt) Next() bool {
|
||||
now := time.Now()
|
||||
sleep := a.nextSleep(now)
|
||||
if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count {
|
||||
return false
|
||||
}
|
||||
a.force = false
|
||||
if sleep > 0 && a.count > 0 {
|
||||
time.Sleep(sleep)
|
||||
now = time.Now()
|
||||
}
|
||||
a.count++
|
||||
a.last = now
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *Attempt) nextSleep(now time.Time) time.Duration {
|
||||
sleep := a.strategy.Delay - now.Sub(a.last)
|
||||
if sleep < 0 {
|
||||
return 0
|
||||
}
|
||||
return sleep
|
||||
}
|
||||
|
||||
// HasNext returns whether another attempt will be made if the current
|
||||
// one fails. If it returns true, the following call to Next is
|
||||
// guaranteed to return true.
|
||||
func (a *Attempt) HasNext() bool {
|
||||
if a.force || a.strategy.Min > a.count {
|
||||
return true
|
||||
}
|
||||
now := time.Now()
|
||||
if now.Add(a.nextSleep(now)).Before(a.end) {
|
||||
a.force = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
58
vendor/github.com/goamz/goamz/aws/attempt_test.go
сгенерированный
поставляемый
58
vendor/github.com/goamz/goamz/aws/attempt_test.go
сгенерированный
поставляемый
@@ -1,58 +0,0 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func (S) TestAttemptTiming(c *C) {
|
||||
testAttempt := aws.AttemptStrategy{
|
||||
Total: 0.25e9,
|
||||
Delay: 0.1e9,
|
||||
}
|
||||
want := []time.Duration{0, 0.1e9, 0.2e9, 0.2e9}
|
||||
got := make([]time.Duration, 0, len(want)) // avoid allocation when testing timing
|
||||
t0 := time.Now()
|
||||
for a := testAttempt.Start(); a.Next(); {
|
||||
got = append(got, time.Now().Sub(t0))
|
||||
}
|
||||
got = append(got, time.Now().Sub(t0))
|
||||
c.Assert(got, HasLen, len(want))
|
||||
const margin = 0.01e9
|
||||
for i, got := range want {
|
||||
lo := want[i] - margin
|
||||
hi := want[i] + margin
|
||||
if got < lo || got > hi {
|
||||
c.Errorf("attempt %d want %g got %g", i, want[i].Seconds(), got.Seconds())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (S) TestAttemptNextHasNext(c *C) {
|
||||
a := aws.AttemptStrategy{}.Start()
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.Next(), Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{}.Start()
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.HasNext(), Equals, false)
|
||||
c.Assert(a.Next(), Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{Total: 2e8}.Start()
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.HasNext(), Equals, true)
|
||||
time.Sleep(2e8)
|
||||
c.Assert(a.HasNext(), Equals, true)
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.Next(), Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{Total: 1e8, Min: 2}.Start()
|
||||
time.Sleep(1e8)
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.HasNext(), Equals, true)
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.HasNext(), Equals, false)
|
||||
c.Assert(a.Next(), Equals, false)
|
||||
}
|
||||
435
vendor/github.com/goamz/goamz/aws/aws.go
сгенерированный
поставляемый
435
vendor/github.com/goamz/goamz/aws/aws.go
сгенерированный
поставляемый
@@ -1,435 +0,0 @@
|
||||
//
|
||||
// goamz - Go packages to interact with the Amazon Web Services.
|
||||
//
|
||||
// https://wiki.ubuntu.com/goamz
|
||||
//
|
||||
// Copyright (c) 2011 Canonical Ltd.
|
||||
//
|
||||
// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
|
||||
//
|
||||
package aws
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/vaughan0/go-ini"
|
||||
)
|
||||
|
||||
// Defines the valid signers
|
||||
const (
|
||||
V2Signature = iota
|
||||
V4Signature = iota
|
||||
Route53Signature = iota
|
||||
)
|
||||
|
||||
// Defines the service endpoint and correct Signer implementation to use
|
||||
// to sign requests for this endpoint
|
||||
type ServiceInfo struct {
|
||||
Endpoint string
|
||||
Signer uint
|
||||
}
|
||||
|
||||
// Region defines the URLs where AWS services may be accessed.
|
||||
//
|
||||
// See http://goo.gl/d8BP1 for more details.
|
||||
type Region struct {
|
||||
Name string // the canonical name of this region.
|
||||
EC2Endpoint string
|
||||
S3Endpoint string
|
||||
S3BucketEndpoint string // Not needed by AWS S3. Use ${bucket} for bucket name.
|
||||
S3LocationConstraint bool // true if this region requires a LocationConstraint declaration.
|
||||
S3LowercaseBucket bool // true if the region requires bucket names to be lower case.
|
||||
SDBEndpoint string
|
||||
SESEndpoint string
|
||||
SNSEndpoint string
|
||||
SQSEndpoint string
|
||||
IAMEndpoint string
|
||||
ELBEndpoint string
|
||||
DynamoDBEndpoint string
|
||||
CloudWatchServicepoint ServiceInfo
|
||||
AutoScalingEndpoint string
|
||||
RDSEndpoint ServiceInfo
|
||||
STSEndpoint string
|
||||
CloudFormationEndpoint string
|
||||
ECSEndpoint string
|
||||
DynamoDBStreamsEndpoint string
|
||||
}
|
||||
|
||||
var Regions = map[string]Region{
|
||||
APNortheast.Name: APNortheast,
|
||||
APNortheast2.Name: APNortheast2,
|
||||
APSoutheast.Name: APSoutheast,
|
||||
APSoutheast2.Name: APSoutheast2,
|
||||
EUCentral.Name: EUCentral,
|
||||
EUWest.Name: EUWest,
|
||||
USEast.Name: USEast,
|
||||
USEast2.Name: USEast2,
|
||||
USWest.Name: USWest,
|
||||
USWest2.Name: USWest2,
|
||||
USGovWest.Name: USGovWest,
|
||||
SAEast.Name: SAEast,
|
||||
CNNorth.Name: CNNorth,
|
||||
}
|
||||
|
||||
// Designates a signer interface suitable for signing AWS requests, params
|
||||
// should be appropriately encoded for the request before signing.
|
||||
//
|
||||
// A signer should be initialized with Auth and the appropriate endpoint.
|
||||
type Signer interface {
|
||||
Sign(method, path string, params map[string]string)
|
||||
}
|
||||
|
||||
// An AWS Service interface with the API to query the AWS service
|
||||
//
|
||||
// Supplied as an easy way to mock out service calls during testing.
|
||||
type AWSService interface {
|
||||
// Queries the AWS service at a given method/path with the params and
|
||||
// returns an http.Response and error
|
||||
Query(method, path string, params map[string]string) (*http.Response, error)
|
||||
// Builds an error given an XML payload in the http.Response, can be used
|
||||
// to process an error if the status code is not 200 for example.
|
||||
BuildError(r *http.Response) error
|
||||
}
|
||||
|
||||
// Implements a Server Query/Post API to easily query AWS services and build
|
||||
// errors when desired
|
||||
type Service struct {
|
||||
service ServiceInfo
|
||||
signer Signer
|
||||
}
|
||||
|
||||
// Create a base set of params for an action
|
||||
func MakeParams(action string) map[string]string {
|
||||
params := make(map[string]string)
|
||||
params["Action"] = action
|
||||
return params
|
||||
}
|
||||
|
||||
// Create a new AWS server to handle making requests
|
||||
func NewService(auth Auth, service ServiceInfo) (s *Service, err error) {
|
||||
var signer Signer
|
||||
switch service.Signer {
|
||||
case V2Signature:
|
||||
signer, err = NewV2Signer(auth, service)
|
||||
// case V4Signature:
|
||||
// signer, err = NewV4Signer(auth, service, Regions["eu-west-1"])
|
||||
default:
|
||||
err = fmt.Errorf("Unsupported signer for service")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = &Service{service: service, signer: signer}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) Query(method, path string, params map[string]string) (resp *http.Response, err error) {
|
||||
params["Timestamp"] = time.Now().UTC().Format(time.RFC3339)
|
||||
u, err := url.Parse(s.service.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Path = path
|
||||
|
||||
s.signer.Sign(method, path, params)
|
||||
if method == "GET" {
|
||||
u.RawQuery = multimap(params).Encode()
|
||||
resp, err = http.Get(u.String())
|
||||
} else if method == "POST" {
|
||||
resp, err = http.PostForm(u.String(), multimap(params))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) BuildError(r *http.Response) error {
|
||||
errors := ErrorResponse{}
|
||||
xml.NewDecoder(r.Body).Decode(&errors)
|
||||
var err Error
|
||||
err = errors.Errors
|
||||
err.RequestId = errors.RequestId
|
||||
err.StatusCode = r.StatusCode
|
||||
if err.Message == "" {
|
||||
err.Message = r.Status
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Errors Error `xml:"Error"`
|
||||
RequestId string // A unique ID for tracking the request
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
StatusCode int
|
||||
Type string
|
||||
Code string
|
||||
Message string
|
||||
RequestId string
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
return fmt.Sprintf("Type: %s, Code: %s, Message: %s",
|
||||
err.Type, err.Code, err.Message,
|
||||
)
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
AccessKey, SecretKey string
|
||||
token string
|
||||
expiration time.Time
|
||||
}
|
||||
|
||||
func (a *Auth) Token() string {
|
||||
if a.token == "" {
|
||||
return ""
|
||||
}
|
||||
if time.Since(a.expiration) >= -30*time.Second { //in an ideal world this should be zero assuming the instance is synching it's clock
|
||||
*a, _ = GetAuth("", "", "", time.Time{})
|
||||
}
|
||||
return a.token
|
||||
}
|
||||
|
||||
func (a *Auth) Expiration() time.Time {
|
||||
return a.expiration
|
||||
}
|
||||
|
||||
// To be used with other APIs that return auth credentials such as STS
|
||||
func NewAuth(accessKey, secretKey, token string, expiration time.Time) *Auth {
|
||||
return &Auth{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
token: token,
|
||||
expiration: expiration,
|
||||
}
|
||||
}
|
||||
|
||||
// ResponseMetadata
|
||||
type ResponseMetadata struct {
|
||||
RequestId string // A unique ID for tracking the request
|
||||
}
|
||||
|
||||
type BaseResponse struct {
|
||||
ResponseMetadata ResponseMetadata
|
||||
}
|
||||
|
||||
var unreserved = make([]bool, 128)
|
||||
var hex = "0123456789ABCDEF"
|
||||
|
||||
func init() {
|
||||
// RFC3986
|
||||
u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
|
||||
for _, c := range u {
|
||||
unreserved[c] = true
|
||||
}
|
||||
}
|
||||
|
||||
func multimap(p map[string]string) url.Values {
|
||||
q := make(url.Values, len(p))
|
||||
for k, v := range p {
|
||||
q[k] = []string{v}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
type credentials struct {
|
||||
Code string
|
||||
LastUpdated string
|
||||
Type string
|
||||
AccessKeyId string
|
||||
SecretAccessKey string
|
||||
Token string
|
||||
Expiration string
|
||||
}
|
||||
|
||||
// GetMetaData retrieves instance metadata about the current machine.
|
||||
//
|
||||
// See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html for more details.
|
||||
func GetMetaData(path string) (contents []byte, err error) {
|
||||
url := "http://169.254.169.254/latest/meta-data/" + path
|
||||
|
||||
resp, err := RetryingClient.Get(url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("Code %d returned for url %s", resp.StatusCode, url)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return []byte(body), err
|
||||
}
|
||||
|
||||
func getInstanceCredentials() (cred credentials, err error) {
|
||||
credentialPath := "iam/security-credentials/"
|
||||
|
||||
// Get the instance role
|
||||
role, err := GetMetaData(credentialPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the instance role credentials
|
||||
credentialJSON, err := GetMetaData(credentialPath + string(role))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(credentialJSON), &cred)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAuth creates an Auth based on either passed in credentials,
|
||||
// environment information or instance based role credentials.
|
||||
func GetAuth(accessKey string, secretKey, token string, expiration time.Time) (auth Auth, err error) {
|
||||
// First try passed in credentials
|
||||
if accessKey != "" && secretKey != "" {
|
||||
return Auth{accessKey, secretKey, token, expiration}, nil
|
||||
}
|
||||
|
||||
// Next try to get auth from the shared credentials file
|
||||
auth, err = SharedAuth()
|
||||
if err == nil {
|
||||
// Found auth, return
|
||||
return
|
||||
}
|
||||
|
||||
// Next try to get auth from the environment
|
||||
auth, err = EnvAuth()
|
||||
if err == nil {
|
||||
// Found auth, return
|
||||
return
|
||||
}
|
||||
|
||||
// Next try getting auth from the instance role
|
||||
cred, err := getInstanceCredentials()
|
||||
if err == nil {
|
||||
// Found auth, return
|
||||
auth.AccessKey = cred.AccessKeyId
|
||||
auth.SecretKey = cred.SecretAccessKey
|
||||
auth.token = cred.Token
|
||||
exptdate, err := time.Parse("2006-01-02T15:04:05Z", cred.Expiration)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error Parseing expiration date: cred.Expiration :%s , error: %s \n", cred.Expiration, err)
|
||||
}
|
||||
auth.expiration = exptdate
|
||||
return auth, err
|
||||
}
|
||||
err = errors.New("No valid AWS authentication found")
|
||||
return auth, err
|
||||
}
|
||||
|
||||
// EnvAuth creates an Auth based on environment information.
|
||||
// The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment
|
||||
// variables are used.
|
||||
// AWS_SESSION_TOKEN is used if present.
|
||||
func EnvAuth() (auth Auth, err error) {
|
||||
auth.AccessKey = os.Getenv("AWS_ACCESS_KEY_ID")
|
||||
if auth.AccessKey == "" {
|
||||
auth.AccessKey = os.Getenv("AWS_ACCESS_KEY")
|
||||
}
|
||||
|
||||
auth.SecretKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
|
||||
if auth.SecretKey == "" {
|
||||
auth.SecretKey = os.Getenv("AWS_SECRET_KEY")
|
||||
}
|
||||
if auth.AccessKey == "" {
|
||||
err = errors.New("AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
|
||||
}
|
||||
if auth.SecretKey == "" {
|
||||
err = errors.New("AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
|
||||
}
|
||||
|
||||
auth.token = os.Getenv("AWS_SESSION_TOKEN")
|
||||
return
|
||||
}
|
||||
|
||||
// SharedAuth creates an Auth based on shared credentials stored in
|
||||
// $HOME/.aws/credentials. The AWS_PROFILE environment variables is used to
|
||||
// select the profile.
|
||||
func SharedAuth() (auth Auth, err error) {
|
||||
var profileName = os.Getenv("AWS_PROFILE")
|
||||
|
||||
if profileName == "" {
|
||||
profileName = "default"
|
||||
}
|
||||
|
||||
var credentialsFile = os.Getenv("AWS_CREDENTIAL_FILE")
|
||||
if credentialsFile == "" {
|
||||
var homeDir = os.Getenv("HOME")
|
||||
if homeDir == "" {
|
||||
err = errors.New("Could not get HOME")
|
||||
return
|
||||
}
|
||||
credentialsFile = homeDir + "/.aws/credentials"
|
||||
}
|
||||
|
||||
file, err := ini.LoadFile(credentialsFile)
|
||||
if err != nil {
|
||||
err = errors.New("Couldn't parse AWS credentials file")
|
||||
return
|
||||
}
|
||||
|
||||
var profile = file[profileName]
|
||||
if profile == nil {
|
||||
err = errors.New("Couldn't find profile in AWS credentials file")
|
||||
return
|
||||
}
|
||||
|
||||
auth.AccessKey = profile["aws_access_key_id"]
|
||||
auth.SecretKey = profile["aws_secret_access_key"]
|
||||
auth.token = profile["aws_session_token"]
|
||||
|
||||
if auth.AccessKey == "" {
|
||||
err = errors.New("AWS_ACCESS_KEY_ID not found in environment in credentials file")
|
||||
}
|
||||
if auth.SecretKey == "" {
|
||||
err = errors.New("AWS_SECRET_ACCESS_KEY not found in credentials file")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Encode takes a string and URI-encodes it in a way suitable
|
||||
// to be used in AWS signatures.
|
||||
func Encode(s string) string {
|
||||
encode := false
|
||||
for i := 0; i != len(s); i++ {
|
||||
c := s[i]
|
||||
if c > 127 || !unreserved[c] {
|
||||
encode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !encode {
|
||||
return s
|
||||
}
|
||||
e := make([]byte, len(s)*3)
|
||||
ei := 0
|
||||
for i := 0; i != len(s); i++ {
|
||||
c := s[i]
|
||||
if c > 127 || !unreserved[c] {
|
||||
e[ei] = '%'
|
||||
e[ei+1] = hex[c>>4]
|
||||
e[ei+2] = hex[c&0xF]
|
||||
ei += 3
|
||||
} else {
|
||||
e[ei] = c
|
||||
ei += 1
|
||||
}
|
||||
}
|
||||
return string(e[:ei])
|
||||
}
|
||||
211
vendor/github.com/goamz/goamz/aws/aws_test.go
сгенерированный
поставляемый
211
vendor/github.com/goamz/goamz/aws/aws_test.go
сгенерированный
поставляемый
@@ -1,211 +0,0 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
type S struct {
|
||||
environ []string
|
||||
}
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
s.environ = os.Environ()
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
os.Clearenv()
|
||||
for _, kv := range s.environ {
|
||||
l := strings.SplitN(kv, "=", 2)
|
||||
os.Setenv(l[0], l[1])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthNoHome(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "foo")
|
||||
_, err := aws.SharedAuth()
|
||||
c.Assert(err, ErrorMatches, "Could not get HOME")
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthNoCredentialsFile(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "foo")
|
||||
os.Setenv("HOME", "/tmp")
|
||||
_, err := aws.SharedAuth()
|
||||
c.Assert(err, ErrorMatches, "Couldn't parse AWS credentials file")
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthNoProfileInFile(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "foo")
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = os.Mkdir(d+"/.aws", 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\n"), 0644)
|
||||
os.Setenv("HOME", d)
|
||||
|
||||
_, err = aws.SharedAuth()
|
||||
c.Assert(err, ErrorMatches, "Couldn't find profile in AWS credentials file")
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthNoKeysInProfile(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "bar")
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = os.Mkdir(d+"/.aws", 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\nawsaccesskeyid = AK.."), 0644)
|
||||
os.Setenv("HOME", d)
|
||||
|
||||
_, err = aws.SharedAuth()
|
||||
c.Assert(err, ErrorMatches, "AWS_SECRET_ACCESS_KEY not found in credentials file")
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthDefaultCredentials(c *C) {
|
||||
os.Clearenv()
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = os.Mkdir(d+"/.aws", 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(d+"/.aws/credentials", []byte("[default]\naws_access_key_id = access\naws_secret_access_key = secret\n"), 0644)
|
||||
os.Setenv("HOME", d)
|
||||
|
||||
auth, err := aws.SharedAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuth(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "bar")
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = os.Mkdir(d+"/.aws", 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\naws_access_key_id = access\naws_secret_access_key = secret\n"), 0644)
|
||||
os.Setenv("HOME", d)
|
||||
|
||||
auth, err := aws.SharedAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthNoSecret(c *C) {
|
||||
os.Clearenv()
|
||||
_, err := aws.EnvAuth()
|
||||
c.Assert(err, ErrorMatches, "AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthNoAccess(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "foo")
|
||||
_, err := aws.EnvAuth()
|
||||
c.Assert(err, ErrorMatches, "AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuth(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||
auth, err := aws.EnvAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthAlt(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY", "access")
|
||||
auth, err := aws.EnvAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthToken(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY", "access")
|
||||
os.Setenv("AWS_SESSION_TOKEN", "token")
|
||||
auth, err := aws.EnvAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth.SecretKey, Equals, "secret")
|
||||
c.Assert(auth.AccessKey, Equals, "access")
|
||||
c.Assert(auth.Token(), Equals, "token")
|
||||
}
|
||||
|
||||
func (s *S) TestGetAuthStatic(c *C) {
|
||||
exptdate := time.Now().Add(time.Hour)
|
||||
auth, err := aws.GetAuth("access", "secret", "token", exptdate)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth.AccessKey, Equals, "access")
|
||||
c.Assert(auth.SecretKey, Equals, "secret")
|
||||
c.Assert(auth.Token(), Equals, "token")
|
||||
c.Assert(auth.Expiration(), Equals, exptdate)
|
||||
}
|
||||
|
||||
func (s *S) TestGetAuthEnv(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||
auth, err := aws.GetAuth("", "", "", time.Time{})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEncode(c *C) {
|
||||
c.Assert(aws.Encode("foo"), Equals, "foo")
|
||||
c.Assert(aws.Encode("/"), Equals, "%2F")
|
||||
}
|
||||
|
||||
func (s *S) TestRegionsAreNamed(c *C) {
|
||||
for n, r := range aws.Regions {
|
||||
c.Assert(n, Equals, r.Name)
|
||||
}
|
||||
}
|
||||
124
vendor/github.com/goamz/goamz/aws/client.go
сгенерированный
поставляемый
124
vendor/github.com/goamz/goamz/aws/client.go
сгенерированный
поставляемый
@@ -1,124 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RetryableFunc func(*http.Request, *http.Response, error) bool
|
||||
type WaitFunc func(try int)
|
||||
type DeadlineFunc func() time.Time
|
||||
|
||||
type ResilientTransport struct {
|
||||
// Timeout is the maximum amount of time a dial will wait for
|
||||
// a connect to complete.
|
||||
//
|
||||
// The default is no timeout.
|
||||
//
|
||||
// With or without a timeout, the operating system may impose
|
||||
// its own earlier timeout. For instance, TCP timeouts are
|
||||
// often around 3 minutes.
|
||||
DialTimeout time.Duration
|
||||
|
||||
// MaxTries, if non-zero, specifies the number of times we will retry on
|
||||
// failure. Retries are only attempted for temporary network errors or known
|
||||
// safe failures.
|
||||
MaxTries int
|
||||
Deadline DeadlineFunc
|
||||
ShouldRetry RetryableFunc
|
||||
Wait WaitFunc
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
// Convenience method for creating an http client
|
||||
func NewClient(rt *ResilientTransport) *http.Client {
|
||||
rt.transport = &http.Transport{
|
||||
Dial: func(netw, addr string) (net.Conn, error) {
|
||||
c, err := net.DialTimeout(netw, addr, rt.DialTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.SetDeadline(rt.Deadline())
|
||||
return c, nil
|
||||
},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
// TODO: Would be nice is ResilientTransport allowed clients to initialize
|
||||
// with http.Transport attributes.
|
||||
return &http.Client{
|
||||
Transport: rt,
|
||||
}
|
||||
}
|
||||
|
||||
var retryingTransport = &ResilientTransport{
|
||||
Deadline: func() time.Time {
|
||||
return time.Now().Add(5 * time.Second)
|
||||
},
|
||||
DialTimeout: 10 * time.Second,
|
||||
MaxTries: 3,
|
||||
ShouldRetry: awsRetry,
|
||||
Wait: ExpBackoff,
|
||||
}
|
||||
|
||||
// Exported default client
|
||||
var RetryingClient = NewClient(retryingTransport)
|
||||
|
||||
func (t *ResilientTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t.tries(req)
|
||||
}
|
||||
|
||||
// Retry a request a maximum of t.MaxTries times.
|
||||
// We'll only retry if the proper criteria are met.
|
||||
// If a wait function is specified, wait that amount of time
|
||||
// In between requests.
|
||||
func (t *ResilientTransport) tries(req *http.Request) (res *http.Response, err error) {
|
||||
for try := 0; try < t.MaxTries; try += 1 {
|
||||
res, err = t.transport.RoundTrip(req)
|
||||
|
||||
if !t.ShouldRetry(req, res, err) {
|
||||
break
|
||||
}
|
||||
if res != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
if t.Wait != nil {
|
||||
t.Wait(try)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ExpBackoff(try int) {
|
||||
time.Sleep(100 * time.Millisecond *
|
||||
time.Duration(math.Exp2(float64(try))))
|
||||
}
|
||||
|
||||
func LinearBackoff(try int) {
|
||||
time.Sleep(time.Duration(try*100) * time.Millisecond)
|
||||
}
|
||||
|
||||
// Decide if we should retry a request.
|
||||
// In general, the criteria for retrying a request is described here
|
||||
// http://docs.aws.amazon.com/general/latest/gr/api-retries.html
|
||||
func awsRetry(req *http.Request, res *http.Response, err error) bool {
|
||||
retry := false
|
||||
|
||||
// Retry if there's a temporary network error.
|
||||
if neterr, ok := err.(net.Error); ok {
|
||||
if neterr.Temporary() {
|
||||
retry = true
|
||||
}
|
||||
}
|
||||
|
||||
// Retry if we get a 5xx series error.
|
||||
if res != nil {
|
||||
if res.StatusCode >= 500 && res.StatusCode < 600 {
|
||||
retry = true
|
||||
}
|
||||
}
|
||||
|
||||
return retry
|
||||
}
|
||||
121
vendor/github.com/goamz/goamz/aws/client_test.go
сгенерированный
поставляемый
121
vendor/github.com/goamz/goamz/aws/client_test.go
сгенерированный
поставляемый
@@ -1,121 +0,0 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Retrieve the response from handler using aws.RetryingClient
|
||||
func serveAndGet(handler http.HandlerFunc) (body string, err error) {
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
resp, err := aws.RetryingClient.Get(ts.URL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("Bad status code: %d", resp.StatusCode)
|
||||
}
|
||||
greeting, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return strings.TrimSpace(string(greeting)), nil
|
||||
}
|
||||
|
||||
func TestClient_expected(t *testing.T) {
|
||||
body := "foo bar"
|
||||
|
||||
resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, body)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp != body {
|
||||
t.Fatal("Body not as expected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_delay(t *testing.T) {
|
||||
body := "baz"
|
||||
wait := 4
|
||||
resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
if wait < 0 {
|
||||
// If we dipped to zero delay and still failed.
|
||||
t.Fatal("Never succeeded.")
|
||||
}
|
||||
wait -= 1
|
||||
time.Sleep(time.Second * time.Duration(wait))
|
||||
fmt.Fprintln(w, body)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp != body {
|
||||
t.Fatal("Body not as expected.", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_no4xxRetry(t *testing.T) {
|
||||
tries := 0
|
||||
|
||||
// Fail once before succeeding.
|
||||
_, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
tries += 1
|
||||
http.Error(w, "error", 404)
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
if tries != 1 {
|
||||
t.Fatalf("should only try once: %d", tries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_retries(t *testing.T) {
|
||||
body := "biz"
|
||||
failed := false
|
||||
// Fail once before succeeding.
|
||||
resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !failed {
|
||||
http.Error(w, "error", 500)
|
||||
failed = true
|
||||
} else {
|
||||
fmt.Fprintln(w, body)
|
||||
}
|
||||
})
|
||||
if failed != true {
|
||||
t.Error("We didn't retry!")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp != body {
|
||||
t.Fatal("Body not as expected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_fails(t *testing.T) {
|
||||
tries := 0
|
||||
// Fail 3 times and return the last error.
|
||||
_, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
tries += 1
|
||||
http.Error(w, "error", 500)
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tries != 3 {
|
||||
t.Fatal("Didn't retry enough")
|
||||
}
|
||||
}
|
||||
29
vendor/github.com/goamz/goamz/aws/export_test.go
сгенерированный
поставляемый
29
vendor/github.com/goamz/goamz/aws/export_test.go
сгенерированный
поставляемый
@@ -1,29 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// V4Signer:
|
||||
// Exporting methods for testing
|
||||
|
||||
func (s *V4Signer) RequestTime(req *http.Request) time.Time {
|
||||
return s.requestTime(req)
|
||||
}
|
||||
|
||||
func (s *V4Signer) CanonicalRequest(req *http.Request) string {
|
||||
return s.canonicalRequest(req)
|
||||
}
|
||||
|
||||
func (s *V4Signer) StringToSign(t time.Time, creq string) string {
|
||||
return s.stringToSign(t, creq)
|
||||
}
|
||||
|
||||
func (s *V4Signer) Signature(t time.Time, sts string) string {
|
||||
return s.signature(t, sts)
|
||||
}
|
||||
|
||||
func (s *V4Signer) Authorization(header http.Header, t time.Time, signature string) string {
|
||||
return s.authorization(header, t, signature)
|
||||
}
|
||||
300
vendor/github.com/goamz/goamz/aws/regions.go
сгенерированный
поставляемый
300
vendor/github.com/goamz/goamz/aws/regions.go
сгенерированный
поставляемый
@@ -1,300 +0,0 @@
|
||||
package aws
|
||||
|
||||
var USGovWest = Region{
|
||||
"us-gov-west-1",
|
||||
"https://ec2.us-gov-west-1.amazonaws.com",
|
||||
"https://s3-fips-us-gov-west-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"",
|
||||
"",
|
||||
"https://sns.us-gov-west-1.amazonaws.com",
|
||||
"https://sqs.us-gov-west-1.amazonaws.com",
|
||||
"https://iam.us-gov.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-gov-west-1.amazonaws.com",
|
||||
"https://dynamodb.us-gov-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-gov-west-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-gov-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-gov-west-1.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-gov-west-1.amazonaws.com",
|
||||
"https://ecs.us-gov-west-1.amazonaws.com",
|
||||
"https://streams.dynamodb.us-gov-west-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var USEast = Region{
|
||||
"us-east-1",
|
||||
"https://ec2.us-east-1.amazonaws.com",
|
||||
"https://s3.amazonaws.com",
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
"https://sdb.amazonaws.com",
|
||||
"https://email.us-east-1.amazonaws.com",
|
||||
"https://sns.us-east-1.amazonaws.com",
|
||||
"https://sqs.us-east-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-east-1.amazonaws.com",
|
||||
"https://dynamodb.us-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-east-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-east-1.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-east-1.amazonaws.com",
|
||||
"https://ecs.us-east-1.amazonaws.com",
|
||||
"https://streams.dynamodb.us-east-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var USEast2 = Region{
|
||||
"us-east-2",
|
||||
"https://ec2.us-east-2.amazonaws.com",
|
||||
"https://s3.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"",
|
||||
"",
|
||||
"https://sns.us-east-2.amazonaws.com",
|
||||
"https://sqs.us-east-2.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-east-2.amazonaws.com",
|
||||
"https://dynamodb.us-east-2.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-east-2.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-east-2.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-east-2.amazonaws.com",
|
||||
"https://ecs.us-east-2.amazonaws.com",
|
||||
"https://streams.dynamodb.us-east-2.amazonaws.com",
|
||||
}
|
||||
|
||||
var USWest = Region{
|
||||
"us-west-1",
|
||||
"https://ec2.us-west-1.amazonaws.com",
|
||||
"https://s3-us-west-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.us-west-1.amazonaws.com",
|
||||
"",
|
||||
"https://sns.us-west-1.amazonaws.com",
|
||||
"https://sqs.us-west-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-west-1.amazonaws.com",
|
||||
"https://dynamodb.us-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-west-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-west-1.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-west-1.amazonaws.com",
|
||||
"https://ecs.us-west-1.amazonaws.com",
|
||||
"https://streams.dynamodb.us-west-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var USWest2 = Region{
|
||||
"us-west-2",
|
||||
"https://ec2.us-west-2.amazonaws.com",
|
||||
"https://s3-us-west-2.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.us-west-2.amazonaws.com",
|
||||
"https://email.us-west-2.amazonaws.com",
|
||||
"https://sns.us-west-2.amazonaws.com",
|
||||
"https://sqs.us-west-2.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-west-2.amazonaws.com",
|
||||
"https://dynamodb.us-west-2.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-west-2.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-west-2.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-west-2.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-west-2.amazonaws.com",
|
||||
"https://ecs.us-west-2.amazonaws.com",
|
||||
"https://streams.dynamodb.us-west-2.amazonaws.com",
|
||||
}
|
||||
|
||||
var EUWest = Region{
|
||||
"eu-west-1",
|
||||
"https://ec2.eu-west-1.amazonaws.com",
|
||||
"https://s3-eu-west-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.eu-west-1.amazonaws.com",
|
||||
"https://email.eu-west-1.amazonaws.com",
|
||||
"https://sns.eu-west-1.amazonaws.com",
|
||||
"https://sqs.eu-west-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.eu-west-1.amazonaws.com",
|
||||
"https://dynamodb.eu-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.eu-west-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.eu-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.eu-west-1.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.eu-west-1.amazonaws.com",
|
||||
"https://ecs.eu-west-1.amazonaws.com",
|
||||
"https://streams.dynamodb.eu-west-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var EUCentral = Region{
|
||||
"eu-central-1",
|
||||
"https://ec2.eu-central-1.amazonaws.com",
|
||||
"https://s3-eu-central-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.eu-central-1.amazonaws.com",
|
||||
"https://email.eu-central-1.amazonaws.com",
|
||||
"https://sns.eu-central-1.amazonaws.com",
|
||||
"https://sqs.eu-central-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.eu-central-1.amazonaws.com",
|
||||
"https://dynamodb.eu-central-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.eu-central-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.eu-central-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.eu-central-1.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.eu-central-1.amazonaws.com",
|
||||
"https://ecs.eu-central-1.amazonaws.com",
|
||||
"https://streams.dynamodb.eu-central-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var APSoutheast = Region{
|
||||
"ap-southeast-1",
|
||||
"https://ec2.ap-southeast-1.amazonaws.com",
|
||||
"https://s3-ap-southeast-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.ap-southeast-1.amazonaws.com",
|
||||
"",
|
||||
"https://sns.ap-southeast-1.amazonaws.com",
|
||||
"https://sqs.ap-southeast-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.ap-southeast-1.amazonaws.com",
|
||||
"https://dynamodb.ap-southeast-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.ap-southeast-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.ap-southeast-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.ap-southeast-1.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.ap-southeast-1.amazonaws.com",
|
||||
"https://ecs.ap-southeast-1.amazonaws.com",
|
||||
"https://streams.dynamodb.ap-southeast-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var APSoutheast2 = Region{
|
||||
"ap-southeast-2",
|
||||
"https://ec2.ap-southeast-2.amazonaws.com",
|
||||
"https://s3-ap-southeast-2.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.ap-southeast-2.amazonaws.com",
|
||||
"",
|
||||
"https://sns.ap-southeast-2.amazonaws.com",
|
||||
"https://sqs.ap-southeast-2.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.ap-southeast-2.amazonaws.com",
|
||||
"https://dynamodb.ap-southeast-2.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.ap-southeast-2.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.ap-southeast-2.amazonaws.com",
|
||||
ServiceInfo{"https://rds.ap-southeast-2.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.ap-southeast-2.amazonaws.com",
|
||||
"https://ecs.ap-southeast-2.amazonaws.com",
|
||||
"https://streams.dynamodb.ap-southeast-2.amazonaws.com",
|
||||
}
|
||||
|
||||
var APNortheast = Region{
|
||||
"ap-northeast-1",
|
||||
"https://ec2.ap-northeast-1.amazonaws.com",
|
||||
"https://s3-ap-northeast-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.ap-northeast-1.amazonaws.com",
|
||||
"",
|
||||
"https://sns.ap-northeast-1.amazonaws.com",
|
||||
"https://sqs.ap-northeast-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.ap-northeast-1.amazonaws.com",
|
||||
"https://dynamodb.ap-northeast-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.ap-northeast-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.ap-northeast-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.ap-northeast-1.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.ap-northeast-1.amazonaws.com",
|
||||
"https://ecs.ap-northeast-1.amazonaws.com",
|
||||
"https://streams.dynamodb.ap-northeast-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var APNortheast2 = Region{
|
||||
"ap-northeast-2",
|
||||
"https://ec2.ap-northeast-2.amazonaws.com",
|
||||
"https://s3-ap-northeast-2.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.ap-northeast-2.amazonaws.com",
|
||||
"",
|
||||
"https://sns.ap-northeast-2.amazonaws.com",
|
||||
"https://sqs.ap-northeast-2.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.ap-northeast-2.amazonaws.com",
|
||||
"https://dynamodb.ap-northeast-2.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.ap-northeast-2.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.ap-northeast-2.amazonaws.com",
|
||||
ServiceInfo{"https://rds.ap-northeast-2.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.ap-northeast-2.amazonaws.com",
|
||||
"https://ecs.ap-northeast-2.amazonaws.com",
|
||||
"https://streams.dynamodb.ap-northeast-2.amazonaws.com",
|
||||
}
|
||||
|
||||
var SAEast = Region{
|
||||
"sa-east-1",
|
||||
"https://ec2.sa-east-1.amazonaws.com",
|
||||
"https://s3-sa-east-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.sa-east-1.amazonaws.com",
|
||||
"",
|
||||
"https://sns.sa-east-1.amazonaws.com",
|
||||
"https://sqs.sa-east-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.sa-east-1.amazonaws.com",
|
||||
"https://dynamodb.sa-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.sa-east-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.sa-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.sa-east-1.amazonaws.com", V2Signature},
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.sa-east-1.amazonaws.com",
|
||||
"https://ecs.sa-east-1.amazonaws.com",
|
||||
"https://streams.dynamodb.sa-east-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var CNNorth = Region{
|
||||
"cn-north-1",
|
||||
"https://ec2.cn-north-1.amazonaws.com.cn",
|
||||
"https://s3.cn-north-1.amazonaws.com.cn",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.cn-north-1.amazonaws.com.cn",
|
||||
"",
|
||||
"https://sns.cn-north-1.amazonaws.com.cn",
|
||||
"https://sqs.cn-north-1.amazonaws.com.cn",
|
||||
"https://iam.cn-north-1.amazonaws.com.cn",
|
||||
"https://elasticloadbalancing.cn-north-1.amazonaws.com.cn",
|
||||
"https://dynamodb.cn-north-1.amazonaws.com.cn",
|
||||
ServiceInfo{"https://monitoring.cn-north-1.amazonaws.com.cn", V4Signature},
|
||||
"https://autoscaling.cn-north-1.amazonaws.com.cn",
|
||||
ServiceInfo{"https://rds.cn-north-1.amazonaws.com.cn", V4Signature},
|
||||
"https://sts.cn-north-1.amazonaws.com.cn",
|
||||
"https://cloudformation.cn-north-1.amazonaws.com.cn",
|
||||
"https://ecs.cn-north-1.amazonaws.com.cn",
|
||||
"https://streams.dynamodb.cn-north-1.amazonaws.com.cn",
|
||||
}
|
||||
357
vendor/github.com/goamz/goamz/aws/sign.go
сгенерированный
поставляемый
357
vendor/github.com/goamz/goamz/aws/sign.go
сгенерированный
поставляемый
@@ -1,357 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type V2Signer struct {
|
||||
auth Auth
|
||||
service ServiceInfo
|
||||
host string
|
||||
}
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
func NewV2Signer(auth Auth, service ServiceInfo) (*V2Signer, error) {
|
||||
u, err := url.Parse(service.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &V2Signer{auth: auth, service: service, host: u.Host}, nil
|
||||
}
|
||||
|
||||
func (s *V2Signer) Sign(method, path string, params map[string]string) {
|
||||
params["AWSAccessKeyId"] = s.auth.AccessKey
|
||||
params["SignatureVersion"] = "2"
|
||||
params["SignatureMethod"] = "HmacSHA256"
|
||||
if s.auth.Token() != "" {
|
||||
params["SecurityToken"] = s.auth.Token()
|
||||
}
|
||||
|
||||
// AWS specifies that the parameters in a signed request must
|
||||
// be provided in the natural order of the keys. This is distinct
|
||||
// from the natural order of the encoded value of key=value.
|
||||
// Percent and Equals affect the sorting order.
|
||||
var keys, sarray []string
|
||||
for k, _ := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
sarray = append(sarray, Encode(k)+"="+Encode(params[k]))
|
||||
}
|
||||
joined := strings.Join(sarray, "&")
|
||||
payload := method + "\n" + s.host + "\n" + path + "\n" + joined
|
||||
hash := hmac.New(sha256.New, []byte(s.auth.SecretKey))
|
||||
hash.Write([]byte(payload))
|
||||
signature := make([]byte, b64.EncodedLen(hash.Size()))
|
||||
b64.Encode(signature, hash.Sum(nil))
|
||||
|
||||
params["Signature"] = string(signature)
|
||||
}
|
||||
|
||||
// Common date formats for signing requests
|
||||
const (
|
||||
ISO8601BasicFormat = "20060102T150405Z"
|
||||
ISO8601BasicFormatShort = "20060102"
|
||||
)
|
||||
|
||||
type Route53Signer struct {
|
||||
auth Auth
|
||||
}
|
||||
|
||||
func NewRoute53Signer(auth Auth) *Route53Signer {
|
||||
return &Route53Signer{auth: auth}
|
||||
}
|
||||
|
||||
// getCurrentDate fetches the date stamp from the aws servers to
|
||||
// ensure the auth headers are within 5 minutes of the server time
|
||||
func (s *Route53Signer) getCurrentDate() string {
|
||||
response, err := http.Get("https://route53.amazonaws.com/date")
|
||||
if err != nil {
|
||||
fmt.Print("Unable to get date from amazon: ", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
response.Body.Close()
|
||||
return response.Header.Get("Date")
|
||||
}
|
||||
|
||||
// Creates the authorize signature based on the date stamp and secret key
|
||||
func (s *Route53Signer) getHeaderAuthorize(message string) string {
|
||||
hmacSha256 := hmac.New(sha256.New, []byte(s.auth.SecretKey))
|
||||
hmacSha256.Write([]byte(message))
|
||||
cryptedString := hmacSha256.Sum(nil)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(cryptedString)
|
||||
}
|
||||
|
||||
// Adds all the required headers for AWS Route53 API to the request
|
||||
// including the authorization
|
||||
func (s *Route53Signer) Sign(req *http.Request) {
|
||||
date := s.getCurrentDate()
|
||||
authHeader := fmt.Sprintf("AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=%s,Signature=%s",
|
||||
s.auth.AccessKey, "HmacSHA256", s.getHeaderAuthorize(date))
|
||||
|
||||
req.Header.Set("Host", req.Host)
|
||||
req.Header.Set("X-Amzn-Authorization", authHeader)
|
||||
req.Header.Set("X-Amz-Date", date)
|
||||
req.Header.Set("Content-Type", "application/xml")
|
||||
}
|
||||
|
||||
/*
|
||||
The V4Signer encapsulates all of the functionality to sign a request with the AWS
|
||||
Signature Version 4 Signing Process. (http://goo.gl/u1OWZz)
|
||||
*/
|
||||
type V4Signer struct {
|
||||
auth Auth
|
||||
serviceName string
|
||||
region Region
|
||||
}
|
||||
|
||||
/*
|
||||
Return a new instance of a V4Signer capable of signing AWS requests.
|
||||
*/
|
||||
func NewV4Signer(auth Auth, serviceName string, region Region) *V4Signer {
|
||||
return &V4Signer{auth: auth, serviceName: serviceName, region: region}
|
||||
}
|
||||
|
||||
/*
|
||||
Sign a request according to the AWS Signature Version 4 Signing Process. (http://goo.gl/u1OWZz)
|
||||
|
||||
The signed request will include an "x-amz-date" header with a current timestamp if a valid "x-amz-date"
|
||||
or "date" header was not available in the original request. In addition, AWS Signature Version 4 requires
|
||||
the "host" header to be a signed header, therefor the Sign method will manually set a "host" header from
|
||||
the request.Host.
|
||||
|
||||
The signed request will include a new "Authorization" header indicating that the request has been signed.
|
||||
|
||||
Any changes to the request after signing the request will invalidate the signature.
|
||||
*/
|
||||
func (s *V4Signer) Sign(req *http.Request) {
|
||||
req.Header.Set("host", req.Host) // host header must be included as a signed header
|
||||
t := s.requestTime(req) // Get requst time
|
||||
creq := s.canonicalRequest(req) // Build canonical request
|
||||
sts := s.stringToSign(t, creq) // Build string to sign
|
||||
signature := s.signature(t, sts) // Calculate the AWS Signature Version 4
|
||||
auth := s.authorization(req.Header, t, signature) // Create Authorization header value
|
||||
req.Header.Set("Authorization", auth) // Add Authorization header to request
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
requestTime method will parse the time from the request "x-amz-date" or "date" headers.
|
||||
If the "x-amz-date" header is present, that will take priority over the "date" header.
|
||||
If neither header is defined or we are unable to parse either header as a valid date
|
||||
then we will create a new "x-amz-date" header with the current time.
|
||||
*/
|
||||
func (s *V4Signer) requestTime(req *http.Request) time.Time {
|
||||
|
||||
// Get "x-amz-date" header
|
||||
date := req.Header.Get("x-amz-date")
|
||||
|
||||
// Attempt to parse as ISO8601BasicFormat
|
||||
t, err := time.Parse(ISO8601BasicFormat, date)
|
||||
if err == nil {
|
||||
return t
|
||||
}
|
||||
|
||||
// Attempt to parse as http.TimeFormat
|
||||
t, err = time.Parse(http.TimeFormat, date)
|
||||
if err == nil {
|
||||
req.Header.Set("x-amz-date", t.Format(ISO8601BasicFormat))
|
||||
return t
|
||||
}
|
||||
|
||||
// Get "date" header
|
||||
date = req.Header.Get("date")
|
||||
|
||||
// Attempt to parse as http.TimeFormat
|
||||
t, err = time.Parse(http.TimeFormat, date)
|
||||
if err == nil {
|
||||
return t
|
||||
}
|
||||
|
||||
// Create a current time header to be used
|
||||
t = time.Now().UTC()
|
||||
req.Header.Set("x-amz-date", t.Format(ISO8601BasicFormat))
|
||||
return t
|
||||
}
|
||||
|
||||
/*
|
||||
canonicalRequest method creates the canonical request according to Task 1 of the AWS Signature Version 4 Signing Process. (http://goo.gl/eUUZ3S)
|
||||
|
||||
CanonicalRequest =
|
||||
HTTPRequestMethod + '\n' +
|
||||
CanonicalURI + '\n' +
|
||||
CanonicalQueryString + '\n' +
|
||||
CanonicalHeaders + '\n' +
|
||||
SignedHeaders + '\n' +
|
||||
HexEncode(Hash(Payload))
|
||||
*/
|
||||
func (s *V4Signer) canonicalRequest(req *http.Request) string {
|
||||
c := new(bytes.Buffer)
|
||||
fmt.Fprintf(c, "%s\n", req.Method)
|
||||
fmt.Fprintf(c, "%s\n", s.canonicalURI(req.URL))
|
||||
fmt.Fprintf(c, "%s\n", s.canonicalQueryString(req.URL))
|
||||
fmt.Fprintf(c, "%s\n\n", s.canonicalHeaders(req.Header))
|
||||
fmt.Fprintf(c, "%s\n", s.signedHeaders(req.Header))
|
||||
fmt.Fprintf(c, "%s", s.payloadHash(req))
|
||||
return c.String()
|
||||
}
|
||||
|
||||
func (s *V4Signer) canonicalURI(u *url.URL) string {
|
||||
canonicalPath := u.RequestURI()
|
||||
if u.RawQuery != "" {
|
||||
canonicalPath = canonicalPath[:len(canonicalPath)-len(u.RawQuery)-1]
|
||||
}
|
||||
slash := strings.HasSuffix(canonicalPath, "/")
|
||||
canonicalPath = path.Clean(canonicalPath)
|
||||
if canonicalPath != "/" && slash {
|
||||
canonicalPath += "/"
|
||||
}
|
||||
return canonicalPath
|
||||
}
|
||||
|
||||
func (s *V4Signer) canonicalQueryString(u *url.URL) string {
|
||||
var a []string
|
||||
for k, vs := range u.Query() {
|
||||
k = Encode(k)
|
||||
for _, v := range vs {
|
||||
if v == "" {
|
||||
a = append(a, k+"=")
|
||||
} else {
|
||||
v = Encode(v)
|
||||
a = append(a, k+"="+v)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(a)
|
||||
return strings.Join(a, "&")
|
||||
}
|
||||
|
||||
func (s *V4Signer) canonicalHeaders(h http.Header) string {
|
||||
i, a := 0, make([]string, len(h))
|
||||
for k, v := range h {
|
||||
for j, w := range v {
|
||||
v[j] = strings.Trim(w, " ")
|
||||
}
|
||||
sort.Strings(v)
|
||||
a[i] = strings.ToLower(k) + ":" + strings.Join(v, ",")
|
||||
i++
|
||||
}
|
||||
sort.Strings(a)
|
||||
return strings.Join(a, "\n")
|
||||
}
|
||||
|
||||
func (s *V4Signer) signedHeaders(h http.Header) string {
|
||||
i, a := 0, make([]string, len(h))
|
||||
for k, _ := range h {
|
||||
a[i] = strings.ToLower(k)
|
||||
i++
|
||||
}
|
||||
sort.Strings(a)
|
||||
return strings.Join(a, ";")
|
||||
}
|
||||
|
||||
func (s *V4Signer) payloadHash(req *http.Request) string {
|
||||
var b []byte
|
||||
if req.Body == nil {
|
||||
b = []byte("")
|
||||
} else {
|
||||
var err error
|
||||
b, err = ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
// TODO: I REALLY DON'T LIKE THIS PANIC!!!!
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
req.Body = ioutil.NopCloser(bytes.NewBuffer(b))
|
||||
return s.hash(string(b))
|
||||
}
|
||||
|
||||
/*
|
||||
stringToSign method creates the string to sign accorting to Task 2 of the AWS Signature Version 4 Signing Process. (http://goo.gl/es1PAu)
|
||||
|
||||
StringToSign =
|
||||
Algorithm + '\n' +
|
||||
RequestDate + '\n' +
|
||||
CredentialScope + '\n' +
|
||||
HexEncode(Hash(CanonicalRequest))
|
||||
*/
|
||||
func (s *V4Signer) stringToSign(t time.Time, creq string) string {
|
||||
w := new(bytes.Buffer)
|
||||
fmt.Fprint(w, "AWS4-HMAC-SHA256\n")
|
||||
fmt.Fprintf(w, "%s\n", t.Format(ISO8601BasicFormat))
|
||||
fmt.Fprintf(w, "%s\n", s.credentialScope(t))
|
||||
fmt.Fprintf(w, "%s", s.hash(creq))
|
||||
return w.String()
|
||||
}
|
||||
|
||||
func (s *V4Signer) credentialScope(t time.Time) string {
|
||||
return fmt.Sprintf("%s/%s/%s/aws4_request", t.Format(ISO8601BasicFormatShort), s.region.Name, s.serviceName)
|
||||
}
|
||||
|
||||
/*
|
||||
signature method calculates the AWS Signature Version 4 according to Task 3 of the AWS Signature Version 4 Signing Process. (http://goo.gl/j0Yqe1)
|
||||
|
||||
signature = HexEncode(HMAC(derived-signing-key, string-to-sign))
|
||||
*/
|
||||
func (s *V4Signer) signature(t time.Time, sts string) string {
|
||||
h := s.hmac(s.derivedKey(t), []byte(sts))
|
||||
return fmt.Sprintf("%x", h)
|
||||
}
|
||||
|
||||
/*
|
||||
derivedKey method derives a signing key to be used for signing a request.
|
||||
|
||||
kSecret = Your AWS Secret Access Key
|
||||
kDate = HMAC("AWS4" + kSecret, Date)
|
||||
kRegion = HMAC(kDate, Region)
|
||||
kService = HMAC(kRegion, Service)
|
||||
kSigning = HMAC(kService, "aws4_request")
|
||||
*/
|
||||
func (s *V4Signer) derivedKey(t time.Time) []byte {
|
||||
h := s.hmac([]byte("AWS4"+s.auth.SecretKey), []byte(t.Format(ISO8601BasicFormatShort)))
|
||||
h = s.hmac(h, []byte(s.region.Name))
|
||||
h = s.hmac(h, []byte(s.serviceName))
|
||||
h = s.hmac(h, []byte("aws4_request"))
|
||||
return h
|
||||
}
|
||||
|
||||
/*
|
||||
authorization method generates the authorization header value.
|
||||
*/
|
||||
func (s *V4Signer) authorization(header http.Header, t time.Time, signature string) string {
|
||||
w := new(bytes.Buffer)
|
||||
fmt.Fprint(w, "AWS4-HMAC-SHA256 ")
|
||||
fmt.Fprintf(w, "Credential=%s/%s, ", s.auth.AccessKey, s.credentialScope(t))
|
||||
fmt.Fprintf(w, "SignedHeaders=%s, ", s.signedHeaders(header))
|
||||
fmt.Fprintf(w, "Signature=%s", signature)
|
||||
return w.String()
|
||||
}
|
||||
|
||||
// hash method calculates the sha256 hash for a given string
|
||||
func (s *V4Signer) hash(in string) string {
|
||||
h := sha256.New()
|
||||
fmt.Fprintf(h, "%s", in)
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
// hmac method calculates the sha256 hmac for a given slice of bytes
|
||||
func (s *V4Signer) hmac(key, data []byte) []byte {
|
||||
h := hmac.New(sha256.New, key)
|
||||
h.Write(data)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
540
vendor/github.com/goamz/goamz/aws/sign_test.go
сгенерированный
поставляемый
540
vendor/github.com/goamz/goamz/aws/sign_test.go
сгенерированный
поставляемый
@@ -1,540 +0,0 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
var _ = Suite(&V4SignerSuite{})
|
||||
|
||||
type V4SignerSuite struct {
|
||||
auth aws.Auth
|
||||
region aws.Region
|
||||
cases []V4SignerSuiteCase
|
||||
}
|
||||
|
||||
type V4SignerSuiteCase struct {
|
||||
label string
|
||||
request V4SignerSuiteCaseRequest
|
||||
canonicalRequest string
|
||||
stringToSign string
|
||||
signature string
|
||||
authorization string
|
||||
}
|
||||
|
||||
type V4SignerSuiteCaseRequest struct {
|
||||
method string
|
||||
host string
|
||||
url string
|
||||
headers []string
|
||||
body string
|
||||
}
|
||||
|
||||
func (s *V4SignerSuite) SetUpSuite(c *C) {
|
||||
s.auth = aws.Auth{AccessKey: "AKIDEXAMPLE", SecretKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"}
|
||||
s.region = aws.USEast
|
||||
|
||||
// Test cases from the Signature Version 4 Test Suite (http://goo.gl/nguvs0)
|
||||
s.cases = append(s.cases,
|
||||
|
||||
// get-header-key-duplicate
|
||||
V4SignerSuiteCase{
|
||||
label: "get-header-key-duplicate",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "ZOO:zoobar", "zoo:foobar", "zoo:zoobar"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:foobar,zoobar,zoobar\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n3c52f0eaae2b61329c0a332e3fa15842a37bc5812cf4d80eb64784308850e313",
|
||||
signature: "54afcaaf45b331f81cd2edb974f7b824ff4dd594cbbaa945ed636b48477368ed",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=54afcaaf45b331f81cd2edb974f7b824ff4dd594cbbaa945ed636b48477368ed",
|
||||
},
|
||||
|
||||
// get-header-value-order
|
||||
V4SignerSuiteCase{
|
||||
label: "get-header-value-order",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "p:z", "p:a", "p:p", "p:a"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\np:a,a,p,z\n\ndate;host;p\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n94c0389fefe0988cbbedc8606f0ca0b485b48da010d09fc844b45b697c8924fe",
|
||||
signature: "d2973954263943b11624a11d1c963ca81fb274169c7868b2858c04f083199e3d",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=d2973954263943b11624a11d1c963ca81fb274169c7868b2858c04f083199e3d",
|
||||
},
|
||||
|
||||
// get-header-value-trim
|
||||
V4SignerSuiteCase{
|
||||
label: "get-header-value-trim",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "p: phfft "},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\np:phfft\n\ndate;host;p\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ndddd1902add08da1ac94782b05f9278c08dc7468db178a84f8950d93b30b1f35",
|
||||
signature: "debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592",
|
||||
},
|
||||
|
||||
// get-relative-relative
|
||||
V4SignerSuiteCase{
|
||||
label: "get-relative-relative",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/foo/bar/../..",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-relative
|
||||
V4SignerSuiteCase{
|
||||
label: "get-relative",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/foo/..",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-slash-dot-slash
|
||||
V4SignerSuiteCase{
|
||||
label: "get-slash-dot-slash",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/./",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-slash-pointless-dot
|
||||
V4SignerSuiteCase{
|
||||
label: "get-slash-pointless-dot",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/./foo",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/foo\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n8021a97572ee460f87ca67f4e8c0db763216d84715f5424a843a5312a3321e2d",
|
||||
signature: "910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a",
|
||||
},
|
||||
|
||||
// get-slash
|
||||
V4SignerSuiteCase{
|
||||
label: "get-slash",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "//",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-slashes
|
||||
V4SignerSuiteCase{
|
||||
label: "get-slashes",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "//foo//",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/foo/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n6bb4476ee8745730c9cb79f33a0c70baa6d8af29c0077fa12e4e8f1dd17e7098",
|
||||
signature: "b00392262853cfe3201e47ccf945601079e9b8a7f51ee4c3d9ee4f187aa9bf19",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b00392262853cfe3201e47ccf945601079e9b8a7f51ee4c3d9ee4f187aa9bf19",
|
||||
},
|
||||
|
||||
// get-space
|
||||
V4SignerSuiteCase{
|
||||
label: "get-space",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/%20/foo",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/%20/foo\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n69c45fb9fe3fd76442b5086e50b2e9fec8298358da957b293ef26e506fdfb54b",
|
||||
signature: "f309cfbd10197a230c42dd17dbf5cca8a0722564cb40a872d25623cfa758e374",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=f309cfbd10197a230c42dd17dbf5cca8a0722564cb40a872d25623cfa758e374",
|
||||
},
|
||||
|
||||
// get-unreserved
|
||||
V4SignerSuiteCase{
|
||||
label: "get-unreserved",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ndf63ee3247c0356c696a3b21f8d8490b01fa9cd5bc6550ef5ef5f4636b7b8901",
|
||||
signature: "830cc36d03f0f84e6ee4953fbe701c1c8b71a0372c63af9255aa364dd183281e",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=830cc36d03f0f84e6ee4953fbe701c1c8b71a0372c63af9255aa364dd183281e",
|
||||
},
|
||||
|
||||
// get-utf8
|
||||
V4SignerSuiteCase{
|
||||
label: "get-utf8",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/%E1%88%B4",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/%E1%88%B4\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n27ba31df5dbc6e063d8f87d62eb07143f7f271c5330a917840586ac1c85b6f6b",
|
||||
signature: "8d6634c189aa8c75c2e51e106b6b5121bed103fdb351f7d7d4381c738823af74",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=8d6634c189aa8c75c2e51e106b6b5121bed103fdb351f7d7d4381c738823af74",
|
||||
},
|
||||
|
||||
// get-vanilla-empty-query-key
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-empty-query-key",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n0846c2945b0832deb7a463c66af5c4f8bd54ec28c438e67a214445b157c9ddf8",
|
||||
signature: "56c054473fd260c13e4e7393eb203662195f5d4a1fada5314b8b52b23f985e9f",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=56c054473fd260c13e4e7393eb203662195f5d4a1fada5314b8b52b23f985e9f",
|
||||
},
|
||||
|
||||
// get-vanilla-space-query-parameters
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-space-query-parameters",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo foo=bar bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\nfoo%20foo=bar%20bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n6a81658ae0a0a4f73aa72ca7d3b01c9cb0c5d4099f7a5ee897f5a571bfe6f7ff",
|
||||
signature: "c8556bc676129d0806ea8fe6ef182dc095666f4c1582c3d9751d47e4306037c9",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=c8556bc676129d0806ea8fe6ef182dc095666f4c1582c3d9751d47e4306037c9",
|
||||
},
|
||||
|
||||
// get-vanilla-query-order-key-case
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query-order-key-case",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=Zoo&foo=aha",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\nfoo=Zoo&foo=aha\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ne25f777ba161a0f1baf778a87faf057187cf5987f17953320e3ca399feb5f00d",
|
||||
signature: "be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09",
|
||||
},
|
||||
|
||||
// get-vanilla-query-order-key
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query-order-key",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?a=foo&b=foo",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\na=foo&b=foo\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n2f23d14fe13caebf6dfda346285c6d9c14f49eaca8f5ec55c627dd7404f7a727",
|
||||
signature: "0dc122f3b28b831ab48ba65cb47300de53fbe91b577fe113edac383730254a3b",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=0dc122f3b28b831ab48ba65cb47300de53fbe91b577fe113edac383730254a3b",
|
||||
},
|
||||
|
||||
// get-vanilla-query-order-value
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query-order-value",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=b&foo=a",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\nfoo=a&foo=b\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n33dffc220e89131f8f6157a35c40903daa658608d9129ff9489e5cf5bbd9b11b",
|
||||
signature: "feb926e49e382bec75c9d7dcb2a1b6dc8aa50ca43c25d2bc51143768c0875acc",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=feb926e49e382bec75c9d7dcb2a1b6dc8aa50ca43c25d2bc51143768c0875acc",
|
||||
},
|
||||
|
||||
// get-vanilla-query-unreserved
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query-unreserved",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nd2578f3156d4c9d180713d1ff20601d8a3eed0dd35447d24603d7d67414bd6b5",
|
||||
signature: "f1498ddb4d6dae767d97c466fb92f1b59a2c71ca29ac954692663f9db03426fb",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=f1498ddb4d6dae767d97c466fb92f1b59a2c71ca29ac954692663f9db03426fb",
|
||||
},
|
||||
|
||||
// get-vanilla-query
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-vanilla-ut8-query
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-ut8-query",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?ሴ=bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n%E1%88%B4=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nde5065ff39c131e6c2e2bd19cd9345a794bf3b561eab20b8d97b2093fc2a979e",
|
||||
signature: "6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c",
|
||||
},
|
||||
|
||||
// get-vanilla
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// post-header-key-case
|
||||
V4SignerSuiteCase{
|
||||
label: "post-header-key-case",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n05da62cee468d24ae84faff3c39f1b85540de60243c1bcaace39c0a2acc7b2c4",
|
||||
signature: "22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726",
|
||||
},
|
||||
|
||||
// post-header-key-sort
|
||||
V4SignerSuiteCase{
|
||||
label: "post-header-key-sort",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "ZOO:zoobar"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:zoobar\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n34e1bddeb99e76ee01d63b5e28656111e210529efeec6cdfd46a48e4c734545d",
|
||||
signature: "b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a",
|
||||
},
|
||||
|
||||
// post-header-value-case
|
||||
V4SignerSuiteCase{
|
||||
label: "post-header-value-case",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "zoo:ZOOBAR"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:ZOOBAR\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n3aae6d8274b8c03e2cc96fc7d6bda4b9bd7a0a184309344470b2c96953e124aa",
|
||||
signature: "273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7",
|
||||
},
|
||||
|
||||
// post-vanilla-empty-query-value
|
||||
V4SignerSuiteCase{
|
||||
label: "post-vanilla-empty-query-value",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ncd4f39132d8e60bb388831d734230460872b564871c47f5de62e62d1a68dbe1e",
|
||||
signature: "b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92",
|
||||
},
|
||||
|
||||
// post-vanilla-query
|
||||
V4SignerSuiteCase{
|
||||
label: "post-vanilla-query",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ncd4f39132d8e60bb388831d734230460872b564871c47f5de62e62d1a68dbe1e",
|
||||
signature: "b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92",
|
||||
},
|
||||
|
||||
// post-vanilla
|
||||
V4SignerSuiteCase{
|
||||
label: "post-vanilla",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n05da62cee468d24ae84faff3c39f1b85540de60243c1bcaace39c0a2acc7b2c4",
|
||||
signature: "22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726",
|
||||
},
|
||||
|
||||
// post-x-www-form-urlencoded-parameters
|
||||
V4SignerSuiteCase{
|
||||
label: "post-x-www-form-urlencoded-parameters",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Content-Type:application/x-www-form-urlencoded; charset=utf8", "Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
body: "foo=bar",
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ncontent-type:application/x-www-form-urlencoded; charset=utf8\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ncontent-type;date;host\n3ba8907e7a252327488df390ed517c45b96dead033600219bdca7107d1d3f88a",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nc4115f9e54b5cecf192b1eaa23b8e88ed8dc5391bd4fde7b3fff3d9c9fe0af1f",
|
||||
signature: "b105eb10c6d318d2294de9d49dd8b031b55e3c3fe139f2e637da70511e9e7b71",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=b105eb10c6d318d2294de9d49dd8b031b55e3c3fe139f2e637da70511e9e7b71",
|
||||
},
|
||||
|
||||
// post-x-www-form-urlencoded
|
||||
V4SignerSuiteCase{
|
||||
label: "post-x-www-form-urlencoded",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Content-Type:application/x-www-form-urlencoded", "Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
body: "foo=bar",
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ncontent-type:application/x-www-form-urlencoded\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ncontent-type;date;host\n3ba8907e7a252327488df390ed517c45b96dead033600219bdca7107d1d3f88a",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n4c5c6e4b52fb5fb947a8733982a8a5a61b14f04345cbfe6e739236c76dd48f74",
|
||||
signature: "5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s *V4SignerSuite) TestCases(c *C) {
|
||||
signer := aws.NewV4Signer(s.auth, "host", s.region)
|
||||
|
||||
for _, testCase := range s.cases {
|
||||
|
||||
req, err := http.NewRequest(testCase.request.method, "http://"+testCase.request.host+testCase.request.url, strings.NewReader(testCase.request.body))
|
||||
c.Assert(err, IsNil, Commentf("Testcase: %s", testCase.label))
|
||||
for _, v := range testCase.request.headers {
|
||||
h := strings.SplitN(v, ":", 2)
|
||||
req.Header.Add(h[0], h[1])
|
||||
}
|
||||
req.Header.Set("host", req.Host)
|
||||
|
||||
t := signer.RequestTime(req)
|
||||
|
||||
canonicalRequest := signer.CanonicalRequest(req)
|
||||
c.Check(canonicalRequest, Equals, testCase.canonicalRequest, Commentf("Testcase: %s", testCase.label))
|
||||
|
||||
stringToSign := signer.StringToSign(t, canonicalRequest)
|
||||
c.Check(stringToSign, Equals, testCase.stringToSign, Commentf("Testcase: %s", testCase.label))
|
||||
|
||||
signature := signer.Signature(t, stringToSign)
|
||||
c.Check(signature, Equals, testCase.signature, Commentf("Testcase: %s", testCase.label))
|
||||
|
||||
authorization := signer.Authorization(req.Header, t, signature)
|
||||
c.Check(authorization, Equals, testCase.authorization, Commentf("Testcase: %s", testCase.label))
|
||||
|
||||
signer.Sign(req)
|
||||
c.Check(req.Header.Get("Authorization"), Equals, testCase.authorization, Commentf("Testcase: %s", testCase.label))
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleV4Signer() {
|
||||
// Get auth from env vars
|
||||
auth, err := aws.EnvAuth()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// Create a signer with the auth, name of the service, and aws region
|
||||
signer := aws.NewV4Signer(auth, "dynamodb", aws.USEast)
|
||||
|
||||
// Create a request
|
||||
req, err := http.NewRequest("POST", aws.USEast.DynamoDBEndpoint, strings.NewReader("sample_request"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// Date or x-amz-date header is required to sign a request
|
||||
req.Header.Add("Date", time.Now().UTC().Format(http.TimeFormat))
|
||||
|
||||
// Sign the request
|
||||
signer.Sign(req)
|
||||
|
||||
// Issue signed request
|
||||
http.DefaultClient.Do(req)
|
||||
}
|
||||
837
vendor/github.com/goamz/goamz/cloudformation/cloudformation.go
сгенерированный
поставляемый
837
vendor/github.com/goamz/goamz/cloudformation/cloudformation.go
сгенерированный
поставляемый
@@ -1,837 +0,0 @@
|
||||
//
|
||||
// cloudformation: This package provides types and functions to interact with the AWS CloudFormation API
|
||||
//
|
||||
// Depends on https://github.com/goamz/goamz
|
||||
//
|
||||
|
||||
package cloudformation
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
)
|
||||
|
||||
// The CloudFormation type encapsulates operations within a specific EC2 region.
|
||||
type CloudFormation struct {
|
||||
aws.Auth
|
||||
aws.Region
|
||||
}
|
||||
|
||||
// New creates a new CloudFormation Client.
|
||||
func New(auth aws.Auth, region aws.Region) *CloudFormation {
|
||||
|
||||
return &CloudFormation{auth, region}
|
||||
|
||||
}
|
||||
|
||||
const debug = false
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Request dispatching logic.
|
||||
|
||||
// Error encapsulates an error returned by the AWS CloudFormation API.
|
||||
//
|
||||
// See http://goo.gl/zDZbuQ for more details.
|
||||
type Error struct {
|
||||
// HTTP status code (200, 403, ...)
|
||||
StatusCode int
|
||||
// Error type
|
||||
Type string `xml:"Type"`
|
||||
// CloudFormation error code
|
||||
Code string `xml:"Code"`
|
||||
// The human-oriented error message
|
||||
Message string `xml:"Message"`
|
||||
RequestId string `xml:"RequestID"`
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
if err.Code == "" {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s (%s)", err.Message, err.Code)
|
||||
}
|
||||
|
||||
type xmlErrors struct {
|
||||
RequestId string `xml:"RequestId"`
|
||||
Errors []Error `xml:"Error"`
|
||||
}
|
||||
|
||||
func (c *CloudFormation) query(params map[string]string, resp interface{}) error {
|
||||
params["Version"] = "2010-05-15"
|
||||
|
||||
data := strings.NewReader(multimap(params).Encode())
|
||||
|
||||
hreq, err := http.NewRequest("POST", c.Region.CloudFormationEndpoint+"/", data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hreq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
|
||||
|
||||
token := c.Auth.Token()
|
||||
if token != "" {
|
||||
hreq.Header.Set("X-Amz-Security-Token", token)
|
||||
}
|
||||
|
||||
signer := aws.NewV4Signer(c.Auth, "cloudformation", c.Region)
|
||||
signer.Sign(hreq)
|
||||
|
||||
if debug {
|
||||
log.Printf("%v -> {\n", hreq)
|
||||
}
|
||||
r, err := http.DefaultClient.Do(hreq)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error calling Amazon")
|
||||
return err
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
if debug {
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
log.Printf("response:\n")
|
||||
log.Printf("%v\n}\n", string(dump))
|
||||
}
|
||||
if r.StatusCode != 200 {
|
||||
return buildError(r)
|
||||
}
|
||||
err = xml.NewDecoder(r.Body).Decode(resp)
|
||||
return err
|
||||
}
|
||||
|
||||
func buildError(r *http.Response) error {
|
||||
var (
|
||||
err Error
|
||||
errors xmlErrors
|
||||
)
|
||||
xml.NewDecoder(r.Body).Decode(&errors)
|
||||
if len(errors.Errors) > 0 {
|
||||
err = errors.Errors[0]
|
||||
}
|
||||
|
||||
err.RequestId = errors.RequestId
|
||||
err.StatusCode = r.StatusCode
|
||||
if err.Message == "" {
|
||||
err.Message = r.Status
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
func makeParams(action string) map[string]string {
|
||||
params := make(map[string]string)
|
||||
params["Action"] = action
|
||||
return params
|
||||
}
|
||||
|
||||
func multimap(p map[string]string) url.Values {
|
||||
q := make(url.Values, len(p))
|
||||
for k, v := range p {
|
||||
q[k] = []string{v}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// addParamsList adds params in the form of param.member.N to the params map
|
||||
func addParamsList(params map[string]string, label string, ids []string) {
|
||||
for i, id := range ids {
|
||||
params[label+"."+strconv.Itoa(i+1)] = id
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// API Supported Types and Methods
|
||||
|
||||
// SimpleResp is the basic response from most actions.
|
||||
type SimpleResp struct {
|
||||
XMLName xml.Name
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// CancelUpdateStack cancels an update on the specified stack.
|
||||
// If the call completes successfully, the stack will roll back the update and revert
|
||||
// to the previous stack configuration.
|
||||
//
|
||||
// See http://goo.gl/ZE6fOa for more details
|
||||
func (c *CloudFormation) CancelUpdateStack(stackName string) (resp *SimpleResp, err error) {
|
||||
params := makeParams("CancelUpdateStack")
|
||||
|
||||
params["StackName"] = stackName
|
||||
|
||||
resp = new(SimpleResp)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Parameter encapsulates the cloudstack paramter data type
|
||||
//
|
||||
// See http://goo.gl/2rg9eG for more details
|
||||
type Parameter struct {
|
||||
ParameterKey string `xml:"ParameterKey"`
|
||||
ParameterValue string `xml:"ParameterValue"`
|
||||
UsePreviousValue bool `xml:"UsePreviousValue"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Key string `xml:"Key"`
|
||||
Value string `xml:"Value"`
|
||||
}
|
||||
|
||||
// CreateStackParams wraps CreateStack request options
|
||||
//
|
||||
// See http://goo.gl/yDZYuV for more information
|
||||
type CreateStackParams struct {
|
||||
Capabilities []string
|
||||
DisableRollback bool
|
||||
NotificationARNs []string
|
||||
OnFailure string
|
||||
Parameters []Parameter
|
||||
StackName string
|
||||
StackPolicyBody string
|
||||
StackPolicyURL string
|
||||
Tags []Tag
|
||||
TemplateBody string
|
||||
TemplateURL string
|
||||
TimeoutInMinutes int
|
||||
}
|
||||
|
||||
// CreateStackResponse wraps a CreateStack call response
|
||||
//
|
||||
// See http://goo.gl/yDZYuV for more details
|
||||
type CreateStackResponse struct {
|
||||
StackId string `xml:"CreateStackResult>StackId"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// CreateStack creates a stack as specified in the template. After the call completes successfully,
|
||||
// the stack creation starts.
|
||||
//
|
||||
// Required params: StackName
|
||||
//
|
||||
// See http://goo.gl/yDZYuV for more details
|
||||
func (c *CloudFormation) CreateStack(options *CreateStackParams) (
|
||||
resp *CreateStackResponse, err error) {
|
||||
params := makeParams("CreateStack")
|
||||
|
||||
params["StackName"] = options.StackName
|
||||
|
||||
if options.DisableRollback {
|
||||
params["DisableRollback"] = strconv.FormatBool(options.DisableRollback)
|
||||
}
|
||||
if options.OnFailure != "" {
|
||||
params["OnFailure"] = options.OnFailure
|
||||
}
|
||||
if options.StackPolicyBody != "" {
|
||||
params["StackPolicyBody"] = options.StackPolicyBody
|
||||
}
|
||||
if options.StackPolicyURL != "" {
|
||||
params["StackPolicyURL"] = options.StackPolicyURL
|
||||
}
|
||||
if options.TemplateBody != "" {
|
||||
params["TemplateBody"] = options.TemplateBody
|
||||
}
|
||||
if options.TemplateURL != "" {
|
||||
params["TemplateURL"] = options.TemplateURL
|
||||
}
|
||||
if options.TimeoutInMinutes != 0 {
|
||||
params["TimeoutInMinutes"] = strconv.Itoa(options.TimeoutInMinutes)
|
||||
}
|
||||
if len(options.Capabilities) > 0 {
|
||||
addParamsList(params, "Capabilities.member", options.Capabilities)
|
||||
}
|
||||
if len(options.NotificationARNs) > 0 {
|
||||
addParamsList(params, "NotificationARNs.member", options.NotificationARNs)
|
||||
}
|
||||
// Add any parameters
|
||||
for i, t := range options.Parameters {
|
||||
key := "Parameters.member.%d.%s"
|
||||
index := i + 1
|
||||
params[fmt.Sprintf(key, index, "ParameterKey")] = t.ParameterKey
|
||||
params[fmt.Sprintf(key, index, "ParameterValue")] = t.ParameterValue
|
||||
params[fmt.Sprintf(key, index, "UsePreviousValue")] = strconv.FormatBool(t.UsePreviousValue)
|
||||
}
|
||||
// Add any tags
|
||||
for i, t := range options.Tags {
|
||||
key := "Tags.member.%d.%s"
|
||||
index := i + 1
|
||||
params[fmt.Sprintf(key, index, "Key")] = t.Key
|
||||
params[fmt.Sprintf(key, index, "Value")] = t.Value
|
||||
}
|
||||
|
||||
resp = new(CreateStackResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteStack deletes a specified stack.
|
||||
// Once the call completes successfully, stack deletion starts.
|
||||
//
|
||||
// See http://goo.gl/CVMpxC for more details
|
||||
func (c *CloudFormation) DeleteStack(stackName string) (resp *SimpleResp, err error) {
|
||||
params := makeParams("DeleteStack")
|
||||
|
||||
params["StackName"] = stackName
|
||||
|
||||
resp = new(SimpleResp)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// StackEvent encapsulates the StackEvent data type
|
||||
//
|
||||
// See http://goo.gl/EHwiMf for more details
|
||||
type StackEvent struct {
|
||||
EventId string `xml:"EventId"`
|
||||
LogicalResourceId string `xml:"LogicalResourceId"`
|
||||
PhysicalResourceId string `xml:"PhysicalResourceId"`
|
||||
ResourceProperties string `xml:"ResourceProperties"`
|
||||
ResourceStatus string `xml:"ResourceStatus"`
|
||||
ResourceStatusReason string `xml:"ResourceStatusReason"`
|
||||
ResourceType string `xml:"ResourceType"`
|
||||
StackId string `xml:"StackId"`
|
||||
StackName string `xml:"StackName"`
|
||||
Timestamp time.Time `xml:"Timestamp"`
|
||||
}
|
||||
|
||||
// DescribeStackEventsResponse wraps a response returned by DescribeStackEvents request
|
||||
//
|
||||
// See http://goo.gl/zqj4Bz for more details
|
||||
type DescribeStackEventsResponse struct {
|
||||
NextToken string `xml:"DescribeStackEventsResult>NextToken"`
|
||||
StackEvents []StackEvent `xml:"DescribeStackEventsResult>StackEvents>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// DescribeStackEvents returns all stack related events for a specified stack.
|
||||
//
|
||||
// See http://goo.gl/zqj4Bz for more details
|
||||
func (c *CloudFormation) DescribeStackEvents(stackName string, nextToken string) (
|
||||
resp *DescribeStackEventsResponse, err error) {
|
||||
params := makeParams("DescribeStackEvents")
|
||||
|
||||
if stackName != "" {
|
||||
params["StackName"] = stackName
|
||||
}
|
||||
if nextToken != "" {
|
||||
params["NextToken"] = nextToken
|
||||
}
|
||||
|
||||
resp = new(DescribeStackEventsResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// StackResourceDetail encapsulates the StackResourceDetail data type
|
||||
//
|
||||
// See http://goo.gl/flce6I for more details
|
||||
type StackResourceDetail struct {
|
||||
Description string `xml:"Description"`
|
||||
LastUpdatedTimestamp time.Time `xml:"LastUpdatedTimestamp"`
|
||||
LogicalResourceId string `xml:"LogicalResourceId"`
|
||||
Metadata string `xml:"Metadata"`
|
||||
PhysicalResourceId string `xml:"PhysicalResourceId"`
|
||||
ResourceStatus string `xml:"ResourceStatus"`
|
||||
ResourceStatusReason string `xml:"ResourceStatusReason"`
|
||||
ResourceType string `xml:"ResourceType"`
|
||||
StackId string `xml:"StackId"`
|
||||
StackName string `xml:"StackName"`
|
||||
}
|
||||
|
||||
// DescribeStackResourceResponse wraps a response returned by DescribeStackResource request
|
||||
//
|
||||
// See http://goo.gl/6pfPFs for more details
|
||||
type DescribeStackResourceResponse struct {
|
||||
StackResourceDetail StackResourceDetail `xml:"DescribeStackResourceResult>StackResourceDetail"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// DescribeStackResource returns a description of the specified resource in the specified stack.
|
||||
// For deleted stacks, DescribeStackResource returns resource information
|
||||
// for up to 90 days after the stack has been deleted.
|
||||
//
|
||||
// Required params: stackName, logicalResourceId
|
||||
//
|
||||
// See http://goo.gl/6pfPFs for more details
|
||||
func (c *CloudFormation) DescribeStackResource(stackName string, logicalResourceId string) (
|
||||
resp *DescribeStackResourceResponse, err error) {
|
||||
params := makeParams("DescribeStackResource")
|
||||
|
||||
params["StackName"] = stackName
|
||||
params["LogicalResourceId"] = logicalResourceId
|
||||
|
||||
resp = new(DescribeStackResourceResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// StackResource encapsulates the StackResource data type
|
||||
//
|
||||
// See http://goo.gl/j4eli5 for more details
|
||||
type StackResource struct {
|
||||
Description string `xml:"Description"`
|
||||
LogicalResourceId string `xml:"LogicalResourceId"`
|
||||
PhysicalResourceId string `xml:"PhysicalResourceId"`
|
||||
ResourceStatus string `xml:"ResourceStatus"`
|
||||
ResourceStatusReason string `xml:"ResourceStatusReason"`
|
||||
ResourceType string `xml:"ResourceType"`
|
||||
StackId string `xml:"StackId"`
|
||||
StackName string `xml:"StackName"`
|
||||
Timestamp time.Time `xml:"Timestamp"`
|
||||
}
|
||||
|
||||
// DescribeStackResourcesResponse wraps a response returned by DescribeStackResources request
|
||||
//
|
||||
// See http://goo.gl/YnY5rs for more details
|
||||
type DescribeStackResourcesResponse struct {
|
||||
StackResources []StackResource `xml:"DescribeStackResourcesResult>StackResources>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// DescribeStackResources returns AWS resource descriptions for running and deleted stacks.
|
||||
// If stackName is specified, all the associated resources that are part of the stack are returned.
|
||||
// If physicalResourceId is specified, the associated resources of the stack that the resource
|
||||
// belongs to are returned.
|
||||
//
|
||||
// Only the first 100 resources will be returned. If your stack has more resources than this,
|
||||
// you should use ListStackResources instead.
|
||||
//
|
||||
// See http://goo.gl/YnY5rs for more details
|
||||
func (c *CloudFormation) DescribeStackResources(stackName, physicalResourceId, logicalResourceId string) (
|
||||
resp *DescribeStackResourcesResponse, err error) {
|
||||
params := makeParams("DescribeStackResources")
|
||||
|
||||
if stackName != "" {
|
||||
params["StackName"] = stackName
|
||||
}
|
||||
if physicalResourceId != "" {
|
||||
params["PhysicalResourceId"] = physicalResourceId
|
||||
}
|
||||
if logicalResourceId != "" {
|
||||
params["LogicalResourceId"] = logicalResourceId
|
||||
}
|
||||
|
||||
resp = new(DescribeStackResourcesResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Output encapsulates the Output AWS data type
|
||||
//
|
||||
// See http://goo.gl/UOn7q6 for more information
|
||||
type Output struct {
|
||||
Description string `xml:"Description"`
|
||||
OutputKey string `xml:"OutputKey"`
|
||||
OutputValue string `xml:"OutputValue"`
|
||||
}
|
||||
|
||||
// Stack encapsulates the Stack AWS data type
|
||||
//
|
||||
// See http://goo.gl/yDZYuV for more information
|
||||
type Stack struct {
|
||||
Capabilities []string `xml:"Capabilities>member"`
|
||||
CreationTime time.Time `xml:"CreationTime"`
|
||||
Description string `xml:"Description"`
|
||||
DisableRollback bool `xml:"DisableRollback"`
|
||||
LastUpdatedTime time.Time `xml:"LastUpdatedTime"`
|
||||
NotificationARNs []string `xml:"NotificationARNs>member"`
|
||||
Outputs []Output `xml:"Outputs>member"`
|
||||
Parameters []Parameter `xml:"Parameters>member"`
|
||||
StackId string `xml:"StackId"`
|
||||
StackName string `xml:"StackName"`
|
||||
StackStatus string `xml:"StackStatus"`
|
||||
StackStatusReason string `xml:"StackStatusReason"`
|
||||
Tags []Tag `xml:"Tags>member"`
|
||||
TimeoutInMinutes int `xml:"TimeoutInMinutes"`
|
||||
}
|
||||
|
||||
// DescribeStacksResponse wraps a response returned by DescribeStacks request
|
||||
//
|
||||
// See http://goo.gl/UOLsXD for more information
|
||||
type DescribeStacksResponse struct {
|
||||
NextToken string `xml:"DescribeStacksResult>NextToken"`
|
||||
Stacks []Stack `xml:"DescribeStacksResult>Stacks>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// DescribeStacks returns the description for the specified stack;
|
||||
// If no stack name was specified, then it returns the description for all the stacks created.
|
||||
//
|
||||
// See http://goo.gl/UOLsXD for more information
|
||||
func (c *CloudFormation) DescribeStacks(stackName string, nextToken string) (
|
||||
resp *DescribeStacksResponse, err error) {
|
||||
params := makeParams("DescribeStacks")
|
||||
|
||||
if stackName != "" {
|
||||
params["StackName"] = stackName
|
||||
}
|
||||
if nextToken != "" {
|
||||
params["NextToken"] = nextToken
|
||||
}
|
||||
|
||||
resp = new(DescribeStacksResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// EstimateTemplateCostResponse wraps a response returned by EstimateTemplateCost request
|
||||
//
|
||||
// See http://goo.gl/PD9hle for more information
|
||||
type EstimateTemplateCostResponse struct {
|
||||
Url string `xml:"EstimateTemplateCostResult>Url"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// EstimateTemplateCost returns the estimated monthly cost of a template.
|
||||
// The return value is an AWS Simple Monthly Calculator URL with a query string that describes
|
||||
// the resources required to run the template.
|
||||
//
|
||||
// See http://goo.gl/PD9hle for more information
|
||||
func (c *CloudFormation) EstimateTemplateCost(parameters []Parameter, templateBody, templateUrl string) (
|
||||
resp *EstimateTemplateCostResponse, err error) {
|
||||
params := makeParams("EstimateTemplateCost")
|
||||
|
||||
if templateBody != "" {
|
||||
params["TemplateBody"] = templateBody
|
||||
}
|
||||
if templateUrl != "" {
|
||||
params["TemplateURL"] = templateUrl
|
||||
}
|
||||
// Add any parameters
|
||||
for i, t := range parameters {
|
||||
key := "Parameters.member.%d.%s"
|
||||
index := i + 1
|
||||
params[fmt.Sprintf(key, index, "ParameterKey")] = t.ParameterKey
|
||||
params[fmt.Sprintf(key, index, "ParameterValue")] = t.ParameterValue
|
||||
params[fmt.Sprintf(key, index, "UsePreviousValue")] = strconv.FormatBool(t.UsePreviousValue)
|
||||
}
|
||||
|
||||
resp = new(EstimateTemplateCostResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetStackPolicyResponse wraps a response returned by GetStackPolicy request
|
||||
//
|
||||
// See http://goo.gl/iZFSgy for more information
|
||||
type GetStackPolicyResponse struct {
|
||||
StackPolicyBody string `xml:"GetStackPolicyResult>StackPolicyBody"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// GetStackPolicy returns the stack policy for a specified stack. If a stack doesn't have a policy,
|
||||
// a null value is returned.
|
||||
//
|
||||
// See http://goo.gl/iZFSgy for more information
|
||||
func (c *CloudFormation) GetStackPolicy(stackName string) (
|
||||
resp *GetStackPolicyResponse, err error) {
|
||||
params := makeParams("GetStackPolicy")
|
||||
|
||||
params["StackName"] = stackName
|
||||
|
||||
resp = new(GetStackPolicyResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetTemplateResponse wraps a response returned by GetTemplate request
|
||||
//
|
||||
// See http://goo.gl/GU59CB for more information
|
||||
type GetTemplateResponse struct {
|
||||
TemplateBody string `xml:"GetTemplateResult>TemplateBody"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// GetTemplate returns the template body for a specified stack.
|
||||
// You can get the template for running or deleted stacks
|
||||
//
|
||||
// Required Params: StackName - The name or the unique identifier associated with the stack,
|
||||
// which are not always interchangeable:
|
||||
// Running stacks: You can specify either the stack's name or its unique stack ID.
|
||||
// Deleted stacks: You must specify the unique stack ID.
|
||||
//
|
||||
// See http://goo.gl/GU59CB for more information
|
||||
func (c *CloudFormation) GetTemplate(stackName string) (
|
||||
resp *GetTemplateResponse, err error) {
|
||||
params := makeParams("GetTemplate")
|
||||
|
||||
params["StackName"] = stackName
|
||||
|
||||
resp = new(GetTemplateResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// StackResourceSummary encapsulates the StackResourceSummary data type
|
||||
//
|
||||
// See http://goo.gl/Af0vcm for more details
|
||||
type StackResourceSummary struct {
|
||||
LastUpdatedTimestamp time.Time `xml:"LastUpdatedTimestamp"`
|
||||
LogicalResourceId string `xml:"LogicalResourceId"`
|
||||
PhysicalResourceId string `xml:"PhysicalResourceId"`
|
||||
ResourceStatus string `xml:"ResourceStatus"`
|
||||
ResourceStatusReason string `xml:"ResourceStatusReason"`
|
||||
ResourceType string `xml:"ResourceType"`
|
||||
}
|
||||
|
||||
// ListStackResourcesResponse wraps a response returned by ListStackResources request
|
||||
//
|
||||
// See http://goo.gl/JUCgLf for more details
|
||||
type ListStackResourcesResponse struct {
|
||||
NextToken string `xml:"ListStackResourcesResult>NextToken"`
|
||||
StackResourceSummaries []StackResourceSummary `xml:"ListStackResourcesResult>StackResourceSummaries>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// ListStackResources returns descriptions of all resources of the specified stack.
|
||||
// For deleted stacks, ListStackResources returns resource information for up to 90 days
|
||||
// after the stack has been deleted.
|
||||
//
|
||||
// Required Params: stackName - the name or the unique identifier associated with the stack,
|
||||
// which are not always interchangeable:
|
||||
// Running stacks: You can specify either the stack's name or its unique stack ID.
|
||||
// Deleted stacks: You must specify the unique stack ID.
|
||||
//
|
||||
// See http://goo.gl/JUCgLf for more details
|
||||
func (c *CloudFormation) ListStackResources(stackName, nextToken string) (
|
||||
resp *ListStackResourcesResponse, err error) {
|
||||
params := makeParams("ListStackResources")
|
||||
|
||||
params["StackName"] = stackName
|
||||
|
||||
if nextToken != "" {
|
||||
params["NextToken"] = nextToken
|
||||
}
|
||||
|
||||
resp = new(ListStackResourcesResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// StackSummary encapsulates the StackSummary data type
|
||||
//
|
||||
// See http://goo.gl/35j3wf for more details
|
||||
type StackSummary struct {
|
||||
CreationTime time.Time `xml:"CreationTime"`
|
||||
DeletionTime time.Time `xml:"DeletionTime"`
|
||||
LastUpdatedTime time.Time `xml:"LastUpdatedTime"`
|
||||
StackId string `xml:"StackId"`
|
||||
StackName string `xml:"StackName"`
|
||||
StackStatus string `xml:"StackStatus"`
|
||||
StackStatusReason string `xml:"StackStatusReason"`
|
||||
TemplateDescription string `xml:"TemplateDescription"`
|
||||
}
|
||||
|
||||
// ListStacksResponse wraps a response returned by ListStacks request
|
||||
//
|
||||
// See http://goo.gl/UWi6nm for more details
|
||||
type ListStacksResponse struct {
|
||||
NextToken string `xml:"ListStacksResult>NextToken"`
|
||||
StackSummaries []StackSummary `xml:"ListStacksResult>StackSummaries>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// ListStacks Returns the summary information for stacks whose status matches the specified StackStatusFilter.
|
||||
// Summary information for stacks that have been deleted is kept for 90 days after the stack is deleted.
|
||||
// If no StackStatusFilter is specified, summary information for all stacks is returned
|
||||
// (including existing stacks and stacks that have been deleted).
|
||||
//
|
||||
// See http://goo.gl/UWi6nm for more details
|
||||
func (c *CloudFormation) ListStacks(stackStatusFilters []string, nextToken string) (
|
||||
resp *ListStacksResponse, err error) {
|
||||
params := makeParams("ListStacks")
|
||||
|
||||
if nextToken != "" {
|
||||
params["NextToken"] = nextToken
|
||||
}
|
||||
|
||||
if len(stackStatusFilters) > 0 {
|
||||
addParamsList(params, "StackStatusFilter.member", stackStatusFilters)
|
||||
}
|
||||
|
||||
resp = new(ListStacksResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SetStackPolicy sets a stack policy for a specified stack.
|
||||
//
|
||||
// Required Params: stackName
|
||||
//
|
||||
// See http://goo.gl/iY9ohu for more information
|
||||
func (c *CloudFormation) SetStackPolicy(stackName, stackPolicyBody, stackPolicyUrl string) (
|
||||
resp *SimpleResp, err error) {
|
||||
params := makeParams("SetStackPolicy")
|
||||
|
||||
params["StackName"] = stackName
|
||||
|
||||
if stackPolicyBody != "" {
|
||||
params["StackPolicyBody"] = stackPolicyBody
|
||||
}
|
||||
if stackPolicyUrl != "" {
|
||||
params["StackPolicyURL"] = stackPolicyUrl
|
||||
}
|
||||
|
||||
resp = new(SimpleResp)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateStackParams wraps UpdateStack request options
|
||||
//
|
||||
// See http://goo.gl/LvkhZq for more information
|
||||
type UpdateStackParams struct {
|
||||
Capabilities []string
|
||||
NotificationARNs []string
|
||||
Parameters []Parameter
|
||||
StackName string
|
||||
StackPolicyBody string
|
||||
StackPolicyDuringUpdateBody string
|
||||
StackPolicyDuringUpdateURL string
|
||||
StackPolicyURL string
|
||||
TemplateBody string
|
||||
TemplateURL string
|
||||
UsePreviousTemplate bool
|
||||
}
|
||||
|
||||
// UpdateStackResponse wraps the UpdateStack call response
|
||||
//
|
||||
// See http://goo.gl/LvkhZq for more information
|
||||
type UpdateStackResponse struct {
|
||||
StackId string `xml:"UpdateStackResult>StackId"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// UpdateStack updates a stack as specified in the template.
|
||||
// After the call completes successfully, the stack update starts.
|
||||
// You can check the status of the stack via the DescribeStacks action.
|
||||
//
|
||||
// Required Params: options.StackName
|
||||
//
|
||||
// See http://goo.gl/LvkhZq for more information
|
||||
func (c *CloudFormation) UpdateStack(options *UpdateStackParams) (
|
||||
resp *UpdateStackResponse, err error) {
|
||||
params := makeParams("UpdateStack")
|
||||
|
||||
params["StackName"] = options.StackName
|
||||
|
||||
if options.StackPolicyBody != "" {
|
||||
params["StackPolicyBody"] = options.StackPolicyBody
|
||||
}
|
||||
if options.StackPolicyDuringUpdateBody != "" {
|
||||
params["StackPolicyDuringUpdateBody"] = options.StackPolicyDuringUpdateBody
|
||||
}
|
||||
if options.StackPolicyDuringUpdateURL != "" {
|
||||
params["StackPolicyDuringUpdateURL"] = options.StackPolicyDuringUpdateURL
|
||||
}
|
||||
if options.StackPolicyURL != "" {
|
||||
params["StackPolicyURL"] = options.StackPolicyURL
|
||||
}
|
||||
if options.TemplateBody != "" {
|
||||
params["TemplateBody"] = options.TemplateBody
|
||||
}
|
||||
if options.TemplateURL != "" {
|
||||
params["TemplateURL"] = options.TemplateURL
|
||||
}
|
||||
if options.UsePreviousTemplate {
|
||||
params["UsePreviousTemplate"] = strconv.FormatBool(options.UsePreviousTemplate)
|
||||
}
|
||||
|
||||
if len(options.Capabilities) > 0 {
|
||||
addParamsList(params, "Capabilities.member", options.Capabilities)
|
||||
}
|
||||
if len(options.NotificationARNs) > 0 {
|
||||
addParamsList(params, "NotificationARNs.member", options.NotificationARNs)
|
||||
}
|
||||
// Add any parameters
|
||||
for i, t := range options.Parameters {
|
||||
key := "Parameters.member.%d.%s"
|
||||
index := i + 1
|
||||
params[fmt.Sprintf(key, index, "ParameterKey")] = t.ParameterKey
|
||||
params[fmt.Sprintf(key, index, "ParameterValue")] = t.ParameterValue
|
||||
params[fmt.Sprintf(key, index, "UsePreviousValue")] = strconv.FormatBool(t.UsePreviousValue)
|
||||
}
|
||||
|
||||
resp = new(UpdateStackResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// TemplateParameter encapsulates the AWS TemplateParameter data type
|
||||
//
|
||||
// See http://goo.gl/OBhNzk for more information
|
||||
type TemplateParameter struct {
|
||||
DefaultValue string `xml:"DefaultValue"`
|
||||
Description string `xml:Description"`
|
||||
NoEcho bool `xml:NoEcho"`
|
||||
ParameterKey string `xml:ParameterKey"`
|
||||
}
|
||||
|
||||
// ValidateTemplateResponse wraps the ValidateTemplate call response
|
||||
//
|
||||
// See http://goo.gl/OBhNzk for more information
|
||||
type ValidateTemplateResponse struct {
|
||||
Capabilities []string `xml:"ValidateTemplateResult>Capabilities>member"`
|
||||
CapabilitiesReason string `xml:"ValidateTemplateResult>CapabilitiesReason"`
|
||||
Description string `xml:"ValidateTemplateResult>Description"`
|
||||
Parameters []TemplateParameter `xml:"ValidateTemplateResult>Parameters>member"`
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// ValidateTemplate validates a specified template.
|
||||
//
|
||||
// See http://goo.gl/OBhNzk for more information
|
||||
func (c *CloudFormation) ValidateTemplate(templateBody, templateUrl string) (
|
||||
resp *ValidateTemplateResponse, err error) {
|
||||
params := makeParams("ValidateTemplate")
|
||||
|
||||
if templateBody != "" {
|
||||
params["TemplateBody"] = templateBody
|
||||
}
|
||||
if templateUrl != "" {
|
||||
params["TemplateURL"] = templateUrl
|
||||
}
|
||||
|
||||
resp = new(ValidateTemplateResponse)
|
||||
if err := c.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
653
vendor/github.com/goamz/goamz/cloudformation/cloudformation_test.go
сгенерированный
поставляемый
653
vendor/github.com/goamz/goamz/cloudformation/cloudformation_test.go
сгенерированный
поставляемый
@@ -1,653 +0,0 @@
|
||||
package cloudformation_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
cf "github.com/goamz/goamz/cloudformation"
|
||||
"github.com/goamz/goamz/testutil"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
type S struct {
|
||||
cf *cf.CloudFormation
|
||||
}
|
||||
|
||||
var testServer = testutil.NewHTTPServer()
|
||||
|
||||
var mockTest bool
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
testServer.Start()
|
||||
auth := aws.Auth{AccessKey: "abc", SecretKey: "123"}
|
||||
s.cf = cf.New(auth, aws.Region{CloudFormationEndpoint: testServer.URL})
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
testServer.Flush()
|
||||
}
|
||||
|
||||
func (s *S) TestCancelUpdateStack(c *C) {
|
||||
testServer.Response(200, nil, CancelUpdateStackResponse)
|
||||
|
||||
resp, err := s.cf.CancelUpdateStack("foo")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "CancelUpdateStack")
|
||||
c.Assert(values.Get("StackName"), Equals, "foo")
|
||||
// Response test
|
||||
c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestCreateStack(c *C) {
|
||||
testServer.Response(200, nil, CreateStackResponse)
|
||||
|
||||
stackParams := &cf.CreateStackParams{
|
||||
NotificationARNs: []string{"arn:aws:sns:us-east-1:1234567890:my-topic"},
|
||||
Parameters: []cf.Parameter{
|
||||
{
|
||||
ParameterKey: "AvailabilityZone",
|
||||
ParameterValue: "us-east-1a",
|
||||
},
|
||||
},
|
||||
StackName: "MyStack",
|
||||
TemplateBody: "[Template Document]",
|
||||
}
|
||||
resp, err := s.cf.CreateStack(stackParams)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "CreateStack")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("NotificationARNs.member.1"), Equals, "arn:aws:sns:us-east-1:1234567890:my-topic")
|
||||
c.Assert(values.Get("TemplateBody"), Equals, "[Template Document]")
|
||||
c.Assert(values.Get("Parameters.member.1.ParameterKey"), Equals, "AvailabilityZone")
|
||||
c.Assert(values.Get("Parameters.member.1.ParameterValue"), Equals, "us-east-1a")
|
||||
// Response test
|
||||
c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE")
|
||||
c.Assert(resp.StackId, Equals, "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83")
|
||||
}
|
||||
|
||||
func (s *S) TestCreateStackWithInvalidParams(c *C) {
|
||||
testServer.Response(400, nil, CreateStackWithInvalidParamsResponse)
|
||||
//testServer.Response(200, nil, DeleteAutoScalingGroupResponse)
|
||||
|
||||
cfTemplate := `
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Sample template",
|
||||
"Parameters" : {
|
||||
"KeyName" : {
|
||||
"Description" : "key pair",
|
||||
"Type" : "String"
|
||||
}
|
||||
},
|
||||
"Resources" : {
|
||||
"Ec2Instance" : {
|
||||
"Type" : "AWS::EC2::Instance",
|
||||
"Properties" : {
|
||||
"KeyName" : { "Ref" : "KeyName" },
|
||||
"ImageId" : "ami-7f418316",
|
||||
"UserData" : { "Fn::Base64" : "80" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"Outputs" : {
|
||||
"InstanceId" : {
|
||||
"Description" : "InstanceId of the newly created EC2 instance",
|
||||
"Value" : { "Ref" : "Ec2Instance" }
|
||||
}
|
||||
}`
|
||||
|
||||
stackParams := &cf.CreateStackParams{
|
||||
Capabilities: []string{"CAPABILITY_IAM"},
|
||||
DisableRollback: true,
|
||||
NotificationARNs: []string{
|
||||
"arn:aws:sns:us-east-1:1234567890:my-topic",
|
||||
"arn:aws:sns:us-east-1:1234567890:my-topic2",
|
||||
},
|
||||
OnFailure: "ROLLBACK",
|
||||
Parameters: []cf.Parameter{
|
||||
{
|
||||
ParameterKey: "AvailabilityZone",
|
||||
ParameterValue: "us-east-1a",
|
||||
},
|
||||
},
|
||||
StackName: "MyStack",
|
||||
StackPolicyBody: "{PolicyBody}",
|
||||
StackPolicyURL: "http://stack-policy-url",
|
||||
Tags: []cf.Tag{
|
||||
{
|
||||
Key: "TagKey",
|
||||
Value: "TagValue",
|
||||
},
|
||||
},
|
||||
TemplateBody: cfTemplate,
|
||||
TemplateURL: "http://url",
|
||||
TimeoutInMinutes: 20,
|
||||
}
|
||||
resp, err := s.cf.CreateStack(stackParams)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(resp, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "CreateStack")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("NotificationARNs.member.1"), Equals, "arn:aws:sns:us-east-1:1234567890:my-topic")
|
||||
c.Assert(values.Get("NotificationARNs.member.2"), Equals, "arn:aws:sns:us-east-1:1234567890:my-topic2")
|
||||
c.Assert(values.Get("Capabilities.member.1"), Equals, "CAPABILITY_IAM")
|
||||
c.Assert(values.Get("TemplateBody"), Equals, cfTemplate)
|
||||
c.Assert(values.Get("TemplateURL"), Equals, "http://url")
|
||||
c.Assert(values.Get("StackPolicyBody"), Equals, "{PolicyBody}")
|
||||
c.Assert(values.Get("StackPolicyURL"), Equals, "http://stack-policy-url")
|
||||
c.Assert(values.Get("OnFailure"), Equals, "ROLLBACK")
|
||||
c.Assert(values.Get("DisableRollback"), Equals, "true")
|
||||
c.Assert(values.Get("Tags.member.1.Key"), Equals, "TagKey")
|
||||
c.Assert(values.Get("Tags.member.1.Value"), Equals, "TagValue")
|
||||
c.Assert(values.Get("Parameters.member.1.ParameterKey"), Equals, "AvailabilityZone")
|
||||
c.Assert(values.Get("Parameters.member.1.ParameterValue"), Equals, "us-east-1a")
|
||||
c.Assert(values.Get("TimeoutInMinutes"), Equals, "20")
|
||||
|
||||
// Response test
|
||||
c.Assert(err.(*cf.Error).RequestId, Equals, "70a76d42-9665-11e2-9fdf-211deEXAMPLE")
|
||||
c.Assert(err.(*cf.Error).Message, Equals, "Either Template URL or Template Body must be specified.")
|
||||
c.Assert(err.(*cf.Error).Type, Equals, "Sender")
|
||||
c.Assert(err.(*cf.Error).Code, Equals, "ValidationError")
|
||||
c.Assert(err.(*cf.Error).StatusCode, Equals, 400)
|
||||
|
||||
}
|
||||
|
||||
func (s *S) TestDeleteStack(c *C) {
|
||||
testServer.Response(200, nil, DeleteStackResponse)
|
||||
|
||||
resp, err := s.cf.DeleteStack("foo")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "DeleteStack")
|
||||
c.Assert(values.Get("StackName"), Equals, "foo")
|
||||
// Response test
|
||||
c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeStackEvents(c *C) {
|
||||
testServer.Response(200, nil, DescribeStackEventsResponse)
|
||||
|
||||
resp, err := s.cf.DescribeStackEvents("MyStack", "")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
|
||||
// Post request test
|
||||
t1, _ := time.Parse(time.RFC3339, "2010-07-27T22:26:28Z")
|
||||
t2, _ := time.Parse(time.RFC3339, "2010-07-27T22:27:28Z")
|
||||
t3, _ := time.Parse(time.RFC3339, "2010-07-27T22:28:28Z")
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeStackEvents")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("NextToken"), Equals, "")
|
||||
|
||||
// Response test
|
||||
expected := &cf.DescribeStackEventsResponse{
|
||||
StackEvents: []cf.StackEvent{
|
||||
{
|
||||
EventId: "Event-1-Id",
|
||||
StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83",
|
||||
StackName: "MyStack",
|
||||
LogicalResourceId: "MyStack",
|
||||
PhysicalResourceId: "MyStack_One",
|
||||
ResourceType: "AWS::CloudFormation::Stack",
|
||||
Timestamp: t1,
|
||||
ResourceStatus: "CREATE_IN_PROGRESS",
|
||||
ResourceStatusReason: "User initiated",
|
||||
},
|
||||
{
|
||||
EventId: "Event-2-Id",
|
||||
StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83",
|
||||
StackName: "MyStack",
|
||||
LogicalResourceId: "MyDBInstance",
|
||||
PhysicalResourceId: "MyStack_DB1",
|
||||
ResourceType: "AWS::SecurityGroup",
|
||||
Timestamp: t2,
|
||||
ResourceStatus: "CREATE_IN_PROGRESS",
|
||||
ResourceProperties: "{\"GroupDescription\":...}",
|
||||
},
|
||||
{
|
||||
EventId: "Event-3-Id",
|
||||
StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83",
|
||||
StackName: "MyStack",
|
||||
LogicalResourceId: "MySG1",
|
||||
PhysicalResourceId: "MyStack_SG1",
|
||||
ResourceType: "AWS::SecurityGroup",
|
||||
Timestamp: t3,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
},
|
||||
NextToken: "",
|
||||
RequestId: "4af14eec-350e-11e4-b260-EXAMPLE",
|
||||
}
|
||||
c.Assert(resp, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeStackResource(c *C) {
|
||||
testServer.Response(200, nil, DescribeStackResourceResponse)
|
||||
|
||||
resp, err := s.cf.DescribeStackResource("MyStack", "MyDBInstance")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeStackResource")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("LogicalResourceId"), Equals, "MyDBInstance")
|
||||
t, _ := time.Parse(time.RFC3339, "2011-07-07T22:27:28Z")
|
||||
// Response test
|
||||
expected := &cf.DescribeStackResourceResponse{
|
||||
StackResourceDetail: cf.StackResourceDetail{
|
||||
StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83",
|
||||
StackName: "MyStack",
|
||||
LogicalResourceId: "MyDBInstance",
|
||||
PhysicalResourceId: "MyStack_DB1",
|
||||
ResourceType: "AWS::RDS::DBInstance",
|
||||
LastUpdatedTimestamp: t,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
RequestId: "4af14eec-350e-11e4-b260-EXAMPLE",
|
||||
}
|
||||
c.Assert(resp, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeStackResources(c *C) {
|
||||
testServer.Response(200, nil, DescribeStackResourcesResponse)
|
||||
|
||||
resp, err := s.cf.DescribeStackResources("MyStack", "", "")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
|
||||
// Post request test
|
||||
t1, _ := time.Parse(time.RFC3339, "2010-07-27T22:27:28Z")
|
||||
t2, _ := time.Parse(time.RFC3339, "2010-07-27T22:28:28Z")
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeStackResources")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("PhysicalResourceId"), Equals, "")
|
||||
c.Assert(values.Get("LogicalResourceId"), Equals, "")
|
||||
|
||||
// Response test
|
||||
expected := &cf.DescribeStackResourcesResponse{
|
||||
StackResources: []cf.StackResource{
|
||||
{
|
||||
StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83",
|
||||
StackName: "MyStack",
|
||||
LogicalResourceId: "MyDBInstance",
|
||||
PhysicalResourceId: "MyStack_DB1",
|
||||
ResourceType: "AWS::DBInstance",
|
||||
Timestamp: t1,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
{
|
||||
StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83",
|
||||
StackName: "MyStack",
|
||||
LogicalResourceId: "MyAutoScalingGroup",
|
||||
PhysicalResourceId: "MyStack_ASG1",
|
||||
ResourceType: "AWS::AutoScalingGroup",
|
||||
Timestamp: t2,
|
||||
ResourceStatus: "CREATE_IN_PROGRESS",
|
||||
},
|
||||
},
|
||||
RequestId: "4af14eec-350e-11e4-b260-EXAMPLE",
|
||||
}
|
||||
c.Assert(resp, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeStacks(c *C) {
|
||||
testServer.Response(200, nil, DescribeStacksResponse)
|
||||
|
||||
resp, err := s.cf.DescribeStacks("MyStack", "")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
|
||||
// Post request test
|
||||
t, _ := time.Parse(time.RFC3339, "2010-07-27T22:28:28Z")
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeStacks")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("NextToken"), Equals, "")
|
||||
|
||||
// Response test
|
||||
expected := &cf.DescribeStacksResponse{
|
||||
Stacks: []cf.Stack{
|
||||
{
|
||||
StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83",
|
||||
StackName: "MyStack",
|
||||
Description: "My Description",
|
||||
Capabilities: []string{"CAPABILITY_IAM"},
|
||||
NotificationARNs: []string{"arn:aws:sns:region-name:account-name:topic-name"},
|
||||
Parameters: []cf.Parameter{
|
||||
{
|
||||
ParameterKey: "MyKey",
|
||||
ParameterValue: "MyValue",
|
||||
},
|
||||
},
|
||||
Tags: []cf.Tag{
|
||||
{
|
||||
Key: "MyTagKey",
|
||||
Value: "MyTagValue",
|
||||
},
|
||||
},
|
||||
CreationTime: t,
|
||||
StackStatus: "CREATE_COMPLETE",
|
||||
DisableRollback: false,
|
||||
Outputs: []cf.Output{
|
||||
{
|
||||
Description: "ServerUrl",
|
||||
OutputKey: "StartPage",
|
||||
OutputValue: "http://my-load-balancer.amazonaws.com:80/index.html",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NextToken: "",
|
||||
RequestId: "4af14eec-350e-11e4-b260-EXAMPLE",
|
||||
}
|
||||
c.Assert(resp, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestEstimateTemplateCost(c *C) {
|
||||
testServer.Response(200, nil, EstimateTemplateCostResponse)
|
||||
|
||||
resp, err := s.cf.EstimateTemplateCost(nil, "", "https://s3.amazonaws.com/cloudformation-samples-us-east-1/Drupal_Simple.template")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "EstimateTemplateCost")
|
||||
c.Assert(values.Get("TemplateBody"), Equals, "")
|
||||
c.Assert(values.Get("TemplateURL"), Equals, "https://s3.amazonaws.com/cloudformation-samples-us-east-1/Drupal_Simple.template")
|
||||
// Response test
|
||||
c.Assert(resp.Url, Equals, "http://calculator.s3.amazonaws.com/calc5.html?key=cf-2e351785-e821-450c-9d58-625e1e1ebfb6")
|
||||
c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestGetStackPolicy(c *C) {
|
||||
testServer.Response(200, nil, GetStackPolicyResponse)
|
||||
|
||||
resp, err := s.cf.GetStackPolicy("MyStack")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "GetStackPolicy")
|
||||
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
// Response test
|
||||
policy := `{
|
||||
"Statement" : [
|
||||
{
|
||||
"Effect" : "Deny",
|
||||
"Action" : "Update:*",
|
||||
"Principal" : "*",
|
||||
"Resource" : "LogicalResourceId/ProductionDatabase"
|
||||
},
|
||||
{
|
||||
"Effect" : "Allow",
|
||||
"Action" : "Update:*",
|
||||
"Principal" : "*",
|
||||
"Resource" : "*"
|
||||
}
|
||||
]
|
||||
}`
|
||||
c.Assert(resp.StackPolicyBody, Equals, policy)
|
||||
c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestGetTemplate(c *C) {
|
||||
testServer.Response(200, nil, GetTemplateResponse)
|
||||
|
||||
resp, err := s.cf.GetTemplate("MyStack")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "GetTemplate")
|
||||
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
// Response test
|
||||
templateBody := `{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Simple example",
|
||||
"Resources" : {
|
||||
"MySQS" : {
|
||||
"Type" : "AWS::SQS::Queue",
|
||||
"Properties" : {
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
c.Assert(resp.TemplateBody, Equals, templateBody)
|
||||
c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestListStackResources(c *C) {
|
||||
testServer.Response(200, nil, ListStackResourcesResponse)
|
||||
|
||||
resp, err := s.cf.ListStackResources("MyStack", "4dad1-32131da-d-31")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "ListStackResources")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("NextToken"), Equals, "4dad1-32131da-d-31")
|
||||
|
||||
// Response test
|
||||
t1, _ := time.Parse(time.RFC3339, "2011-06-21T20:15:58Z")
|
||||
t2, _ := time.Parse(time.RFC3339, "2011-06-21T20:25:57Z")
|
||||
t3, _ := time.Parse(time.RFC3339, "2011-06-21T20:26:12Z")
|
||||
t4, _ := time.Parse(time.RFC3339, "2011-06-21T20:28:48Z")
|
||||
t5, _ := time.Parse(time.RFC3339, "2011-06-21T20:29:06Z")
|
||||
t6, _ := time.Parse(time.RFC3339, "2011-06-21T20:29:23Z")
|
||||
|
||||
expected := &cf.ListStackResourcesResponse{
|
||||
StackResourceSummaries: []cf.StackResourceSummary{
|
||||
{
|
||||
LogicalResourceId: "DBSecurityGroup",
|
||||
PhysicalResourceId: "gmarcteststack-dbsecuritygroup-1s5m0ez5lkk6w",
|
||||
ResourceType: "AWS::RDS::DBSecurityGroup",
|
||||
LastUpdatedTimestamp: t1,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
{
|
||||
LogicalResourceId: "SampleDB",
|
||||
PhysicalResourceId: "MyStack-sampledb-ycwhk1v830lx",
|
||||
ResourceType: "AWS::RDS::DBInstance",
|
||||
LastUpdatedTimestamp: t2,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
{
|
||||
LogicalResourceId: "SampleApplication",
|
||||
PhysicalResourceId: "MyStack-SampleApplication-1MKNASYR3RBQL",
|
||||
ResourceType: "AWS::ElasticBeanstalk::Application",
|
||||
LastUpdatedTimestamp: t3,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
{
|
||||
LogicalResourceId: "SampleEnvironment",
|
||||
PhysicalResourceId: "myst-Samp-1AGU6ERZX6M3Q",
|
||||
ResourceType: "AWS::ElasticBeanstalk::Environment",
|
||||
LastUpdatedTimestamp: t4,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
{
|
||||
LogicalResourceId: "AlarmTopic",
|
||||
PhysicalResourceId: "arn:aws:sns:us-east-1:803981987763:MyStack-AlarmTopic-SW4IQELG7RPJ",
|
||||
ResourceType: "AWS::SNS::Topic",
|
||||
LastUpdatedTimestamp: t5,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
{
|
||||
LogicalResourceId: "CPUAlarmHigh",
|
||||
PhysicalResourceId: "MyStack-CPUAlarmHigh-POBWQPDJA81F",
|
||||
ResourceType: "AWS::CloudWatch::Alarm",
|
||||
LastUpdatedTimestamp: t6,
|
||||
ResourceStatus: "CREATE_COMPLETE",
|
||||
},
|
||||
},
|
||||
NextToken: "",
|
||||
RequestId: "2d06e36c-ac1d-11e0-a958-f9382b6eb86b",
|
||||
}
|
||||
c.Assert(resp, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestListStacks(c *C) {
|
||||
testServer.Response(200, nil, ListStacksResponse)
|
||||
|
||||
resp, err := s.cf.ListStacks([]string{"CREATE_IN_PROGRESS", "DELETE_COMPLETE"}, "4dad1-32131da-d-31")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "ListStacks")
|
||||
c.Assert(values.Get("StackStatusFilter.member.1"), Equals, "CREATE_IN_PROGRESS")
|
||||
c.Assert(values.Get("StackStatusFilter.member.2"), Equals, "DELETE_COMPLETE")
|
||||
c.Assert(values.Get("NextToken"), Equals, "4dad1-32131da-d-31")
|
||||
|
||||
// Response test
|
||||
c1, _ := time.Parse(time.RFC3339, "2011-05-23T15:47:44Z")
|
||||
c2, _ := time.Parse(time.RFC3339, "2011-03-05T19:57:58Z")
|
||||
d2, _ := time.Parse(time.RFC3339, "2011-03-10T16:20:51Z")
|
||||
|
||||
expected := &cf.ListStacksResponse{
|
||||
StackSummaries: []cf.StackSummary{
|
||||
{
|
||||
StackId: "arn:aws:cloudformation:us-east-1:1234567:stack/TestCreate1/aaaaa",
|
||||
StackName: "vpc1",
|
||||
StackStatus: "CREATE_IN_PROGRESS",
|
||||
CreationTime: c1,
|
||||
TemplateDescription: "Creates one EC2 instance and a load balancer.",
|
||||
},
|
||||
{
|
||||
StackId: "arn:aws:cloudformation:us-east-1:1234567:stack/TestDelete2/bbbbb",
|
||||
StackName: "WP1",
|
||||
StackStatus: "DELETE_COMPLETE",
|
||||
CreationTime: c2,
|
||||
DeletionTime: d2,
|
||||
TemplateDescription: "A simple basic Cloudformation Template.",
|
||||
},
|
||||
},
|
||||
NextToken: "",
|
||||
RequestId: "2d06e36c-ac1d-11e0-a958-f9382b6eb86b",
|
||||
}
|
||||
c.Assert(resp, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestSetStackPolicy(c *C) {
|
||||
testServer.Response(200, nil, SetStackPolicyResponse)
|
||||
|
||||
resp, err := s.cf.SetStackPolicy("MyStack", "[Stack Policy Document]", "")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "SetStackPolicy")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("StackPolicyBody"), Equals, "[Stack Policy Document]")
|
||||
c.Assert(values.Get("StackPolicyUrl"), Equals, "")
|
||||
// Response test
|
||||
c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestUpdateStack(c *C) {
|
||||
testServer.Response(200, nil, UpdateStackResponse)
|
||||
|
||||
stackParams := &cf.UpdateStackParams{
|
||||
Capabilities: []string{"CAPABILITY_IAM"},
|
||||
NotificationARNs: []string{"arn:aws:sns:us-east-1:1234567890:my-topic"},
|
||||
StackPolicyBody: "{PolicyBody}",
|
||||
StackPolicyDuringUpdateBody: "{PolicyDuringUpdateBody}",
|
||||
Parameters: []cf.Parameter{
|
||||
{
|
||||
ParameterKey: "AvailabilityZone",
|
||||
ParameterValue: "us-east-1a",
|
||||
},
|
||||
},
|
||||
UsePreviousTemplate: true,
|
||||
StackName: "MyStack",
|
||||
TemplateBody: "[Template Document]",
|
||||
}
|
||||
resp, err := s.cf.UpdateStack(stackParams)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "UpdateStack")
|
||||
c.Assert(values.Get("StackName"), Equals, "MyStack")
|
||||
c.Assert(values.Get("NotificationARNs.member.1"), Equals, "arn:aws:sns:us-east-1:1234567890:my-topic")
|
||||
c.Assert(values.Get("TemplateBody"), Equals, "[Template Document]")
|
||||
c.Assert(values.Get("Parameters.member.1.ParameterKey"), Equals, "AvailabilityZone")
|
||||
c.Assert(values.Get("Parameters.member.1.ParameterValue"), Equals, "us-east-1a")
|
||||
c.Assert(values.Get("Capabilities.member.1"), Equals, "CAPABILITY_IAM")
|
||||
c.Assert(values.Get("StackPolicyBody"), Equals, "{PolicyBody}")
|
||||
c.Assert(values.Get("StackPolicyDuringUpdateBody"), Equals, "{PolicyDuringUpdateBody}")
|
||||
c.Assert(values.Get("UsePreviousTemplate"), Equals, "true")
|
||||
|
||||
// Response test
|
||||
c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE")
|
||||
c.Assert(resp.StackId, Equals, "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83")
|
||||
}
|
||||
|
||||
func (s *S) TestValidateTemplate(c *C) {
|
||||
testServer.Response(200, nil, ValidateTemplateResponse)
|
||||
|
||||
resp, err := s.cf.ValidateTemplate("", "http://myTemplateRepository/TemplateOne.template")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
|
||||
// Post request test
|
||||
c.Assert(values.Get("Version"), Equals, "2010-05-15")
|
||||
c.Assert(values.Get("Action"), Equals, "ValidateTemplate")
|
||||
c.Assert(values.Get("TemplateURL"), Equals, "http://myTemplateRepository/TemplateOne.template")
|
||||
c.Assert(values.Get("TemplateBody"), Equals, "")
|
||||
|
||||
// Response test
|
||||
expected := &cf.ValidateTemplateResponse{
|
||||
Description: "Test",
|
||||
Capabilities: []string{"CAPABILITY_IAM"},
|
||||
Parameters: []cf.TemplateParameter{
|
||||
{
|
||||
NoEcho: false,
|
||||
ParameterKey: "InstanceType",
|
||||
Description: "Type of instance to launch",
|
||||
DefaultValue: "m1.small",
|
||||
},
|
||||
{
|
||||
NoEcho: false,
|
||||
ParameterKey: "WebServerPort",
|
||||
Description: "The TCP port for the Web Server",
|
||||
DefaultValue: "8888",
|
||||
},
|
||||
{
|
||||
NoEcho: false,
|
||||
ParameterKey: "KeyName",
|
||||
Description: "Name of an existing EC2 KeyPair to enable SSH access into the server",
|
||||
},
|
||||
},
|
||||
RequestId: "0be7b6e8-e4a0-11e0-a5bd-9f8d5a7dbc91",
|
||||
}
|
||||
c.Assert(resp, DeepEquals, expected)
|
||||
}
|
||||
371
vendor/github.com/goamz/goamz/cloudformation/responses_test.go
сгенерированный
поставляемый
371
vendor/github.com/goamz/goamz/cloudformation/responses_test.go
сгенерированный
поставляемый
@@ -1,371 +0,0 @@
|
||||
package cloudformation_test
|
||||
|
||||
var CancelUpdateStackResponse = `
|
||||
<CancelUpdateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<CancelUpdateStackResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CancelUpdateStackResponse>
|
||||
`
|
||||
|
||||
var CreateStackResponse = `
|
||||
<CreateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<CreateStackResult>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
</CreateStackResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateStackResponse>
|
||||
`
|
||||
|
||||
var CreateStackWithInvalidParamsResponse = `
|
||||
<ErrorResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>ValidationError</Code>
|
||||
<Message>Either Template URL or Template Body must be specified.</Message>
|
||||
</Error>
|
||||
<RequestId>70a76d42-9665-11e2-9fdf-211deEXAMPLE</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
|
||||
var DeleteStackResponse = `
|
||||
<DeleteStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<DeleteStackResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteStackResponse>
|
||||
`
|
||||
var DescribeStackEventsResponse = `
|
||||
<DescribeStackEventsResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<DescribeStackEventsResult>
|
||||
<StackEvents>
|
||||
<member>
|
||||
<EventId>Event-1-Id</EventId>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
<StackName>MyStack</StackName>
|
||||
<LogicalResourceId>MyStack</LogicalResourceId>
|
||||
<PhysicalResourceId>MyStack_One</PhysicalResourceId>
|
||||
<ResourceType>AWS::CloudFormation::Stack</ResourceType>
|
||||
<Timestamp>2010-07-27T22:26:28Z</Timestamp>
|
||||
<ResourceStatus>CREATE_IN_PROGRESS</ResourceStatus>
|
||||
<ResourceStatusReason>User initiated</ResourceStatusReason>
|
||||
</member>
|
||||
<member>
|
||||
<EventId>Event-2-Id</EventId>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
<StackName>MyStack</StackName>
|
||||
<LogicalResourceId>MyDBInstance</LogicalResourceId>
|
||||
<PhysicalResourceId>MyStack_DB1</PhysicalResourceId>
|
||||
<ResourceType>AWS::SecurityGroup</ResourceType>
|
||||
<Timestamp>2010-07-27T22:27:28Z</Timestamp>
|
||||
<ResourceStatus>CREATE_IN_PROGRESS</ResourceStatus>
|
||||
<ResourceProperties>{"GroupDescription":...}</ResourceProperties>
|
||||
</member>
|
||||
<member>
|
||||
<EventId>Event-3-Id</EventId>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
<StackName>MyStack</StackName>
|
||||
<LogicalResourceId>MySG1</LogicalResourceId>
|
||||
<PhysicalResourceId>MyStack_SG1</PhysicalResourceId>
|
||||
<ResourceType>AWS::SecurityGroup</ResourceType>
|
||||
<Timestamp>2010-07-27T22:28:28Z</Timestamp>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
</member>
|
||||
</StackEvents>
|
||||
<NextToken/>
|
||||
</DescribeStackEventsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeStackEventsResponse>
|
||||
`
|
||||
|
||||
var DescribeStackResourceResponse = `
|
||||
<DescribeStackResourceResponse>
|
||||
<DescribeStackResourceResult>
|
||||
<StackResourceDetail>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
<StackName>MyStack</StackName>
|
||||
<LogicalResourceId>MyDBInstance</LogicalResourceId>
|
||||
<PhysicalResourceId>MyStack_DB1</PhysicalResourceId>
|
||||
<ResourceType>AWS::RDS::DBInstance</ResourceType>
|
||||
<LastUpdatedTimestamp>2011-07-07T22:27:28Z</LastUpdatedTimestamp>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
</StackResourceDetail>
|
||||
</DescribeStackResourceResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeStackResourceResponse>
|
||||
`
|
||||
var DescribeStackResourcesResponse = `
|
||||
<DescribeStackResourcesResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<DescribeStackResourcesResult>
|
||||
<StackResources>
|
||||
<member>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
<StackName>MyStack</StackName>
|
||||
<LogicalResourceId>MyDBInstance</LogicalResourceId>
|
||||
<PhysicalResourceId>MyStack_DB1</PhysicalResourceId>
|
||||
<ResourceType>AWS::DBInstance</ResourceType>
|
||||
<Timestamp>2010-07-27T22:27:28Z</Timestamp>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
</member>
|
||||
<member>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
<StackName>MyStack</StackName>
|
||||
<LogicalResourceId>MyAutoScalingGroup</LogicalResourceId>
|
||||
<PhysicalResourceId>MyStack_ASG1</PhysicalResourceId>
|
||||
<ResourceType>AWS::AutoScalingGroup</ResourceType>
|
||||
<Timestamp>2010-07-27T22:28:28Z</Timestamp>
|
||||
<ResourceStatus>CREATE_IN_PROGRESS</ResourceStatus>
|
||||
</member>
|
||||
</StackResources>
|
||||
</DescribeStackResourcesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeStackResourcesResponse>
|
||||
`
|
||||
|
||||
var DescribeStacksResponse = `
|
||||
<DescribeStacksResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<DescribeStacksResult>
|
||||
<Stacks>
|
||||
<member>
|
||||
<StackName>MyStack</StackName>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
<StackStatusReason/>
|
||||
<Description>My Description</Description>
|
||||
<Capabilities>
|
||||
<member>CAPABILITY_IAM</member>
|
||||
</Capabilities>
|
||||
<NotificationARNs>
|
||||
<member>arn:aws:sns:region-name:account-name:topic-name</member>
|
||||
</NotificationARNs>
|
||||
<Parameters>
|
||||
<member>
|
||||
<ParameterValue>MyValue</ParameterValue>
|
||||
<ParameterKey>MyKey</ParameterKey>
|
||||
</member>
|
||||
</Parameters>
|
||||
<Tags>
|
||||
<member>
|
||||
<Key>MyTagKey</Key>
|
||||
<Value>MyTagValue</Value>
|
||||
</member>
|
||||
</Tags>
|
||||
<CreationTime>2010-07-27T22:28:28Z</CreationTime>
|
||||
<StackStatus>CREATE_COMPLETE</StackStatus>
|
||||
<DisableRollback>false</DisableRollback>
|
||||
<Outputs>
|
||||
<member>
|
||||
<Description>ServerUrl</Description>
|
||||
<OutputKey>StartPage</OutputKey>
|
||||
<OutputValue>http://my-load-balancer.amazonaws.com:80/index.html</OutputValue>
|
||||
</member>
|
||||
</Outputs>
|
||||
</member>
|
||||
</Stacks>
|
||||
<NextToken/>
|
||||
</DescribeStacksResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeStacksResponse>
|
||||
`
|
||||
|
||||
var EstimateTemplateCostResponse = `
|
||||
<EstimateTemplateCostResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<EstimateTemplateCostResult>
|
||||
<Url>http://calculator.s3.amazonaws.com/calc5.html?key=cf-2e351785-e821-450c-9d58-625e1e1ebfb6</Url>
|
||||
</EstimateTemplateCostResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</EstimateTemplateCostResponse>
|
||||
`
|
||||
|
||||
var GetStackPolicyResponse = `
|
||||
<GetStackPolicyResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<GetStackPolicyResult>
|
||||
<StackPolicyBody>{
|
||||
"Statement" : [
|
||||
{
|
||||
"Effect" : "Deny",
|
||||
"Action" : "Update:*",
|
||||
"Principal" : "*",
|
||||
"Resource" : "LogicalResourceId/ProductionDatabase"
|
||||
},
|
||||
{
|
||||
"Effect" : "Allow",
|
||||
"Action" : "Update:*",
|
||||
"Principal" : "*",
|
||||
"Resource" : "*"
|
||||
}
|
||||
]
|
||||
}</StackPolicyBody>
|
||||
</GetStackPolicyResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</GetStackPolicyResponse>
|
||||
`
|
||||
|
||||
var GetTemplateResponse = `
|
||||
<GetTemplateResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<GetTemplateResult>
|
||||
<TemplateBody>{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Simple example",
|
||||
"Resources" : {
|
||||
"MySQS" : {
|
||||
"Type" : "AWS::SQS::Queue",
|
||||
"Properties" : {
|
||||
}
|
||||
}
|
||||
}
|
||||
}</TemplateBody>
|
||||
</GetTemplateResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</GetTemplateResponse>
|
||||
`
|
||||
|
||||
var ListStackResourcesResponse = `
|
||||
<ListStackResourcesResponse>
|
||||
<ListStackResourcesResult>
|
||||
<StackResourceSummaries>
|
||||
<member>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
<LogicalResourceId>DBSecurityGroup</LogicalResourceId>
|
||||
<LastUpdatedTimestamp>2011-06-21T20:15:58Z</LastUpdatedTimestamp>
|
||||
<PhysicalResourceId>gmarcteststack-dbsecuritygroup-1s5m0ez5lkk6w</PhysicalResourceId>
|
||||
<ResourceType>AWS::RDS::DBSecurityGroup</ResourceType>
|
||||
</member>
|
||||
<member>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
<LogicalResourceId>SampleDB</LogicalResourceId>
|
||||
<LastUpdatedTimestamp>2011-06-21T20:25:57Z</LastUpdatedTimestamp>
|
||||
<PhysicalResourceId>MyStack-sampledb-ycwhk1v830lx</PhysicalResourceId>
|
||||
<ResourceType>AWS::RDS::DBInstance</ResourceType>
|
||||
</member>
|
||||
<member>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
<LogicalResourceId>SampleApplication</LogicalResourceId>
|
||||
<LastUpdatedTimestamp>2011-06-21T20:26:12Z</LastUpdatedTimestamp>
|
||||
<PhysicalResourceId>MyStack-SampleApplication-1MKNASYR3RBQL</PhysicalResourceId>
|
||||
<ResourceType>AWS::ElasticBeanstalk::Application</ResourceType>
|
||||
</member>
|
||||
<member>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
<LogicalResourceId>SampleEnvironment</LogicalResourceId>
|
||||
<LastUpdatedTimestamp>2011-06-21T20:28:48Z</LastUpdatedTimestamp>
|
||||
<PhysicalResourceId>myst-Samp-1AGU6ERZX6M3Q</PhysicalResourceId>
|
||||
<ResourceType>AWS::ElasticBeanstalk::Environment</ResourceType>
|
||||
</member>
|
||||
<member>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
<LogicalResourceId>AlarmTopic</LogicalResourceId>
|
||||
<LastUpdatedTimestamp>2011-06-21T20:29:06Z</LastUpdatedTimestamp>
|
||||
<PhysicalResourceId>arn:aws:sns:us-east-1:803981987763:MyStack-AlarmTopic-SW4IQELG7RPJ</PhysicalResourceId>
|
||||
<ResourceType>AWS::SNS::Topic</ResourceType>
|
||||
</member>
|
||||
<member>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
<LogicalResourceId>CPUAlarmHigh</LogicalResourceId>
|
||||
<LastUpdatedTimestamp>2011-06-21T20:29:23Z</LastUpdatedTimestamp>
|
||||
<PhysicalResourceId>MyStack-CPUAlarmHigh-POBWQPDJA81F</PhysicalResourceId>
|
||||
<ResourceType>AWS::CloudWatch::Alarm</ResourceType>
|
||||
</member>
|
||||
</StackResourceSummaries>
|
||||
</ListStackResourcesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>2d06e36c-ac1d-11e0-a958-f9382b6eb86b</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListStackResourcesResponse>
|
||||
`
|
||||
|
||||
var ListStacksResponse = `
|
||||
<ListStacksResponse>
|
||||
<ListStacksResult>
|
||||
<StackSummaries>
|
||||
<member>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:1234567:stack/TestCreate1/aaaaa</StackId>
|
||||
<StackStatus>CREATE_IN_PROGRESS</StackStatus>
|
||||
<StackName>vpc1</StackName>
|
||||
<CreationTime>2011-05-23T15:47:44Z</CreationTime>
|
||||
<TemplateDescription>Creates one EC2 instance and a load balancer.</TemplateDescription>
|
||||
</member>
|
||||
<member>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:1234567:stack/TestDelete2/bbbbb</StackId>
|
||||
<StackStatus>DELETE_COMPLETE</StackStatus>
|
||||
<DeletionTime>2011-03-10T16:20:51Z</DeletionTime>
|
||||
<StackName>WP1</StackName>
|
||||
<CreationTime>2011-03-05T19:57:58Z</CreationTime>
|
||||
<TemplateDescription>A simple basic Cloudformation Template.</TemplateDescription>
|
||||
</member>
|
||||
</StackSummaries>
|
||||
</ListStacksResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>2d06e36c-ac1d-11e0-a958-f9382b6eb86b</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListStacksResponse>
|
||||
`
|
||||
|
||||
var SetStackPolicyResponse = `
|
||||
<SetStackPolicyResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<SetStackPolicyResponse/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</SetStackPolicyResponse>
|
||||
`
|
||||
|
||||
var UpdateStackResponse = `
|
||||
<UpdateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<UpdateStackResult>
|
||||
<StackId>arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83</StackId>
|
||||
</UpdateStackResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4af14eec-350e-11e4-b260-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</UpdateStackResponse>
|
||||
`
|
||||
var ValidateTemplateResponse = `
|
||||
<ValidateTemplateResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<ValidateTemplateResult>
|
||||
<Description>Test</Description>
|
||||
<Capabilities>
|
||||
<member>CAPABILITY_IAM</member>
|
||||
</Capabilities>
|
||||
<Parameters>
|
||||
<member>
|
||||
<NoEcho>false</NoEcho>
|
||||
<ParameterKey>InstanceType</ParameterKey>
|
||||
<Description>Type of instance to launch</Description>
|
||||
<DefaultValue>m1.small</DefaultValue>
|
||||
</member>
|
||||
<member>
|
||||
<NoEcho>false</NoEcho>
|
||||
<ParameterKey>WebServerPort</ParameterKey>
|
||||
<Description>The TCP port for the Web Server</Description>
|
||||
<DefaultValue>8888</DefaultValue>
|
||||
</member>
|
||||
<member>
|
||||
<NoEcho>false</NoEcho>
|
||||
<ParameterKey>KeyName</ParameterKey>
|
||||
<Description>Name of an existing EC2 KeyPair to enable SSH access into the server</Description>
|
||||
</member>
|
||||
</Parameters>
|
||||
</ValidateTemplateResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>0be7b6e8-e4a0-11e0-a5bd-9f8d5a7dbc91</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ValidateTemplateResponse>
|
||||
`
|
||||
135
vendor/github.com/goamz/goamz/cloudfront/cloudfront.go
сгенерированный
поставляемый
135
vendor/github.com/goamz/goamz/cloudfront/cloudfront.go
сгенерированный
поставляемый
@@ -1,135 +0,0 @@
|
||||
package cloudfront
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CloudFront struct {
|
||||
BaseURL string
|
||||
keyPairId string
|
||||
key *rsa.PrivateKey
|
||||
}
|
||||
|
||||
var base64Replacer = strings.NewReplacer("=", "_", "+", "-", "/", "~")
|
||||
|
||||
func New(baseurl string, key *rsa.PrivateKey, keyPairId string) *CloudFront {
|
||||
return &CloudFront{
|
||||
BaseURL: baseurl,
|
||||
keyPairId: keyPairId,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
type epochTime struct {
|
||||
EpochTime int64 `json:"AWS:EpochTime"`
|
||||
}
|
||||
|
||||
type condition struct {
|
||||
DateLessThan epochTime
|
||||
}
|
||||
|
||||
type statement struct {
|
||||
Resource string
|
||||
Condition condition
|
||||
}
|
||||
|
||||
type policy struct {
|
||||
Statement []statement
|
||||
}
|
||||
|
||||
func buildPolicy(resource string, expireTime time.Time) ([]byte, error) {
|
||||
p := &policy{
|
||||
Statement: []statement{
|
||||
statement{
|
||||
Resource: resource,
|
||||
Condition: condition{
|
||||
DateLessThan: epochTime{
|
||||
EpochTime: expireTime.Truncate(time.Millisecond).Unix(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return json.Marshal(p)
|
||||
}
|
||||
|
||||
func (cf *CloudFront) generateSignature(policy []byte) (string, error) {
|
||||
hash := sha1.New()
|
||||
if _, err := hash.Write(policy); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hashed := hash.Sum(nil)
|
||||
|
||||
signed, err := rsa.SignPKCS1v15(rand.Reader, cf.key, crypto.SHA1, hashed)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encoded := base64Replacer.Replace(base64.StdEncoding.EncodeToString(signed))
|
||||
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
// Creates a signed url using RSAwithSHA1 as specified by
|
||||
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html#private-content-canned-policy-creating-signature
|
||||
func (cf *CloudFront) CannedSignedURL(path, queryString string, expires time.Time) (string, error) {
|
||||
resource := cf.BaseURL + path
|
||||
if queryString != "" {
|
||||
resource = path + "?" + queryString
|
||||
}
|
||||
|
||||
policy, err := buildPolicy(resource, expires)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
signature, err := cf.generateSignature(policy)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// TOOD: Do this once
|
||||
uri, err := url.Parse(cf.BaseURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
uri.RawQuery = queryString
|
||||
if queryString != "" {
|
||||
uri.RawQuery += "&"
|
||||
}
|
||||
|
||||
expireTime := expires.Truncate(time.Millisecond).Unix()
|
||||
|
||||
uri.Path = path
|
||||
uri.RawQuery += fmt.Sprintf("Expires=%d&Signature=%s&Key-Pair-Id=%s", expireTime, signature, cf.keyPairId)
|
||||
|
||||
return uri.String(), nil
|
||||
}
|
||||
|
||||
func (cloudfront *CloudFront) SignedURL(path, querystrings string, expires time.Time) string {
|
||||
policy := `{"Statement":[{"Resource":"` + path + "?" + querystrings + `,"Condition":{"DateLessThan":{"AWS:EpochTime":` + strconv.FormatInt(expires.Truncate(time.Millisecond).Unix(), 10) + `}}}]}`
|
||||
|
||||
hash := sha1.New()
|
||||
hash.Write([]byte(policy))
|
||||
b := hash.Sum(nil)
|
||||
he := base64.StdEncoding.EncodeToString(b)
|
||||
|
||||
policySha1 := he
|
||||
|
||||
url := cloudfront.BaseURL + path + "?" + querystrings + "&Expires=" + strconv.FormatInt(expires.Unix(), 10) + "&Signature=" + policySha1 + "&Key-Pair-Id=" + cloudfront.keyPairId
|
||||
|
||||
return url
|
||||
}
|
||||
52
vendor/github.com/goamz/goamz/cloudfront/cloudfront_test.go
сгенерированный
поставляемый
52
vendor/github.com/goamz/goamz/cloudfront/cloudfront_test.go
сгенерированный
поставляемый
@@ -1,52 +0,0 @@
|
||||
package cloudfront
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSignedCannedURL(t *testing.T) {
|
||||
rawKey, err := ioutil.ReadFile("testdata/key.pem")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pemKey, _ := pem.Decode(rawKey)
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cf := &CloudFront{
|
||||
key: privateKey,
|
||||
keyPairId: "test-key-pair-1231245",
|
||||
BaseURL: "https://cloudfront.com",
|
||||
}
|
||||
|
||||
expireTime, err := time.Parse(time.RFC3339, "2014-03-28T14:00:21Z")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
query := make(url.Values)
|
||||
query.Add("test", "value")
|
||||
|
||||
uri, err := cf.CannedSignedURL("test", "test=value", expireTime)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
signature := parsed.Query().Get("Signature")
|
||||
if signature == "" {
|
||||
t.Fatal("Encoded signature is empty")
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/goamz/goamz/cloudfront/testdata/key.pem
сгенерированный
поставляемый
15
vendor/github.com/goamz/goamz/cloudfront/testdata/key.pem
сгенерированный
поставляемый
@@ -1,15 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQC0yMzp9DkPAE99DhsEaGkqougLvtmDKri4bZj0fFjmGmjyyjz9
|
||||
hlrsr87LHVWzH/7igK7040HG1UqypX3ijtJa9+6BKHwBBctboU3y4GfwFwVAOumY
|
||||
9UytFpyPlgUFrffZLQAywKkT24OgcfEj0G5kiQn760wFnmSUtOuITo708QIDAQAB
|
||||
AoGAJUA6+PoZx72Io3wElSPuh5qJteHdb+mdpmLu4XG936wRc/W4G4VTtvGC6tdg
|
||||
kUhGfOWHJ26sXwwUGDuBdO146m0DkBTuIooy97afpL6hXgL5v4ELHbbuFJcf4Geg
|
||||
/UAuexvRT1HenYFQ/iXM0LlqI33i8cFRc1A+j0Gseo07gAECQQDYFCn7OUokX+Q8
|
||||
M2Cwhu7JT1obmP2HwsBtXl0CDDxtOQkuYJP/UqvtdYPz/kRn3yQjoynaCTHYrFz/
|
||||
H8oN1nNhAkEA1i9TEpo7RbanIyT4vbc1/5xfjE7Pj0lnGku0QXFp/S+8YxbqhjrQ
|
||||
4Qp7TTXIPPqvQhhEpAGGspM460K3F6h7kQJBANJCbMeFa9wRY2ohJIkiA+HoUWph
|
||||
aPNeUxkZpa+EcJhn08NJPzpIG/ypSYl3duEMhYIYF3WPVO3ea2/mYxsr/oECQFj5
|
||||
td/fdEoEk7AU1sQxDNyPwF2QC8dxbcRNuKcLD0Wfg/oB9hEm88jYytoLQpCabx3c
|
||||
6P7cp3EdmaKZx2erlRECQDYTSK2tS0+VoXSV9JbU08Pbu53j3Zhmp4l0csP+l7EU
|
||||
U+rRQzKho4X9vpR/VpRGXbw8tTIhojNpHh5ofryVfgk=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
6
vendor/github.com/goamz/goamz/cloudfront/testdata/key.pub
сгенерированный
поставляемый
6
vendor/github.com/goamz/goamz/cloudfront/testdata/key.pub
сгенерированный
поставляемый
@@ -1,6 +0,0 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0yMzp9DkPAE99DhsEaGkqougL
|
||||
vtmDKri4bZj0fFjmGmjyyjz9hlrsr87LHVWzH/7igK7040HG1UqypX3ijtJa9+6B
|
||||
KHwBBctboU3y4GfwFwVAOumY9UytFpyPlgUFrffZLQAywKkT24OgcfEj0G5kiQn7
|
||||
60wFnmSUtOuITo708QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
7
vendor/github.com/goamz/goamz/cloudwatch/ChangeLog
сгенерированный
поставляемый
7
vendor/github.com/goamz/goamz/cloudwatch/ChangeLog
сгенерированный
поставляемый
@@ -1,7 +0,0 @@
|
||||
2013-10-21 Carlos Salguero <cfsalguero@gmail.com>
|
||||
|
||||
* Removed Namespace from the constructor as not all AWS API method needs it
|
||||
and methods like ListMetrics you could need to call the method without a
|
||||
Namespace to list all available metrics
|
||||
|
||||
* Added ListMetrics method
|
||||
109
vendor/github.com/goamz/goamz/cloudwatch/README.md
сгенерированный
поставляемый
109
vendor/github.com/goamz/goamz/cloudwatch/README.md
сгенерированный
поставляемый
@@ -1,109 +0,0 @@
|
||||
#GoLang AWS Cloudwatch
|
||||
|
||||
## Installation
|
||||
Please refer to the project's main page at [https://github.com/goamz/goamz](https://github.com/goamz/goamz) for instructions about how to install.
|
||||
|
||||
## Available methods
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>GetMetricStatistics</td>
|
||||
<td>Gets statistics for the specified metric.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ListMetrics</td>
|
||||
<td>Returns a list of valid metrics stored for the AWS account.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PutMetricData</td>
|
||||
<td>Publishes metric data points to Amazon CloudWatch.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PutMetricAlarm</td>
|
||||
<td>Creates or updates an alarm and associates it with the specified Amazon CloudWatch metric.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
[Please refer to AWS Cloudwatch's documentation for more info](http://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_Operations.html)
|
||||
|
||||
##Examples
|
||||
####Get Metric Statistics
|
||||
|
||||
```
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"os"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/cloudwatch"
|
||||
)
|
||||
|
||||
func test_get_metric_statistics() {
|
||||
region := aws.Regions["a_region"]
|
||||
namespace:= "AWS/ELB"
|
||||
dimension := &cloudwatch.Dimension{
|
||||
Name: "LoadBalancerName",
|
||||
Value: "your_value",
|
||||
}
|
||||
metricName := "RequestCount"
|
||||
now := time.Now()
|
||||
prev := now.Add(time.Duration(600)*time.Second*-1) // 600 secs = 10 minutes
|
||||
|
||||
auth, err := aws.GetAuth("your_AccessKeyId", "your_SecretAccessKey", "", now)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cw, err := cloudwatch.NewCloudWatch(auth, region.CloudWatchServicepoint)
|
||||
request := &cloudwatch.GetMetricStatisticsRequest {
|
||||
Dimensions: []cloudwatch.Dimension{*dimension},
|
||||
EndTime: now,
|
||||
StartTime: prev,
|
||||
MetricName: metricName,
|
||||
Unit: "Count", // Not mandatory
|
||||
Period: 60,
|
||||
Statistics: []string{"Sum"},
|
||||
Namespace: namespace,
|
||||
}
|
||||
|
||||
response, err := cw.GetMetricStatistics(request)
|
||||
if err == nil {
|
||||
fmt.Printf("%+v\n", response)
|
||||
} else {
|
||||
fmt.Printf("Error: %+v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
####List Metrics
|
||||
|
||||
```
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"os"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/cloudwatch"
|
||||
)
|
||||
|
||||
func test_list_metrics() {
|
||||
region := aws.Regions["us-east-1"] // Any region here
|
||||
now := time.Now()
|
||||
|
||||
auth, err := aws.GetAuth("an AccessKeyId", "a SecretAccessKey", "", now)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cw, err := cloudwatch.NewCloudWatch(auth, region.CloudWatchServicepoint)
|
||||
request := &cloudwatch.ListMetricsRequest{Namespace: "AWS/EC2"}
|
||||
|
||||
response, err := cw.ListMetrics(request)
|
||||
if err == nil {
|
||||
fmt.Printf("%+v\n", response)
|
||||
} else {
|
||||
fmt.Printf("Error: %+v\n", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
404
vendor/github.com/goamz/goamz/cloudwatch/cloudwatch.go
сгенерированный
поставляемый
404
vendor/github.com/goamz/goamz/cloudwatch/cloudwatch.go
сгенерированный
поставляемый
@@ -1,404 +0,0 @@
|
||||
/***** BEGIN LICENSE BLOCK *****
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2012
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Ben Bangert (bbangert@mozilla.com)
|
||||
# Logan Owen (lsowen@s1network.com)
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****/
|
||||
|
||||
package cloudwatch
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/feyeleanor/sets"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The CloudWatch type encapsulates all the CloudWatch operations in a region.
|
||||
type CloudWatch struct {
|
||||
Service aws.AWSService
|
||||
}
|
||||
|
||||
type Dimension struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
type StatisticSet struct {
|
||||
Maximum float64
|
||||
Minimum float64
|
||||
SampleCount float64
|
||||
Sum float64
|
||||
}
|
||||
|
||||
type MetricDatum struct {
|
||||
Dimensions []Dimension
|
||||
MetricName string
|
||||
StatisticValues *StatisticSet
|
||||
Timestamp time.Time
|
||||
Unit string
|
||||
Value float64
|
||||
}
|
||||
|
||||
type Datapoint struct {
|
||||
Average float64
|
||||
Maximum float64
|
||||
Minimum float64
|
||||
SampleCount float64
|
||||
Sum float64
|
||||
Timestamp time.Time
|
||||
Unit string
|
||||
}
|
||||
|
||||
type GetMetricStatisticsRequest struct {
|
||||
Dimensions []Dimension
|
||||
EndTime time.Time
|
||||
StartTime time.Time
|
||||
MetricName string
|
||||
Unit string
|
||||
Period int
|
||||
Statistics []string
|
||||
Namespace string
|
||||
}
|
||||
|
||||
type GetMetricStatisticsResult struct {
|
||||
Datapoints []Datapoint `xml:"Datapoints>member"`
|
||||
NextToken string `xml:"NextToken"`
|
||||
}
|
||||
|
||||
type GetMetricStatisticsResponse struct {
|
||||
GetMetricStatisticsResult GetMetricStatisticsResult
|
||||
ResponseMetadata aws.ResponseMetadata
|
||||
}
|
||||
|
||||
type Metric struct {
|
||||
Dimensions []Dimension `xml:"Dimensions>member"`
|
||||
MetricName string
|
||||
Namespace string
|
||||
}
|
||||
|
||||
type ListMetricsResult struct {
|
||||
Metrics []Metric `xml:"Metrics>member"`
|
||||
NextToken string
|
||||
}
|
||||
|
||||
type ListMetricsResponse struct {
|
||||
ListMetricsResult ListMetricsResult
|
||||
ResponseMetadata aws.ResponseMetadata
|
||||
}
|
||||
|
||||
type ListMetricsRequest struct {
|
||||
Dimensions []Dimension
|
||||
MetricName string
|
||||
Namespace string
|
||||
NextToken string
|
||||
}
|
||||
|
||||
type AlarmAction struct {
|
||||
ARN string
|
||||
}
|
||||
|
||||
type MetricAlarm struct {
|
||||
AlarmActions []AlarmAction
|
||||
AlarmDescription string
|
||||
AlarmName string
|
||||
ComparisonOperator string
|
||||
Dimensions []Dimension
|
||||
EvaluationPeriods int
|
||||
InsufficientDataActions []AlarmAction
|
||||
MetricName string
|
||||
Namespace string
|
||||
OkActions []AlarmAction
|
||||
Period int
|
||||
Statistic string
|
||||
Threshold float64
|
||||
Unit string
|
||||
}
|
||||
|
||||
var attempts = aws.AttemptStrategy{
|
||||
Min: 5,
|
||||
Total: 5 * time.Second,
|
||||
Delay: 200 * time.Millisecond,
|
||||
}
|
||||
|
||||
var validUnits = sets.SSet(
|
||||
"Seconds",
|
||||
"Microseconds",
|
||||
"Milliseconds",
|
||||
"Bytes",
|
||||
"Kilobytes",
|
||||
"Megabytes",
|
||||
"Gigabytes",
|
||||
"Terabytes",
|
||||
"Bits",
|
||||
"Kilobits",
|
||||
"Megabits",
|
||||
"Gigabits",
|
||||
"Terabits",
|
||||
"Percent",
|
||||
"Count",
|
||||
"Bytes/Second",
|
||||
"Kilobytes/Second",
|
||||
"Megabytes/Second",
|
||||
"Gigabytes/Second",
|
||||
"Terabytes/Second",
|
||||
"Bits/Second",
|
||||
"Kilobits/Second",
|
||||
"Megabits/Second",
|
||||
"Gigabits/Second",
|
||||
"Terabits/Second",
|
||||
"Count/Second",
|
||||
)
|
||||
|
||||
var validMetricStatistics = sets.SSet(
|
||||
"Average",
|
||||
"Sum",
|
||||
"SampleCount",
|
||||
"Maximum",
|
||||
"Minimum",
|
||||
)
|
||||
|
||||
var validComparisonOperators = sets.SSet(
|
||||
"LessThanThreshold",
|
||||
"LessThanOrEqualToThreshold",
|
||||
"GreaterThanThreshold",
|
||||
"GreaterThanOrEqualToThreshold",
|
||||
)
|
||||
|
||||
// Create a new CloudWatch object for a given namespace
|
||||
func NewCloudWatch(auth aws.Auth, region aws.ServiceInfo) (*CloudWatch, error) {
|
||||
service, err := aws.NewService(auth, region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CloudWatch{
|
||||
Service: service,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *CloudWatch) query(method, path string, params map[string]string, resp interface{}) error {
|
||||
// Add basic Cloudwatch param
|
||||
params["Version"] = "2010-08-01"
|
||||
|
||||
r, err := c.Service.Query(method, path, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if r.StatusCode != 200 {
|
||||
return c.Service.BuildError(r)
|
||||
}
|
||||
err = xml.NewDecoder(r.Body).Decode(resp)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get statistics for specified metric
|
||||
//
|
||||
// If the arguments are invalid or the server returns an error, the error will
|
||||
// be set and the other values undefined.
|
||||
func (c *CloudWatch) GetMetricStatistics(req *GetMetricStatisticsRequest) (result *GetMetricStatisticsResponse, err error) {
|
||||
statisticsSet := sets.SSet(req.Statistics...)
|
||||
// Kick out argument errors
|
||||
switch {
|
||||
case req.EndTime.IsZero():
|
||||
err = errors.New("No endTime specified")
|
||||
case req.StartTime.IsZero():
|
||||
err = errors.New("No startTime specified")
|
||||
case req.MetricName == "":
|
||||
err = errors.New("No metricName specified")
|
||||
case req.Namespace == "":
|
||||
err = errors.New("No Namespace specified")
|
||||
case req.Period < 60 || req.Period%60 != 0:
|
||||
err = errors.New("Period not 60 seconds or a multiple of 60 seconds")
|
||||
case len(req.Statistics) < 1:
|
||||
err = errors.New("No statistics supplied")
|
||||
case validMetricStatistics.Union(statisticsSet).Len() != validMetricStatistics.Len():
|
||||
err = errors.New("Invalid statistic values supplied")
|
||||
case req.Unit != "" && !validUnits.Member(req.Unit):
|
||||
err = errors.New("Unit is not a valid value")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize all the params
|
||||
params := aws.MakeParams("GetMetricStatistics")
|
||||
params["EndTime"] = req.EndTime.UTC().Format(time.RFC3339)
|
||||
params["StartTime"] = req.StartTime.UTC().Format(time.RFC3339)
|
||||
params["MetricName"] = req.MetricName
|
||||
params["Namespace"] = req.Namespace
|
||||
params["Period"] = strconv.Itoa(req.Period)
|
||||
if req.Unit != "" {
|
||||
params["Unit"] = req.Unit
|
||||
}
|
||||
|
||||
// Serialize the lists of data
|
||||
for i, d := range req.Dimensions {
|
||||
prefix := "Dimensions.member." + strconv.Itoa(i+1)
|
||||
params[prefix+".Name"] = d.Name
|
||||
params[prefix+".Value"] = d.Value
|
||||
}
|
||||
for i, d := range req.Statistics {
|
||||
prefix := "Statistics.member." + strconv.Itoa(i+1)
|
||||
params[prefix] = d
|
||||
}
|
||||
result = new(GetMetricStatisticsResponse)
|
||||
err = c.query("GET", "/", params, result)
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a list of valid metrics stored for the AWS account owner.
|
||||
// Returned metrics can be used with GetMetricStatistics to obtain statistical data for a given metric.
|
||||
|
||||
func (c *CloudWatch) ListMetrics(req *ListMetricsRequest) (result *ListMetricsResponse, err error) {
|
||||
|
||||
// Serialize all the params
|
||||
params := aws.MakeParams("ListMetrics")
|
||||
if req.Namespace != "" {
|
||||
params["Namespace"] = req.Namespace
|
||||
}
|
||||
if len(req.Dimensions) > 0 {
|
||||
for i, d := range req.Dimensions {
|
||||
prefix := "Dimensions.member." + strconv.Itoa(i+1)
|
||||
params[prefix+".Name"] = d.Name
|
||||
params[prefix+".Value"] = d.Value
|
||||
}
|
||||
}
|
||||
|
||||
result = new(ListMetricsResponse)
|
||||
err = c.query("GET", "/", params, &result)
|
||||
metrics := result.ListMetricsResult.Metrics
|
||||
if result.ListMetricsResult.NextToken != "" {
|
||||
params = aws.MakeParams("ListMetrics")
|
||||
params["NextToken"] = result.ListMetricsResult.NextToken
|
||||
for result.ListMetricsResult.NextToken != "" && err == nil {
|
||||
result = new(ListMetricsResponse)
|
||||
err = c.query("GET", "/", params, &result)
|
||||
if err == nil {
|
||||
newslice := make([]Metric, len(metrics)+len(result.ListMetricsResult.Metrics))
|
||||
copy(newslice, metrics)
|
||||
copy(newslice[len(metrics):], result.ListMetricsResult.Metrics)
|
||||
metrics = newslice
|
||||
}
|
||||
}
|
||||
result.ListMetricsResult.Metrics = metrics
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CloudWatch) PutMetricData(metrics []MetricDatum) (result *aws.BaseResponse, err error) {
|
||||
return c.PutMetricDataNamespace(metrics, "")
|
||||
}
|
||||
|
||||
func (c *CloudWatch) PutMetricDataNamespace(metrics []MetricDatum, namespace string) (result *aws.BaseResponse, err error) {
|
||||
// Serialize the params
|
||||
params := aws.MakeParams("PutMetricData")
|
||||
if namespace != "" {
|
||||
params["Namespace"] = namespace
|
||||
}
|
||||
for i, metric := range metrics {
|
||||
prefix := "MetricData.member." + strconv.Itoa(i+1)
|
||||
if metric.MetricName == "" {
|
||||
err = fmt.Errorf("No metric name supplied for metric: %d", i)
|
||||
return
|
||||
}
|
||||
params[prefix+".MetricName"] = metric.MetricName
|
||||
if metric.Unit != "" {
|
||||
params[prefix+".Unit"] = metric.Unit
|
||||
}
|
||||
params[prefix+".Value"] = strconv.FormatFloat(metric.Value, 'E', 10, 64)
|
||||
if !metric.Timestamp.IsZero() {
|
||||
params[prefix+".Timestamp"] = metric.Timestamp.UTC().Format(time.RFC3339)
|
||||
}
|
||||
for j, dim := range metric.Dimensions {
|
||||
dimprefix := prefix + ".Dimensions.member." + strconv.Itoa(j+1)
|
||||
params[dimprefix+".Name"] = dim.Name
|
||||
params[dimprefix+".Value"] = dim.Value
|
||||
}
|
||||
if metric.StatisticValues != nil {
|
||||
statprefix := prefix + ".StatisticValues"
|
||||
params[statprefix+".Maximum"] = strconv.FormatFloat(metric.StatisticValues.Maximum, 'E', 10, 64)
|
||||
params[statprefix+".Minimum"] = strconv.FormatFloat(metric.StatisticValues.Minimum, 'E', 10, 64)
|
||||
params[statprefix+".SampleCount"] = strconv.FormatFloat(metric.StatisticValues.SampleCount, 'E', 10, 64)
|
||||
params[statprefix+".Sum"] = strconv.FormatFloat(metric.StatisticValues.Sum, 'E', 10, 64)
|
||||
}
|
||||
}
|
||||
result = new(aws.BaseResponse)
|
||||
err = c.query("POST", "/", params, result)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CloudWatch) PutMetricAlarm(alarm *MetricAlarm) (result *aws.BaseResponse, err error) {
|
||||
// Serialize the params
|
||||
params := aws.MakeParams("PutMetricAlarm")
|
||||
|
||||
switch {
|
||||
case alarm.AlarmName == "":
|
||||
err = errors.New("No AlarmName supplied")
|
||||
case !validComparisonOperators.Member(alarm.ComparisonOperator):
|
||||
err = errors.New("ComparisonOperator is not valid")
|
||||
case alarm.EvaluationPeriods == 0:
|
||||
err = errors.New("No number of EvaluationPeriods specified")
|
||||
case alarm.MetricName == "":
|
||||
err = errors.New("No MetricName specified")
|
||||
case alarm.Namespace == "":
|
||||
err = errors.New("No Namespace specified")
|
||||
case alarm.Period == 0:
|
||||
err = errors.New("No Period over which statistic should apply was specified")
|
||||
case !validMetricStatistics.Member(alarm.Statistic):
|
||||
err = errors.New("Invalid statistic value supplied")
|
||||
case alarm.Threshold == 0:
|
||||
err = errors.New("No Threshold value specified")
|
||||
case alarm.Unit != "" && !validUnits.Member(alarm.Unit):
|
||||
err = errors.New("Unit is not a valid value")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i, action := range alarm.AlarmActions {
|
||||
params["AlarmActions.member."+strconv.Itoa(i+1)] = action.ARN
|
||||
}
|
||||
for i, action := range alarm.InsufficientDataActions {
|
||||
params["InsufficientDataActions.member."+strconv.Itoa(i+1)] = action.ARN
|
||||
}
|
||||
for i, action := range alarm.OkActions {
|
||||
params["OKActions.member."+strconv.Itoa(i+1)] = action.ARN
|
||||
}
|
||||
if alarm.AlarmDescription != "" {
|
||||
params["AlarmDescription"] = alarm.AlarmDescription
|
||||
}
|
||||
params["AlarmDescription"] = alarm.AlarmDescription
|
||||
params["AlarmName"] = alarm.AlarmName
|
||||
params["ComparisonOperator"] = alarm.ComparisonOperator
|
||||
for i, dim := range alarm.Dimensions {
|
||||
dimprefix := "Dimensions.member." + strconv.Itoa(i+1)
|
||||
params[dimprefix+".Name"] = dim.Name
|
||||
params[dimprefix+".Value"] = dim.Value
|
||||
}
|
||||
params["EvaluationPeriods"] = strconv.Itoa(alarm.EvaluationPeriods)
|
||||
params["MetricName"] = alarm.MetricName
|
||||
params["Namespace"] = alarm.Namespace
|
||||
params["Period"] = strconv.Itoa(alarm.Period)
|
||||
params["Statistic"] = alarm.Statistic
|
||||
params["Threshold"] = strconv.FormatFloat(alarm.Threshold, 'E', 10, 64)
|
||||
if alarm.Unit != "" {
|
||||
params["Unit"] = alarm.Unit
|
||||
}
|
||||
|
||||
result = new(aws.BaseResponse)
|
||||
err = c.query("POST", "/", params, result)
|
||||
return
|
||||
}
|
||||
132
vendor/github.com/goamz/goamz/cloudwatch/cloudwatch_test.go
сгенерированный
поставляемый
132
vendor/github.com/goamz/goamz/cloudwatch/cloudwatch_test.go
сгенерированный
поставляемый
@@ -1,132 +0,0 @@
|
||||
package cloudwatch_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/cloudwatch"
|
||||
"github.com/goamz/goamz/testutil"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
type S struct {
|
||||
cw *cloudwatch.CloudWatch
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
var testServer = testutil.NewHTTPServer()
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
testServer.Start()
|
||||
auth := aws.Auth{AccessKey: "abc", SecretKey: "123"}
|
||||
s.cw, _ = cloudwatch.NewCloudWatch(auth, aws.ServiceInfo{testServer.URL, aws.V2Signature})
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
testServer.Flush()
|
||||
}
|
||||
|
||||
func getTestAlarm() *cloudwatch.MetricAlarm {
|
||||
alarm := new(cloudwatch.MetricAlarm)
|
||||
|
||||
alarm.AlarmName = "TestAlarm"
|
||||
alarm.MetricName = "TestMetric"
|
||||
alarm.Namespace = "TestNamespace"
|
||||
alarm.ComparisonOperator = "LessThanThreshold"
|
||||
alarm.Threshold = 1
|
||||
alarm.EvaluationPeriods = 5
|
||||
alarm.Period = 60
|
||||
alarm.Statistic = "Sum"
|
||||
|
||||
return alarm
|
||||
}
|
||||
|
||||
func (s *S) TestPutAlarm(c *C) {
|
||||
testServer.Response(200, nil, "<RequestId>123</RequestId>")
|
||||
|
||||
alarm := getTestAlarm()
|
||||
|
||||
_, err := s.cw.PutMetricAlarm(alarm)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, Equals, "POST")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"PutMetricAlarm"})
|
||||
c.Assert(req.Form["AlarmName"], DeepEquals, []string{"TestAlarm"})
|
||||
c.Assert(req.Form["ComparisonOperator"], DeepEquals, []string{"LessThanThreshold"})
|
||||
c.Assert(req.Form["EvaluationPeriods"], DeepEquals, []string{"5"})
|
||||
c.Assert(req.Form["Threshold"], DeepEquals, []string{"1.0000000000E+00"})
|
||||
c.Assert(req.Form["Period"], DeepEquals, []string{"60"})
|
||||
c.Assert(req.Form["Statistic"], DeepEquals, []string{"Sum"})
|
||||
}
|
||||
|
||||
func (s *S) TestPutAlarmWithAction(c *C) {
|
||||
testServer.Response(200, nil, "<RequestId>123</RequestId>")
|
||||
|
||||
alarm := getTestAlarm()
|
||||
|
||||
alarm.AlarmActions = []cloudwatch.AlarmAction{
|
||||
cloudwatch.AlarmAction{
|
||||
ARN: "123",
|
||||
},
|
||||
}
|
||||
|
||||
alarm.OkActions = []cloudwatch.AlarmAction{
|
||||
cloudwatch.AlarmAction{
|
||||
ARN: "456",
|
||||
},
|
||||
}
|
||||
|
||||
alarm.InsufficientDataActions = []cloudwatch.AlarmAction{
|
||||
cloudwatch.AlarmAction{
|
||||
ARN: "789",
|
||||
},
|
||||
}
|
||||
|
||||
_, err := s.cw.PutMetricAlarm(alarm)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, Equals, "POST")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"PutMetricAlarm"})
|
||||
c.Assert(req.Form["AlarmActions.member.1"], DeepEquals, []string{"123"})
|
||||
c.Assert(req.Form["OKActions.member.1"], DeepEquals, []string{"456"})
|
||||
c.Assert(req.Form["InsufficientDataActions.member.1"], DeepEquals, []string{"789"})
|
||||
c.Assert(req.Form["AlarmName"], DeepEquals, []string{"TestAlarm"})
|
||||
c.Assert(req.Form["ComparisonOperator"], DeepEquals, []string{"LessThanThreshold"})
|
||||
c.Assert(req.Form["EvaluationPeriods"], DeepEquals, []string{"5"})
|
||||
c.Assert(req.Form["Threshold"], DeepEquals, []string{"1.0000000000E+00"})
|
||||
c.Assert(req.Form["Period"], DeepEquals, []string{"60"})
|
||||
c.Assert(req.Form["Statistic"], DeepEquals, []string{"Sum"})
|
||||
}
|
||||
|
||||
func (s *S) TestPutAlarmInvalidComapirsonOperator(c *C) {
|
||||
testServer.Response(200, nil, "<RequestId>123</RequestId>")
|
||||
|
||||
alarm := getTestAlarm()
|
||||
|
||||
alarm.ComparisonOperator = "LessThan"
|
||||
|
||||
_, err := s.cw.PutMetricAlarm(alarm)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err.Error(), Equals, "ComparisonOperator is not valid")
|
||||
}
|
||||
|
||||
func (s *S) TestPutAlarmInvalidStatistic(c *C) {
|
||||
testServer.Response(200, nil, "<RequestId>123</RequestId>")
|
||||
|
||||
alarm := getTestAlarm()
|
||||
|
||||
alarm.Statistic = "Count"
|
||||
|
||||
_, err := s.cw.PutMetricAlarm(alarm)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err.Error(), Equals, "Invalid statistic value supplied")
|
||||
}
|
||||
1
vendor/github.com/goamz/goamz/dynamodb/.gitignore
сгенерированный
поставляемый
1
vendor/github.com/goamz/goamz/dynamodb/.gitignore
сгенерированный
поставляемый
@@ -1 +0,0 @@
|
||||
dynamodb_local*
|
||||
13
vendor/github.com/goamz/goamz/dynamodb/Makefile
сгенерированный
поставляемый
13
vendor/github.com/goamz/goamz/dynamodb/Makefile
сгенерированный
поставляемый
@@ -1,13 +0,0 @@
|
||||
DYNAMODB_LOCAL_VERSION = 2013-12-12
|
||||
|
||||
launch: DynamoDBLocal.jar
|
||||
cd dynamodb_local_$(DYNAMODB_LOCAL_VERSION) && java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar
|
||||
|
||||
DynamoDBLocal.jar: dynamodb_local_$(DYNAMODB_LOCAL_VERSION).tar.gz
|
||||
[ -d dynamodb_local_$(DYNAMODB_LOCAL_VERSION) ] || tar -zxf dynamodb_local_$(DYNAMODB_LOCAL_VERSION).tar.gz
|
||||
|
||||
dynamodb_local_$(DYNAMODB_LOCAL_VERSION).tar.gz:
|
||||
curl -O https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_$(DYNAMODB_LOCAL_VERSION).tar.gz
|
||||
|
||||
clean:
|
||||
rm -rf dynamodb_local_$(DYNAMODB_LOCAL_VERSION)*
|
||||
27
vendor/github.com/goamz/goamz/dynamodb/README.md
сгенерированный
поставляемый
27
vendor/github.com/goamz/goamz/dynamodb/README.md
сгенерированный
поставляемый
@@ -1,27 +0,0 @@
|
||||
# Running integration tests
|
||||
|
||||
## against DynamoDB local
|
||||
|
||||
To download and launch DynamoDB local:
|
||||
|
||||
```sh
|
||||
$ make
|
||||
```
|
||||
|
||||
To test:
|
||||
|
||||
```sh
|
||||
$ go test -v -amazon
|
||||
```
|
||||
|
||||
## against real DynamoDB server on us-east
|
||||
|
||||
_WARNING_: Some dangerous operations such as `DeleteTable` will be performed during the tests. Please be careful.
|
||||
|
||||
To test:
|
||||
|
||||
```sh
|
||||
$ go test -v -amazon -local=false
|
||||
```
|
||||
|
||||
_Note_: Running tests against real DynamoDB will take several minutes.
|
||||
185
vendor/github.com/goamz/goamz/dynamodb/attribute.go
сгенерированный
поставляемый
185
vendor/github.com/goamz/goamz/dynamodb/attribute.go
сгенерированный
поставляемый
@@ -1,185 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
TYPE_STRING = "S"
|
||||
TYPE_NUMBER = "N"
|
||||
TYPE_BINARY = "B"
|
||||
|
||||
TYPE_STRING_SET = "SS"
|
||||
TYPE_NUMBER_SET = "NS"
|
||||
TYPE_BINARY_SET = "BS"
|
||||
|
||||
COMPARISON_EQUAL = "EQ"
|
||||
COMPARISON_NOT_EQUAL = "NE"
|
||||
COMPARISON_LESS_THAN_OR_EQUAL = "LE"
|
||||
COMPARISON_LESS_THAN = "LT"
|
||||
COMPARISON_GREATER_THAN_OR_EQUAL = "GE"
|
||||
COMPARISON_GREATER_THAN = "GT"
|
||||
COMPARISON_ATTRIBUTE_EXISTS = "NOT_NULL"
|
||||
COMPARISON_ATTRIBUTE_DOES_NOT_EXIST = "NULL"
|
||||
COMPARISON_CONTAINS = "CONTAINS"
|
||||
COMPARISON_DOES_NOT_CONTAIN = "NOT_CONTAINS"
|
||||
COMPARISON_BEGINS_WITH = "BEGINS_WITH"
|
||||
COMPARISON_IN = "IN"
|
||||
COMPARISON_BETWEEN = "BETWEEN"
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
HashKey string
|
||||
RangeKey string
|
||||
}
|
||||
|
||||
type PrimaryKey struct {
|
||||
KeyAttribute *Attribute
|
||||
RangeAttribute *Attribute
|
||||
}
|
||||
|
||||
type Attribute struct {
|
||||
Type string
|
||||
Name string
|
||||
Value string
|
||||
SetValues []string
|
||||
Exists string // exists on dynamodb? Values: "true", "false", or ""
|
||||
}
|
||||
|
||||
type AttributeComparison struct {
|
||||
AttributeName string
|
||||
ComparisonOperator string
|
||||
AttributeValueList []Attribute // contains attributes with only types and names (value ignored)
|
||||
}
|
||||
|
||||
func NewEqualInt64AttributeComparison(attributeName string, equalToValue int64) *AttributeComparison {
|
||||
numeric := NewNumericAttribute(attributeName, strconv.FormatInt(equalToValue, 10))
|
||||
return &AttributeComparison{attributeName,
|
||||
COMPARISON_EQUAL,
|
||||
[]Attribute{*numeric},
|
||||
}
|
||||
}
|
||||
|
||||
func NewEqualStringAttributeComparison(attributeName string, equalToValue string) *AttributeComparison {
|
||||
str := NewStringAttribute(attributeName, equalToValue)
|
||||
return &AttributeComparison{attributeName,
|
||||
COMPARISON_EQUAL,
|
||||
[]Attribute{*str},
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringAttributeComparison(attributeName string, comparisonOperator string, value string) *AttributeComparison {
|
||||
valueToCompare := NewStringAttribute(attributeName, value)
|
||||
return &AttributeComparison{attributeName,
|
||||
comparisonOperator,
|
||||
[]Attribute{*valueToCompare},
|
||||
}
|
||||
}
|
||||
|
||||
func NewNumericAttributeComparison(attributeName string, comparisonOperator string, value int64) *AttributeComparison {
|
||||
valueToCompare := NewNumericAttribute(attributeName, strconv.FormatInt(value, 10))
|
||||
return &AttributeComparison{attributeName,
|
||||
comparisonOperator,
|
||||
[]Attribute{*valueToCompare},
|
||||
}
|
||||
}
|
||||
|
||||
func NewBinaryAttributeComparison(attributeName string, comparisonOperator string, value bool) *AttributeComparison {
|
||||
valueToCompare := NewBinaryAttribute(attributeName, strconv.FormatBool(value))
|
||||
return &AttributeComparison{attributeName,
|
||||
comparisonOperator,
|
||||
[]Attribute{*valueToCompare},
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringAttribute(name string, value string) *Attribute {
|
||||
return &Attribute{
|
||||
Type: TYPE_STRING,
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func NewNumericAttribute(name string, value string) *Attribute {
|
||||
return &Attribute{
|
||||
Type: TYPE_NUMBER,
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBinaryAttribute(name string, value string) *Attribute {
|
||||
return &Attribute{
|
||||
Type: TYPE_BINARY,
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringSetAttribute(name string, values []string) *Attribute {
|
||||
return &Attribute{
|
||||
Type: TYPE_STRING_SET,
|
||||
Name: name,
|
||||
SetValues: values,
|
||||
}
|
||||
}
|
||||
|
||||
func NewNumericSetAttribute(name string, values []string) *Attribute {
|
||||
return &Attribute{
|
||||
Type: TYPE_NUMBER_SET,
|
||||
Name: name,
|
||||
SetValues: values,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBinarySetAttribute(name string, values []string) *Attribute {
|
||||
return &Attribute{
|
||||
Type: TYPE_BINARY_SET,
|
||||
Name: name,
|
||||
SetValues: values,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Attribute) SetType() bool {
|
||||
switch a.Type {
|
||||
case TYPE_BINARY_SET, TYPE_NUMBER_SET, TYPE_STRING_SET:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Attribute) SetExists(exists bool) *Attribute {
|
||||
if exists {
|
||||
a.Exists = "true"
|
||||
} else {
|
||||
a.Exists = "false"
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (k *PrimaryKey) HasRange() bool {
|
||||
return k.RangeAttribute != nil
|
||||
}
|
||||
|
||||
// Useful when you may have many goroutines using a primary key, so they don't fuxor up your values.
|
||||
func (k *PrimaryKey) Clone(h string, r string) []Attribute {
|
||||
pk := &Attribute{
|
||||
Type: k.KeyAttribute.Type,
|
||||
Name: k.KeyAttribute.Name,
|
||||
Value: h,
|
||||
}
|
||||
|
||||
result := []Attribute{*pk}
|
||||
|
||||
if k.HasRange() {
|
||||
rk := &Attribute{
|
||||
Type: k.RangeAttribute.Type,
|
||||
Name: k.RangeAttribute.Name,
|
||||
Value: r,
|
||||
}
|
||||
|
||||
result = append(result, *rk)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
11
vendor/github.com/goamz/goamz/dynamodb/const.go
сгенерированный
поставляемый
11
vendor/github.com/goamz/goamz/dynamodb/const.go
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
type ReturnValues string
|
||||
|
||||
const (
|
||||
NONE ReturnValues = "NONE"
|
||||
ALL_OLD ReturnValues = "ALL_HOLD"
|
||||
UPDATED_OLD ReturnValues = "UPDATED_OLD"
|
||||
ALL_NEW ReturnValues = "ALL_NEW"
|
||||
UPDATED_NEW ReturnValues = "UPDATED_NEW"
|
||||
)
|
||||
142
vendor/github.com/goamz/goamz/dynamodb/dynamodb.go
сгенерированный
поставляемый
142
vendor/github.com/goamz/goamz/dynamodb/dynamodb.go
сгенерированный
поставляемый
@@ -1,142 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import simplejson "github.com/bitly/go-simplejson"
|
||||
import (
|
||||
"errors"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Auth aws.Auth
|
||||
Region aws.Region
|
||||
}
|
||||
|
||||
/*
|
||||
type Query struct {
|
||||
Query string
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func NewQuery(queryParts []string) *Query {
|
||||
return &Query{
|
||||
"{" + strings.Join(queryParts, ",") + "}",
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const (
|
||||
// DynamoDBAPIPrefix is the versioned prefix for DynamoDB API commands.
|
||||
DynamoDBAPIPrefix = "DynamoDB_20120810."
|
||||
// DynamoDBStreamsAPIPrefix is the versioned prefix for DynamoDB Streams API commands.
|
||||
DynamoDBStreamsAPIPrefix = "DynamoDBStreams_20120810."
|
||||
)
|
||||
|
||||
// Specific error constants
|
||||
var ErrNotFound = errors.New("Item not found")
|
||||
|
||||
// Error represents an error in an operation with Dynamodb (following goamz/s3)
|
||||
type Error struct {
|
||||
StatusCode int // HTTP status code (200, 403, ...)
|
||||
Status string
|
||||
Code string // Dynamodb error code ("MalformedQueryString", ...)
|
||||
Message string // The human-oriented error message
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return e.Code + ": " + e.Message
|
||||
}
|
||||
|
||||
func buildError(r *http.Response, jsonBody []byte) error {
|
||||
|
||||
ddbError := Error{
|
||||
StatusCode: r.StatusCode,
|
||||
Status: r.Status,
|
||||
}
|
||||
// TODO return error if Unmarshal fails?
|
||||
|
||||
json, err := simplejson.NewJson(jsonBody)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse body as JSON")
|
||||
return err
|
||||
}
|
||||
ddbError.Message = json.Get("message").MustString()
|
||||
|
||||
// Of the form: com.amazon.coral.validate#ValidationException
|
||||
// We only want the last part
|
||||
codeStr := json.Get("__type").MustString()
|
||||
hashIndex := strings.Index(codeStr, "#")
|
||||
if hashIndex > 0 {
|
||||
codeStr = codeStr[hashIndex+1:]
|
||||
}
|
||||
ddbError.Code = codeStr
|
||||
|
||||
return &ddbError
|
||||
}
|
||||
|
||||
func (s *Server) queryServer(target string, query *Query) ([]byte, error) {
|
||||
data := strings.NewReader(query.String())
|
||||
var endpoint string
|
||||
if isStreamsTarget(target) {
|
||||
endpoint = s.Region.DynamoDBStreamsEndpoint
|
||||
} else {
|
||||
endpoint = s.Region.DynamoDBEndpoint
|
||||
}
|
||||
hreq, err := http.NewRequest("POST", endpoint+"/", data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hreq.Header.Set("Content-Type", "application/x-amz-json-1.0")
|
||||
hreq.Header.Set("X-Amz-Date", time.Now().UTC().Format(aws.ISO8601BasicFormat))
|
||||
hreq.Header.Set("X-Amz-Target", target)
|
||||
|
||||
token := s.Auth.Token()
|
||||
if token != "" {
|
||||
hreq.Header.Set("X-Amz-Security-Token", token)
|
||||
}
|
||||
|
||||
signer := aws.NewV4Signer(s.Auth, "dynamodb", s.Region)
|
||||
signer.Sign(hreq)
|
||||
|
||||
resp, err := http.DefaultClient.Do(hreq)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error calling Amazon")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Printf("Could not read response body")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ErrorHandling.html
|
||||
// "A response code of 200 indicates the operation was successful."
|
||||
if resp.StatusCode != 200 {
|
||||
ddbErr := buildError(resp, body)
|
||||
return nil, ddbErr
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func target(name string) string {
|
||||
return DynamoDBAPIPrefix + name
|
||||
}
|
||||
|
||||
func streamsTarget(name string) string {
|
||||
return DynamoDBStreamsAPIPrefix + name
|
||||
}
|
||||
|
||||
func isStreamsTarget(target string) bool {
|
||||
return strings.HasPrefix(target, DynamoDBStreamsAPIPrefix)
|
||||
}
|
||||
166
vendor/github.com/goamz/goamz/dynamodb/dynamodb_test.go
сгенерированный
поставляемый
166
vendor/github.com/goamz/goamz/dynamodb/dynamodb_test.go
сгенерированный
поставляемый
@@ -1,166 +0,0 @@
|
||||
package dynamodb_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/dynamodb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
const TIMEOUT = 3 * time.Minute
|
||||
|
||||
var amazon = flag.Bool("amazon", false, "Enable tests against dynamodb")
|
||||
var local = flag.Bool("local", true, "Use DynamoDB local on 8080 instead of real server on us-east.")
|
||||
|
||||
var dynamodb_region aws.Region
|
||||
var dynamodb_auth aws.Auth
|
||||
|
||||
type DynamoDBTest struct {
|
||||
server *dynamodb.Server
|
||||
aws.Region // Exports Region
|
||||
TableDescriptionT dynamodb.TableDescriptionT
|
||||
table *dynamodb.Table
|
||||
}
|
||||
|
||||
// Delete all items in the table
|
||||
func (s *DynamoDBTest) TearDownTest(c *C) {
|
||||
pk, err := s.TableDescriptionT.BuildPrimaryKey()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
attrs, err := s.table.Scan(nil)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
for _, a := range attrs {
|
||||
key := &dynamodb.Key{
|
||||
HashKey: a[pk.KeyAttribute.Name].Value,
|
||||
}
|
||||
if pk.HasRange() {
|
||||
key.RangeKey = a[pk.RangeAttribute.Name].Value
|
||||
}
|
||||
if ok, err := s.table.DeleteItem(key); !ok {
|
||||
c.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DynamoDBTest) TearDownSuite(c *C) {
|
||||
// return immediately in the case of calling c.Skip() in SetUpSuite()
|
||||
if s.server == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// check whether the table exists
|
||||
if tables, err := s.server.ListTables(); err != nil {
|
||||
c.Fatal(err)
|
||||
} else {
|
||||
if !findTableByName(tables, s.TableDescriptionT.TableName) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the table and wait
|
||||
if _, err := s.server.DeleteTable(s.TableDescriptionT); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
timeout := time.After(TIMEOUT)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
tables, err := s.server.ListTables()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
if findTableByName(tables, s.TableDescriptionT.TableName) {
|
||||
time.Sleep(5 * time.Second)
|
||||
} else {
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
break
|
||||
case <-timeout:
|
||||
c.Error("Expect the table to be deleted but timed out")
|
||||
close(done)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DynamoDBTest) WaitUntilStatus(c *C, status string) {
|
||||
// We should wait until the table is in specified status because a real DynamoDB has some delay for ready
|
||||
done := make(chan bool)
|
||||
timeout := time.After(TIMEOUT)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
desc, err := s.table.DescribeTable()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
if desc.TableStatus == status {
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
break
|
||||
case <-timeout:
|
||||
c.Errorf("Expect a status to be %s, but timed out", status)
|
||||
close(done)
|
||||
}
|
||||
}
|
||||
|
||||
func setUpAuth(c *C) {
|
||||
if !*amazon {
|
||||
c.Skip("Test against amazon not enabled.")
|
||||
}
|
||||
if *local {
|
||||
c.Log("Using local server")
|
||||
dynamodb_region = aws.Region{
|
||||
DynamoDBEndpoint: "http://127.0.0.1:8000",
|
||||
DynamoDBStreamsEndpoint: "http://127.0.0.1:8000",
|
||||
}
|
||||
dynamodb_auth = aws.Auth{AccessKey: "DUMMY_KEY", SecretKey: "DUMMY_SECRET"}
|
||||
} else {
|
||||
c.Log("Using REAL AMAZON SERVER")
|
||||
dynamodb_region = aws.USEast
|
||||
auth, err := aws.EnvAuth()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
dynamodb_auth = auth
|
||||
}
|
||||
}
|
||||
|
||||
func findTableByName(tables []string, name string) bool {
|
||||
for _, t := range tables {
|
||||
if t == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
351
vendor/github.com/goamz/goamz/dynamodb/item.go
сгенерированный
поставляемый
351
vendor/github.com/goamz/goamz/dynamodb/item.go
сгенерированный
поставляемый
@@ -1,351 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import simplejson "github.com/bitly/go-simplejson"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type BatchGetItem struct {
|
||||
Server *Server
|
||||
Keys map[*Table][]Key
|
||||
}
|
||||
|
||||
type BatchWriteItem struct {
|
||||
Server *Server
|
||||
ItemActions map[*Table]map[string][][]Attribute
|
||||
}
|
||||
|
||||
func (t *Table) BatchGetItems(keys []Key) *BatchGetItem {
|
||||
batchGetItem := &BatchGetItem{t.Server, make(map[*Table][]Key)}
|
||||
|
||||
batchGetItem.Keys[t] = keys
|
||||
return batchGetItem
|
||||
}
|
||||
|
||||
func (t *Table) BatchWriteItems(itemActions map[string][][]Attribute) *BatchWriteItem {
|
||||
batchWriteItem := &BatchWriteItem{t.Server, make(map[*Table]map[string][][]Attribute)}
|
||||
|
||||
batchWriteItem.ItemActions[t] = itemActions
|
||||
return batchWriteItem
|
||||
}
|
||||
|
||||
func (batchGetItem *BatchGetItem) AddTable(t *Table, keys *[]Key) *BatchGetItem {
|
||||
batchGetItem.Keys[t] = *keys
|
||||
return batchGetItem
|
||||
}
|
||||
|
||||
func (batchWriteItem *BatchWriteItem) AddTable(t *Table, itemActions *map[string][][]Attribute) *BatchWriteItem {
|
||||
batchWriteItem.ItemActions[t] = *itemActions
|
||||
return batchWriteItem
|
||||
}
|
||||
|
||||
func (batchGetItem *BatchGetItem) Execute() (map[string][]map[string]*Attribute, error) {
|
||||
q := NewEmptyQuery()
|
||||
q.AddGetRequestItems(batchGetItem.Keys)
|
||||
|
||||
jsonResponse, err := batchGetItem.Server.queryServer("DynamoDB_20120810.BatchGetItem", q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results := make(map[string][]map[string]*Attribute)
|
||||
|
||||
tables, err := json.Get("Responses").Map()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
for table, entries := range tables {
|
||||
var tableResult []map[string]*Attribute
|
||||
|
||||
jsonEntriesArray, ok := entries.([]interface{})
|
||||
if !ok {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
for _, entry := range jsonEntriesArray {
|
||||
item, ok := entry.(map[string]interface{})
|
||||
if !ok {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
unmarshalledItem := parseAttributes(item)
|
||||
tableResult = append(tableResult, unmarshalledItem)
|
||||
}
|
||||
|
||||
results[table] = tableResult
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (batchWriteItem *BatchWriteItem) Execute() (map[string]interface{}, error) {
|
||||
q := NewEmptyQuery()
|
||||
q.AddWriteRequestItems(batchWriteItem.ItemActions)
|
||||
|
||||
jsonResponse, err := batchWriteItem.Server.queryServer("DynamoDB_20120810.BatchWriteItem", q)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unprocessed, err := json.Get("UnprocessedItems").Map()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
if len(unprocessed) == 0 {
|
||||
return nil, nil
|
||||
} else {
|
||||
return unprocessed, errors.New("One or more unprocessed items.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (t *Table) GetItem(key *Key) (map[string]*Attribute, error) {
|
||||
return t.getItem(key, false)
|
||||
}
|
||||
|
||||
func (t *Table) GetItemConsistent(key *Key, consistentRead bool) (map[string]*Attribute, error) {
|
||||
return t.getItem(key, consistentRead)
|
||||
}
|
||||
|
||||
func (t *Table) getItem(key *Key, consistentRead bool) (map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKey(t, key)
|
||||
|
||||
if consistentRead {
|
||||
q.ConsistentRead(consistentRead)
|
||||
}
|
||||
|
||||
jsonResponse, err := t.Server.queryServer(target("GetItem"), q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemJson, ok := json.CheckGet("Item")
|
||||
if !ok {
|
||||
// We got an empty from amz. The item doesn't exist.
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
item, err := itemJson.Map()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
return parseAttributes(item), nil
|
||||
|
||||
}
|
||||
|
||||
func (t *Table) PutItem(hashKey string, rangeKey string, attributes []Attribute) (bool, error) {
|
||||
return t.putItem(hashKey, rangeKey, attributes, nil)
|
||||
}
|
||||
|
||||
func (t *Table) ConditionalPutItem(hashKey, rangeKey string, attributes, expected []Attribute) (bool, error) {
|
||||
return t.putItem(hashKey, rangeKey, attributes, expected)
|
||||
}
|
||||
|
||||
func (t *Table) putItem(hashKey, rangeKey string, attributes, expected []Attribute) (bool, error) {
|
||||
if len(attributes) == 0 {
|
||||
return false, errors.New("At least one attribute is required.")
|
||||
}
|
||||
|
||||
q := NewQuery(t)
|
||||
|
||||
keys := t.Key.Clone(hashKey, rangeKey)
|
||||
attributes = append(attributes, keys...)
|
||||
|
||||
q.AddItem(attributes)
|
||||
if expected != nil {
|
||||
q.AddExpected(expected)
|
||||
}
|
||||
|
||||
jsonResponse, err := t.Server.queryServer(target("PutItem"), q)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (t *Table) deleteItem(key *Key, expected []Attribute) (bool, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKey(t, key)
|
||||
|
||||
if expected != nil {
|
||||
q.AddExpected(expected)
|
||||
}
|
||||
|
||||
jsonResponse, err := t.Server.queryServer(target("DeleteItem"), q)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (t *Table) DeleteItem(key *Key) (bool, error) {
|
||||
return t.deleteItem(key, nil)
|
||||
}
|
||||
|
||||
func (t *Table) ConditionalDeleteItem(key *Key, expected []Attribute) (bool, error) {
|
||||
return t.deleteItem(key, expected)
|
||||
}
|
||||
|
||||
func (t *Table) AddAttributes(key *Key, attributes []Attribute) (bool, error) {
|
||||
return t.modifyAttributes(key, attributes, nil, "ADD")
|
||||
}
|
||||
|
||||
func (t *Table) UpdateAttributes(key *Key, attributes []Attribute) (bool, error) {
|
||||
return t.modifyAttributes(key, attributes, nil, "PUT")
|
||||
}
|
||||
|
||||
func (t *Table) DeleteAttributes(key *Key, attributes []Attribute) (bool, error) {
|
||||
return t.modifyAttributes(key, attributes, nil, "DELETE")
|
||||
}
|
||||
|
||||
func (t *Table) ConditionalAddAttributes(key *Key, attributes, expected []Attribute) (bool, error) {
|
||||
return t.modifyAttributes(key, attributes, expected, "ADD")
|
||||
}
|
||||
|
||||
func (t *Table) ConditionalUpdateAttributes(key *Key, attributes, expected []Attribute) (bool, error) {
|
||||
return t.modifyAttributes(key, attributes, expected, "PUT")
|
||||
}
|
||||
|
||||
func (t *Table) ConditionalDeleteAttributes(key *Key, attributes, expected []Attribute) (bool, error) {
|
||||
return t.modifyAttributes(key, attributes, expected, "DELETE")
|
||||
}
|
||||
|
||||
func (t *Table) modifyAttributes(key *Key, attributes, expected []Attribute, action string) (bool, error) {
|
||||
|
||||
if len(attributes) == 0 {
|
||||
return false, errors.New("At least one attribute is required.")
|
||||
}
|
||||
|
||||
q := NewQuery(t)
|
||||
q.AddKey(t, key)
|
||||
q.AddUpdates(attributes, action)
|
||||
|
||||
if expected != nil {
|
||||
q.AddExpected(expected)
|
||||
}
|
||||
|
||||
jsonResponse, err := t.Server.queryServer(target("UpdateItem"), q)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func parseAttributes(s map[string]interface{}) map[string]*Attribute {
|
||||
results := map[string]*Attribute{}
|
||||
|
||||
for key, value := range s {
|
||||
if v, ok := value.(map[string]interface{}); ok {
|
||||
if val, ok := v[TYPE_STRING].(string); ok {
|
||||
results[key] = &Attribute{
|
||||
Type: TYPE_STRING,
|
||||
Name: key,
|
||||
Value: val,
|
||||
}
|
||||
} else if val, ok := v[TYPE_NUMBER].(string); ok {
|
||||
results[key] = &Attribute{
|
||||
Type: TYPE_NUMBER,
|
||||
Name: key,
|
||||
Value: val,
|
||||
}
|
||||
} else if val, ok := v[TYPE_BINARY].(string); ok {
|
||||
results[key] = &Attribute{
|
||||
Type: TYPE_BINARY,
|
||||
Name: key,
|
||||
Value: val,
|
||||
}
|
||||
} else if vals, ok := v[TYPE_STRING_SET].([]interface{}); ok {
|
||||
arry := make([]string, len(vals))
|
||||
for i, ivalue := range vals {
|
||||
if val, ok := ivalue.(string); ok {
|
||||
arry[i] = val
|
||||
}
|
||||
}
|
||||
results[key] = &Attribute{
|
||||
Type: TYPE_STRING_SET,
|
||||
Name: key,
|
||||
SetValues: arry,
|
||||
}
|
||||
} else if vals, ok := v[TYPE_NUMBER_SET].([]interface{}); ok {
|
||||
arry := make([]string, len(vals))
|
||||
for i, ivalue := range vals {
|
||||
if val, ok := ivalue.(string); ok {
|
||||
arry[i] = val
|
||||
}
|
||||
}
|
||||
results[key] = &Attribute{
|
||||
Type: TYPE_NUMBER_SET,
|
||||
Name: key,
|
||||
SetValues: arry,
|
||||
}
|
||||
} else if vals, ok := v[TYPE_BINARY_SET].([]interface{}); ok {
|
||||
arry := make([]string, len(vals))
|
||||
for i, ivalue := range vals {
|
||||
if val, ok := ivalue.(string); ok {
|
||||
arry[i] = val
|
||||
}
|
||||
}
|
||||
results[key] = &Attribute{
|
||||
Type: TYPE_BINARY_SET,
|
||||
Name: key,
|
||||
SetValues: arry,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("type assertion to map[string] interface{} failed for : %s\n ", value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
446
vendor/github.com/goamz/goamz/dynamodb/item_test.go
сгенерированный
поставляемый
446
vendor/github.com/goamz/goamz/dynamodb/item_test.go
сгенерированный
поставляемый
@@ -1,446 +0,0 @@
|
||||
package dynamodb_test
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/dynamodb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type ItemSuite struct {
|
||||
TableDescriptionT dynamodb.TableDescriptionT
|
||||
DynamoDBTest
|
||||
WithRange bool
|
||||
}
|
||||
|
||||
func (s *ItemSuite) SetUpSuite(c *C) {
|
||||
setUpAuth(c)
|
||||
s.DynamoDBTest.TableDescriptionT = s.TableDescriptionT
|
||||
s.server = &dynamodb.Server{dynamodb_auth, dynamodb_region}
|
||||
pk, err := s.TableDescriptionT.BuildPrimaryKey()
|
||||
if err != nil {
|
||||
c.Skip(err.Error())
|
||||
}
|
||||
s.table = s.server.NewTable(s.TableDescriptionT.TableName, pk)
|
||||
|
||||
// Cleanup
|
||||
s.TearDownSuite(c)
|
||||
_, err = s.server.CreateTable(s.TableDescriptionT)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
s.WaitUntilStatus(c, "ACTIVE")
|
||||
}
|
||||
|
||||
var item_suite = &ItemSuite{
|
||||
TableDescriptionT: dynamodb.TableDescriptionT{
|
||||
TableName: "DynamoDBTestMyTable",
|
||||
AttributeDefinitions: []dynamodb.AttributeDefinitionT{
|
||||
dynamodb.AttributeDefinitionT{"TestHashKey", "S"},
|
||||
dynamodb.AttributeDefinitionT{"TestRangeKey", "N"},
|
||||
},
|
||||
KeySchema: []dynamodb.KeySchemaT{
|
||||
dynamodb.KeySchemaT{"TestHashKey", "HASH"},
|
||||
dynamodb.KeySchemaT{"TestRangeKey", "RANGE"},
|
||||
},
|
||||
ProvisionedThroughput: dynamodb.ProvisionedThroughputT{
|
||||
ReadCapacityUnits: 1,
|
||||
WriteCapacityUnits: 1,
|
||||
},
|
||||
},
|
||||
WithRange: true,
|
||||
}
|
||||
|
||||
var item_without_range_suite = &ItemSuite{
|
||||
TableDescriptionT: dynamodb.TableDescriptionT{
|
||||
TableName: "DynamoDBTestMyTable",
|
||||
AttributeDefinitions: []dynamodb.AttributeDefinitionT{
|
||||
dynamodb.AttributeDefinitionT{"TestHashKey", "S"},
|
||||
},
|
||||
KeySchema: []dynamodb.KeySchemaT{
|
||||
dynamodb.KeySchemaT{"TestHashKey", "HASH"},
|
||||
},
|
||||
ProvisionedThroughput: dynamodb.ProvisionedThroughputT{
|
||||
ReadCapacityUnits: 1,
|
||||
WriteCapacityUnits: 1,
|
||||
},
|
||||
},
|
||||
WithRange: false,
|
||||
}
|
||||
|
||||
var _ = Suite(item_suite)
|
||||
var _ = Suite(item_without_range_suite)
|
||||
|
||||
func (s *ItemSuite) TestConditionalPutUpdateDeleteItem(c *C) {
|
||||
if s.WithRange {
|
||||
// No rangekey test required
|
||||
return
|
||||
}
|
||||
|
||||
attrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("Attr1", "Attr1Val"),
|
||||
}
|
||||
pk := &dynamodb.Key{HashKey: "NewHashKeyVal"}
|
||||
|
||||
// Put
|
||||
if ok, err := s.table.PutItem("NewHashKeyVal", "", attrs); !ok {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
{
|
||||
// Put with condition failed
|
||||
expected := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("Attr1", "expectedAttr1Val").SetExists(true),
|
||||
*dynamodb.NewStringAttribute("AttrNotExists", "").SetExists(false),
|
||||
}
|
||||
if ok, err := s.table.ConditionalPutItem("NewHashKeyVal", "", attrs, expected); ok {
|
||||
c.Errorf("Expect condition does not meet.")
|
||||
} else {
|
||||
c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*")
|
||||
}
|
||||
|
||||
// Add attributes with condition failed
|
||||
if ok, err := s.table.ConditionalAddAttributes(pk, attrs, expected); ok {
|
||||
c.Errorf("Expect condition does not meet.")
|
||||
} else {
|
||||
c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*")
|
||||
}
|
||||
|
||||
// Update attributes with condition failed
|
||||
if ok, err := s.table.ConditionalUpdateAttributes(pk, attrs, expected); ok {
|
||||
c.Errorf("Expect condition does not meet.")
|
||||
} else {
|
||||
c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*")
|
||||
}
|
||||
|
||||
// Delete attributes with condition failed
|
||||
if ok, err := s.table.ConditionalDeleteAttributes(pk, attrs, expected); ok {
|
||||
c.Errorf("Expect condition does not meet.")
|
||||
} else {
|
||||
c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
expected := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("Attr1", "Attr1Val").SetExists(true),
|
||||
}
|
||||
|
||||
// Add attributes with condition met
|
||||
addNewAttrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewNumericAttribute("AddNewAttr1", "10"),
|
||||
*dynamodb.NewNumericAttribute("AddNewAttr2", "20"),
|
||||
}
|
||||
if ok, err := s.table.ConditionalAddAttributes(pk, addNewAttrs, nil); !ok {
|
||||
c.Errorf("Expect condition met. %s", err)
|
||||
}
|
||||
|
||||
// Update attributes with condition met
|
||||
updateAttrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewNumericAttribute("AddNewAttr1", "100"),
|
||||
}
|
||||
if ok, err := s.table.ConditionalUpdateAttributes(pk, updateAttrs, expected); !ok {
|
||||
c.Errorf("Expect condition met. %s", err)
|
||||
}
|
||||
|
||||
// Delete attributes with condition met
|
||||
deleteAttrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewNumericAttribute("AddNewAttr2", ""),
|
||||
}
|
||||
if ok, err := s.table.ConditionalDeleteAttributes(pk, deleteAttrs, expected); !ok {
|
||||
c.Errorf("Expect condition met. %s", err)
|
||||
}
|
||||
|
||||
// Get to verify operations that condition are met
|
||||
item, err := s.table.GetItem(pk)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
if val, ok := item["AddNewAttr1"]; ok {
|
||||
c.Check(val, DeepEquals, dynamodb.NewNumericAttribute("AddNewAttr1", "100"))
|
||||
} else {
|
||||
c.Error("Expect AddNewAttr1 attribute to be added and updated")
|
||||
}
|
||||
|
||||
if _, ok := item["AddNewAttr2"]; ok {
|
||||
c.Error("Expect AddNewAttr2 attribute to be deleted")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Put with condition met
|
||||
expected := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("Attr1", "Attr1Val").SetExists(true),
|
||||
}
|
||||
newattrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("Attr1", "Attr2Val"),
|
||||
}
|
||||
if ok, err := s.table.ConditionalPutItem("NewHashKeyVal", "", newattrs, expected); !ok {
|
||||
c.Errorf("Expect condition met. %s", err)
|
||||
}
|
||||
|
||||
// Get to verify Put operation that condition are met
|
||||
item, err := s.table.GetItem(pk)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
if val, ok := item["Attr1"]; ok {
|
||||
c.Check(val, DeepEquals, dynamodb.NewStringAttribute("Attr1", "Attr2Val"))
|
||||
} else {
|
||||
c.Error("Expect Attr1 attribute to be updated")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Delete with condition failed
|
||||
expected := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("Attr1", "expectedAttr1Val").SetExists(true),
|
||||
}
|
||||
if ok, err := s.table.ConditionalDeleteItem(pk, expected); ok {
|
||||
c.Errorf("Expect condition does not meet.")
|
||||
} else {
|
||||
c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Delete with condition met
|
||||
expected := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("Attr1", "Attr2Val").SetExists(true),
|
||||
}
|
||||
if ok, _ := s.table.ConditionalDeleteItem(pk, expected); !ok {
|
||||
c.Errorf("Expect condition met.")
|
||||
}
|
||||
|
||||
// Get to verify Delete operation
|
||||
_, err := s.table.GetItem(pk)
|
||||
c.Check(err.Error(), Matches, "Item not found")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ItemSuite) TestPutGetDeleteItem(c *C) {
|
||||
attrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("Attr1", "Attr1Val"),
|
||||
}
|
||||
|
||||
var rk string
|
||||
if s.WithRange {
|
||||
rk = "1"
|
||||
}
|
||||
|
||||
// Put
|
||||
if ok, err := s.table.PutItem("NewHashKeyVal", rk, attrs); !ok {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// Get to verify Put operation
|
||||
pk := &dynamodb.Key{HashKey: "NewHashKeyVal", RangeKey: rk}
|
||||
item, err := s.table.GetItem(pk)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
if val, ok := item["TestHashKey"]; ok {
|
||||
c.Check(val, DeepEquals, dynamodb.NewStringAttribute("TestHashKey", "NewHashKeyVal"))
|
||||
} else {
|
||||
c.Error("Expect TestHashKey to be found")
|
||||
}
|
||||
|
||||
if s.WithRange {
|
||||
if val, ok := item["TestRangeKey"]; ok {
|
||||
c.Check(val, DeepEquals, dynamodb.NewNumericAttribute("TestRangeKey", "1"))
|
||||
} else {
|
||||
c.Error("Expect TestRangeKey to be found")
|
||||
}
|
||||
}
|
||||
|
||||
// Delete
|
||||
if ok, _ := s.table.DeleteItem(pk); !ok {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// Get to verify Delete operation
|
||||
_, err = s.table.GetItem(pk)
|
||||
c.Check(err.Error(), Matches, "Item not found")
|
||||
}
|
||||
|
||||
func (s *ItemSuite) TestUpdateItem(c *C) {
|
||||
attrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewNumericAttribute("count", "0"),
|
||||
}
|
||||
|
||||
var rk string
|
||||
if s.WithRange {
|
||||
rk = "1"
|
||||
}
|
||||
|
||||
if ok, err := s.table.PutItem("NewHashKeyVal", rk, attrs); !ok {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// UpdateItem with Add
|
||||
attrs = []dynamodb.Attribute{
|
||||
*dynamodb.NewNumericAttribute("count", "10"),
|
||||
}
|
||||
pk := &dynamodb.Key{HashKey: "NewHashKeyVal", RangeKey: rk}
|
||||
if ok, err := s.table.AddAttributes(pk, attrs); !ok {
|
||||
c.Error(err)
|
||||
}
|
||||
|
||||
// Get to verify Add operation
|
||||
if item, err := s.table.GetItemConsistent(pk, true); err != nil {
|
||||
c.Error(err)
|
||||
} else {
|
||||
if val, ok := item["count"]; ok {
|
||||
c.Check(val, DeepEquals, dynamodb.NewNumericAttribute("count", "10"))
|
||||
} else {
|
||||
c.Error("Expect count to be found")
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateItem with Put
|
||||
attrs = []dynamodb.Attribute{
|
||||
*dynamodb.NewNumericAttribute("count", "100"),
|
||||
}
|
||||
if ok, err := s.table.UpdateAttributes(pk, attrs); !ok {
|
||||
c.Error(err)
|
||||
}
|
||||
|
||||
// Get to verify Put operation
|
||||
if item, err := s.table.GetItem(pk); err != nil {
|
||||
c.Fatal(err)
|
||||
} else {
|
||||
if val, ok := item["count"]; ok {
|
||||
c.Check(val, DeepEquals, dynamodb.NewNumericAttribute("count", "100"))
|
||||
} else {
|
||||
c.Error("Expect count to be found")
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateItem with Delete
|
||||
attrs = []dynamodb.Attribute{
|
||||
*dynamodb.NewNumericAttribute("count", ""),
|
||||
}
|
||||
if ok, err := s.table.DeleteAttributes(pk, attrs); !ok {
|
||||
c.Error(err)
|
||||
}
|
||||
|
||||
// Get to verify Delete operation
|
||||
if item, err := s.table.GetItem(pk); err != nil {
|
||||
c.Error(err)
|
||||
} else {
|
||||
if _, ok := item["count"]; ok {
|
||||
c.Error("Expect count not to be found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ItemSuite) TestUpdateItemWithSet(c *C) {
|
||||
attrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringSetAttribute("list", []string{"A", "B"}),
|
||||
}
|
||||
|
||||
var rk string
|
||||
if s.WithRange {
|
||||
rk = "1"
|
||||
}
|
||||
|
||||
if ok, err := s.table.PutItem("NewHashKeyVal", rk, attrs); !ok {
|
||||
c.Error(err)
|
||||
}
|
||||
|
||||
// UpdateItem with Add
|
||||
attrs = []dynamodb.Attribute{
|
||||
*dynamodb.NewStringSetAttribute("list", []string{"C"}),
|
||||
}
|
||||
pk := &dynamodb.Key{HashKey: "NewHashKeyVal", RangeKey: rk}
|
||||
if ok, err := s.table.AddAttributes(pk, attrs); !ok {
|
||||
c.Error(err)
|
||||
}
|
||||
|
||||
// Get to verify Add operation
|
||||
if item, err := s.table.GetItem(pk); err != nil {
|
||||
c.Error(err)
|
||||
} else {
|
||||
if val, ok := item["list"]; ok {
|
||||
c.Check(val, DeepEquals, dynamodb.NewStringSetAttribute("list", []string{"A", "B", "C"}))
|
||||
} else {
|
||||
c.Error("Expect count to be found")
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateItem with Delete
|
||||
attrs = []dynamodb.Attribute{
|
||||
*dynamodb.NewStringSetAttribute("list", []string{"A"}),
|
||||
}
|
||||
if ok, err := s.table.DeleteAttributes(pk, attrs); !ok {
|
||||
c.Error(err)
|
||||
}
|
||||
|
||||
// Get to verify Delete operation
|
||||
if item, err := s.table.GetItem(pk); err != nil {
|
||||
c.Error(err)
|
||||
} else {
|
||||
if val, ok := item["list"]; ok {
|
||||
c.Check(val, DeepEquals, dynamodb.NewStringSetAttribute("list", []string{"B", "C"}))
|
||||
} else {
|
||||
c.Error("Expect list to be remained")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ItemSuite) TestUpdateItem_new(c *C) {
|
||||
attrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("intval", "1"),
|
||||
}
|
||||
var rk string
|
||||
if s.WithRange {
|
||||
rk = "1"
|
||||
}
|
||||
pk := &dynamodb.Key{HashKey: "UpdateKeyVal", RangeKey: rk}
|
||||
|
||||
num := func(a, b string) dynamodb.Attribute {
|
||||
return *dynamodb.NewNumericAttribute(a, b)
|
||||
}
|
||||
|
||||
checkVal := func(i string) {
|
||||
if item, err := s.table.GetItem(pk); err != nil {
|
||||
c.Error(err)
|
||||
} else {
|
||||
c.Check(item["intval"], DeepEquals, dynamodb.NewNumericAttribute("intval", i))
|
||||
}
|
||||
}
|
||||
|
||||
if ok, err := s.table.PutItem("UpdateKeyVal", rk, attrs); !ok {
|
||||
c.Error(err)
|
||||
}
|
||||
checkVal("1")
|
||||
|
||||
// Simple Increment
|
||||
s.table.UpdateItem(pk).UpdateExpression("SET intval = intval + :incr", num(":incr", "5")).Execute()
|
||||
checkVal("6")
|
||||
|
||||
conditionalUpdate := func(check string) {
|
||||
s.table.UpdateItem(pk).
|
||||
ConditionExpression("intval = :check").
|
||||
UpdateExpression("SET intval = intval + :incr").
|
||||
ExpressionAttributes(num(":check", check), num(":incr", "4")).
|
||||
Execute()
|
||||
}
|
||||
// Conditional increment should be a no-op.
|
||||
conditionalUpdate("42")
|
||||
checkVal("6")
|
||||
|
||||
// conditional increment should succeed this time
|
||||
conditionalUpdate("6")
|
||||
checkVal("10")
|
||||
|
||||
// Update with new values getting values
|
||||
result, err := s.table.UpdateItem(pk).
|
||||
ReturnValues(dynamodb.UPDATED_NEW).
|
||||
UpdateExpression("SET intval = intval + :incr", num(":incr", "2")).
|
||||
Execute()
|
||||
c.Check(err, IsNil)
|
||||
c.Check(result.Attributes["intval"], DeepEquals, num("intval", "12"))
|
||||
checkVal("12")
|
||||
}
|
||||
626
vendor/github.com/goamz/goamz/dynamodb/marshaller.go
сгенерированный
поставляемый
626
vendor/github.com/goamz/goamz/dynamodb/marshaller.go
сгенерированный
поставляемый
@@ -1,626 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func MarshalAttributes(m interface{}) ([]Attribute, error) {
|
||||
v := reflect.ValueOf(m).Elem()
|
||||
|
||||
builder := &attributeBuilder{}
|
||||
builder.buffer = []Attribute{}
|
||||
for _, f := range cachedTypeFields(v.Type()) { // loop on each field
|
||||
fv := fieldByIndex(v, f.index)
|
||||
if !fv.IsValid() || isEmptyValueToOmit(fv) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := builder.reflectToDynamoDBAttribute(f.name, fv)
|
||||
if err != nil {
|
||||
return builder.buffer, err
|
||||
}
|
||||
}
|
||||
|
||||
return builder.buffer, nil
|
||||
}
|
||||
|
||||
func UnmarshalAttributes(attributesRef *map[string]*Attribute, m interface{}) error {
|
||||
rv := reflect.ValueOf(m)
|
||||
if rv.Kind() != reflect.Ptr || rv.IsNil() {
|
||||
return fmt.Errorf("InvalidUnmarshalError reflect.ValueOf(v): %#v, m interface{}: %#v", rv, reflect.TypeOf(m))
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(m).Elem()
|
||||
|
||||
attributes := *attributesRef
|
||||
for _, f := range cachedTypeFields(v.Type()) { // loop on each field
|
||||
fv := fieldByIndex(v, f.index)
|
||||
correlatedAttribute := attributes[f.name]
|
||||
if correlatedAttribute == nil {
|
||||
continue
|
||||
}
|
||||
err := unmarshallAttribute(correlatedAttribute, fv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type attributeBuilder struct {
|
||||
buffer []Attribute
|
||||
}
|
||||
|
||||
func (builder *attributeBuilder) Push(attribute *Attribute) {
|
||||
builder.buffer = append(builder.buffer, *attribute)
|
||||
}
|
||||
|
||||
func unmarshallAttribute(a *Attribute, v reflect.Value) error {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
n, err := strconv.ParseInt(a.Value, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UnmarshalTypeError (bool) %#v: %#v", a.Value, err)
|
||||
}
|
||||
v.SetBool(n != 0)
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n, err := strconv.ParseInt(a.Value, 10, 64)
|
||||
if err != nil || v.OverflowInt(n) {
|
||||
return fmt.Errorf("UnmarshalTypeError (number) %#v: %#v", a.Value, err)
|
||||
}
|
||||
v.SetInt(n)
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
n, err := strconv.ParseUint(a.Value, 10, 64)
|
||||
if err != nil || v.OverflowUint(n) {
|
||||
return fmt.Errorf("UnmarshalTypeError (number) %#v: %#v", a.Value, err)
|
||||
}
|
||||
v.SetUint(n)
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
n, err := strconv.ParseFloat(a.Value, v.Type().Bits())
|
||||
if err != nil || v.OverflowFloat(n) {
|
||||
return fmt.Errorf("UnmarshalTypeError (number) %#v: %#v", a.Value, err)
|
||||
}
|
||||
v.SetFloat(n)
|
||||
|
||||
case reflect.String:
|
||||
v.SetString(a.Value)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.Type().Elem().Kind() == reflect.Uint8 { // byte arrays are a special case
|
||||
b := make([]byte, base64.StdEncoding.DecodedLen(len(a.Value)))
|
||||
n, err := base64.StdEncoding.Decode(b, []byte(a.Value))
|
||||
if err != nil {
|
||||
return fmt.Errorf("UnmarshalTypeError (byte) %#v: %#v", a.Value, err)
|
||||
}
|
||||
v.Set(reflect.ValueOf(b[0:n]))
|
||||
break
|
||||
}
|
||||
|
||||
if a.SetType() { // Special NS and SS types should be correctly handled
|
||||
nativeSetCreated := false
|
||||
switch v.Type().Elem().Kind() {
|
||||
case reflect.Bool:
|
||||
nativeSetCreated = true
|
||||
arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues))
|
||||
for i, aval := range a.SetValues {
|
||||
n, err := strconv.ParseInt(aval, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UnmarshalSetTypeError (bool) %#v: %#v", aval, err)
|
||||
}
|
||||
arry.Index(i).SetBool(n != 0)
|
||||
}
|
||||
v.Set(arry)
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
nativeSetCreated = true
|
||||
arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues))
|
||||
for i, aval := range a.SetValues {
|
||||
n, err := strconv.ParseInt(aval, 10, 64)
|
||||
if err != nil || arry.Index(i).OverflowInt(n) {
|
||||
return fmt.Errorf("UnmarshalSetTypeError (number) %#v: %#v", aval, err)
|
||||
}
|
||||
arry.Index(i).SetInt(n)
|
||||
}
|
||||
v.Set(arry)
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
nativeSetCreated = true
|
||||
arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues))
|
||||
for i, aval := range a.SetValues {
|
||||
n, err := strconv.ParseUint(aval, 10, 64)
|
||||
if err != nil || arry.Index(i).OverflowUint(n) {
|
||||
return fmt.Errorf("UnmarshalSetTypeError (number) %#v: %#v", aval, err)
|
||||
}
|
||||
arry.Index(i).SetUint(n)
|
||||
}
|
||||
v.Set(arry)
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
nativeSetCreated = true
|
||||
arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues))
|
||||
for i, aval := range a.SetValues {
|
||||
n, err := strconv.ParseFloat(aval, arry.Index(i).Type().Bits())
|
||||
if err != nil || arry.Index(i).OverflowFloat(n) {
|
||||
return fmt.Errorf("UnmarshalSetTypeError (number) %#v: %#v", aval, err)
|
||||
}
|
||||
arry.Index(i).SetFloat(n)
|
||||
}
|
||||
v.Set(arry)
|
||||
|
||||
case reflect.String:
|
||||
nativeSetCreated = true
|
||||
arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues))
|
||||
for i, aval := range a.SetValues {
|
||||
arry.Index(i).SetString(aval)
|
||||
}
|
||||
v.Set(arry)
|
||||
}
|
||||
|
||||
if nativeSetCreated {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Slices can be marshalled as nil, but otherwise are handled
|
||||
// as arrays.
|
||||
fallthrough
|
||||
case reflect.Array, reflect.Struct, reflect.Map, reflect.Interface, reflect.Ptr:
|
||||
unmarshalled := reflect.New(v.Type())
|
||||
err := json.Unmarshal([]byte(a.Value), unmarshalled.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Set(unmarshalled.Elem())
|
||||
|
||||
default:
|
||||
return fmt.Errorf("UnsupportedTypeError %#v", v.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// reflectValueQuoted writes the value in v to the output.
|
||||
// If quoted is true, the serialization is wrapped in a JSON string.
|
||||
func (e *attributeBuilder) reflectToDynamoDBAttribute(name string, v reflect.Value) error {
|
||||
if !v.IsValid() {
|
||||
return nil
|
||||
} // don't build
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
|
||||
rv, err := numericReflectedValueString(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.Push(NewNumericAttribute(name, rv))
|
||||
|
||||
case reflect.String:
|
||||
e.Push(NewStringAttribute(name, v.String()))
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
break
|
||||
}
|
||||
if v.Type().Elem().Kind() == reflect.Uint8 {
|
||||
// Byte slices are treated as errors
|
||||
s := v.Bytes()
|
||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
|
||||
base64.StdEncoding.Encode(dst, s)
|
||||
e.Push(NewStringAttribute(name, string(dst)))
|
||||
break
|
||||
}
|
||||
|
||||
// Special NS and SS types should be correctly handled
|
||||
nativeSetCreated := false
|
||||
switch v.Type().Elem().Kind() {
|
||||
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
|
||||
nativeSetCreated = true
|
||||
arrystrings := make([]string, v.Len())
|
||||
for i, _ := range arrystrings {
|
||||
var err error
|
||||
arrystrings[i], err = numericReflectedValueString(v.Index(i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
e.Push(NewNumericSetAttribute(name, arrystrings))
|
||||
case reflect.String: // simple copy will suffice
|
||||
nativeSetCreated = true
|
||||
arrystrings := make([]string, v.Len())
|
||||
for i, _ := range arrystrings {
|
||||
arrystrings[i] = v.Index(i).String()
|
||||
}
|
||||
e.Push(NewStringSetAttribute(name, arrystrings))
|
||||
}
|
||||
|
||||
if nativeSetCreated {
|
||||
break
|
||||
}
|
||||
|
||||
// Slices can be marshalled as nil, but otherwise are handled
|
||||
// as arrays.
|
||||
fallthrough
|
||||
case reflect.Array, reflect.Struct, reflect.Map, reflect.Interface, reflect.Ptr:
|
||||
jsonVersion, err := json.Marshal(v.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
escapedJson := `"` + string(jsonVersion) + `"` // strconv.Quote not required because the entire string is escaped from json Marshall
|
||||
e.Push(NewStringAttribute(name, escapedJson[1:len(escapedJson)-1]))
|
||||
|
||||
default:
|
||||
return fmt.Errorf("UnsupportedTypeError %#v", v.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func numericReflectedValueString(v reflect.Value) (string, error) {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
x := v.Bool()
|
||||
if x {
|
||||
return "1", nil
|
||||
} else {
|
||||
return "0", nil
|
||||
}
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(v.Int(), 10), nil
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return strconv.FormatUint(v.Uint(), 10), nil
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
f := v.Float()
|
||||
if math.IsInf(f, 0) || math.IsNaN(f) {
|
||||
return "", fmt.Errorf("UnsupportedValueError %#v (formatted float: %s)", v, strconv.FormatFloat(f, 'g', -1, v.Type().Bits()))
|
||||
}
|
||||
return strconv.FormatFloat(f, 'g', -1, v.Type().Bits()), nil
|
||||
}
|
||||
return "", fmt.Errorf("UnsupportedNumericValueError %#v", v.Type())
|
||||
}
|
||||
|
||||
// In DynamoDB we should omit empty value in some type
|
||||
// See http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html
|
||||
func isEmptyValueToOmit(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String, reflect.Interface, reflect.Ptr:
|
||||
// should omit if empty value
|
||||
return isEmptyValue(v)
|
||||
}
|
||||
// otherwise should not omit
|
||||
return false
|
||||
}
|
||||
|
||||
// ---------------- Below are copied handy functions from http://golang.org/src/pkg/encoding/json/encode.go --------------------------------
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
||||
for _, i := range index {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
v = v.Field(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// A field represents a single field found in a struct.
|
||||
type field struct {
|
||||
name string
|
||||
tag bool
|
||||
index []int
|
||||
typ reflect.Type
|
||||
omitEmpty bool
|
||||
quoted bool
|
||||
}
|
||||
|
||||
// byName sorts field by name, breaking ties with depth,
|
||||
// then breaking ties with "name came from json tag", then
|
||||
// breaking ties with index sequence.
|
||||
type byName []field
|
||||
|
||||
func (x byName) Len() int { return len(x) }
|
||||
|
||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byName) Less(i, j int) bool {
|
||||
if x[i].name != x[j].name {
|
||||
return x[i].name < x[j].name
|
||||
}
|
||||
if len(x[i].index) != len(x[j].index) {
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
if x[i].tag != x[j].tag {
|
||||
return x[i].tag
|
||||
}
|
||||
return byIndex(x).Less(i, j)
|
||||
}
|
||||
|
||||
// byIndex sorts field by index sequence.
|
||||
type byIndex []field
|
||||
|
||||
func (x byIndex) Len() int { return len(x) }
|
||||
|
||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byIndex) Less(i, j int) bool {
|
||||
for k, xik := range x[i].index {
|
||||
if k >= len(x[j].index) {
|
||||
return false
|
||||
}
|
||||
if xik != x[j].index[k] {
|
||||
return xik < x[j].index[k]
|
||||
}
|
||||
}
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
|
||||
func isValidTag(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
switch {
|
||||
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
|
||||
// Backslash and quote chars are reserved, but
|
||||
// otherwise any punctuation chars are allowed
|
||||
// in a tag name.
|
||||
default:
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// tagOptions is the string following a comma in a struct field's "json"
|
||||
// tag, or the empty string. It does not include the leading comma.
|
||||
type tagOptions string
|
||||
|
||||
// Contains returns whether checks that a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (o tagOptions) Contains(optionName string) bool {
|
||||
if len(o) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(o)
|
||||
for s != "" {
|
||||
var next string
|
||||
i := strings.Index(s, ",")
|
||||
if i >= 0 {
|
||||
s, next = s[:i], s[i+1:]
|
||||
}
|
||||
if s == optionName {
|
||||
return true
|
||||
}
|
||||
s = next
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseTag splits a struct field's json tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx], tagOptions(tag[idx+1:])
|
||||
}
|
||||
return tag, tagOptions("")
|
||||
}
|
||||
|
||||
// typeFields returns a list of fields that JSON should recognize for the given type.
|
||||
// The algorithm is breadth-first search over the set of structs to include - the top struct
|
||||
// and then any reachable anonymous structs.
|
||||
func typeFields(t reflect.Type) []field {
|
||||
// Anonymous fields to explore at the current level and the next.
|
||||
current := []field{}
|
||||
next := []field{{typ: t}}
|
||||
|
||||
// Count of queued names for current level and the next.
|
||||
count := map[reflect.Type]int{}
|
||||
nextCount := map[reflect.Type]int{}
|
||||
|
||||
// Types already visited at an earlier level.
|
||||
visited := map[reflect.Type]bool{}
|
||||
|
||||
// Fields found.
|
||||
var fields []field
|
||||
|
||||
for len(next) > 0 {
|
||||
current, next = next, current[:0]
|
||||
count, nextCount = nextCount, map[reflect.Type]int{}
|
||||
|
||||
for _, f := range current {
|
||||
if visited[f.typ] {
|
||||
continue
|
||||
}
|
||||
visited[f.typ] = true
|
||||
|
||||
// Scan f.typ for fields to include.
|
||||
for i := 0; i < f.typ.NumField(); i++ {
|
||||
sf := f.typ.Field(i)
|
||||
if sf.PkgPath != "" { // unexported
|
||||
continue
|
||||
}
|
||||
tag := sf.Tag.Get("json")
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
name, opts := parseTag(tag)
|
||||
if !isValidTag(name) {
|
||||
name = ""
|
||||
}
|
||||
index := make([]int, len(f.index)+1)
|
||||
copy(index, f.index)
|
||||
index[len(f.index)] = i
|
||||
|
||||
ft := sf.Type
|
||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
||||
// Follow pointer.
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
// Record found field and index sequence.
|
||||
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
||||
tagged := name != ""
|
||||
if name == "" {
|
||||
name = sf.Name
|
||||
}
|
||||
fields = append(fields, field{name, tagged, index, ft,
|
||||
opts.Contains("omitempty"), opts.Contains("string")})
|
||||
if count[f.typ] > 1 {
|
||||
// If there were multiple instances, add a second,
|
||||
// so that the annihilation code will see a duplicate.
|
||||
// It only cares about the distinction between 1 or 2,
|
||||
// so don't bother generating any more copies.
|
||||
fields = append(fields, fields[len(fields)-1])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Record new anonymous struct to explore in next round.
|
||||
nextCount[ft]++
|
||||
if nextCount[ft] == 1 {
|
||||
next = append(next, field{name: ft.Name(), index: index, typ: ft})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(byName(fields))
|
||||
|
||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
||||
// except that fields with JSON tags are promoted.
|
||||
|
||||
// The fields are sorted in primary order of name, secondary order
|
||||
// of field index length. Loop over names; for each name, delete
|
||||
// hidden fields by choosing the one dominant field that survives.
|
||||
out := fields[:0]
|
||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
||||
// One iteration per name.
|
||||
// Find the sequence of fields with the name of this first field.
|
||||
fi := fields[i]
|
||||
name := fi.name
|
||||
for advance = 1; i+advance < len(fields); advance++ {
|
||||
fj := fields[i+advance]
|
||||
if fj.name != name {
|
||||
break
|
||||
}
|
||||
}
|
||||
if advance == 1 { // Only one field with this name
|
||||
out = append(out, fi)
|
||||
continue
|
||||
}
|
||||
dominant, ok := dominantField(fields[i : i+advance])
|
||||
if ok {
|
||||
out = append(out, dominant)
|
||||
}
|
||||
}
|
||||
|
||||
fields = out
|
||||
sort.Sort(byIndex(fields))
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// dominantField looks through the fields, all of which are known to
|
||||
// have the same name, to find the single field that dominates the
|
||||
// others using Go's embedding rules, modified by the presence of
|
||||
// JSON tags. If there are multiple top-level fields, the boolean
|
||||
// will be false: This condition is an error in Go and we skip all
|
||||
// the fields.
|
||||
func dominantField(fields []field) (field, bool) {
|
||||
// The fields are sorted in increasing index-length order. The winner
|
||||
// must therefore be one with the shortest index length. Drop all
|
||||
// longer entries, which is easy: just truncate the slice.
|
||||
length := len(fields[0].index)
|
||||
tagged := -1 // Index of first tagged field.
|
||||
for i, f := range fields {
|
||||
if len(f.index) > length {
|
||||
fields = fields[:i]
|
||||
break
|
||||
}
|
||||
if f.tag {
|
||||
if tagged >= 0 {
|
||||
// Multiple tagged fields at the same level: conflict.
|
||||
// Return no field.
|
||||
return field{}, false
|
||||
}
|
||||
tagged = i
|
||||
}
|
||||
}
|
||||
if tagged >= 0 {
|
||||
return fields[tagged], true
|
||||
}
|
||||
// All remaining fields have the same length. If there's more than one,
|
||||
// we have a conflict (two fields named "X" at the same level) and we
|
||||
// return no field.
|
||||
if len(fields) > 1 {
|
||||
return field{}, false
|
||||
}
|
||||
return fields[0], true
|
||||
}
|
||||
|
||||
var fieldCache struct {
|
||||
sync.RWMutex
|
||||
m map[reflect.Type][]field
|
||||
}
|
||||
|
||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
||||
func cachedTypeFields(t reflect.Type) []field {
|
||||
fieldCache.RLock()
|
||||
f := fieldCache.m[t]
|
||||
fieldCache.RUnlock()
|
||||
if f != nil {
|
||||
return f
|
||||
}
|
||||
|
||||
// Compute fields without lock.
|
||||
// Might duplicate effort but won't hold other computations back.
|
||||
f = typeFields(t)
|
||||
if f == nil {
|
||||
f = []field{}
|
||||
}
|
||||
|
||||
fieldCache.Lock()
|
||||
if fieldCache.m == nil {
|
||||
fieldCache.m = map[reflect.Type][]field{}
|
||||
}
|
||||
fieldCache.m[t] = f
|
||||
fieldCache.Unlock()
|
||||
return f
|
||||
}
|
||||
283
vendor/github.com/goamz/goamz/dynamodb/marshaller_test.go
сгенерированный
поставляемый
283
vendor/github.com/goamz/goamz/dynamodb/marshaller_test.go
сгенерированный
поставляемый
@@ -1,283 +0,0 @@
|
||||
package dynamodb_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/dynamodb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type TestSubStruct struct {
|
||||
SubBool bool
|
||||
SubInt int
|
||||
SubString string
|
||||
SubStringArray []string
|
||||
}
|
||||
|
||||
type TestStruct struct {
|
||||
TestBool bool
|
||||
TestInt int
|
||||
TestInt32 int32
|
||||
TestInt64 int64
|
||||
TestUint uint
|
||||
TestFloat32 float32
|
||||
TestFloat64 float64
|
||||
TestString string
|
||||
TestByteArray []byte
|
||||
TestStringArray []string
|
||||
TestIntArray []int
|
||||
TestInt8Array []int8
|
||||
TestFloatArray []float64
|
||||
TestSub TestSubStruct
|
||||
}
|
||||
|
||||
type TestStructTime struct {
|
||||
TestTime time.Time
|
||||
}
|
||||
|
||||
func testObject() *TestStruct {
|
||||
return &TestStruct{
|
||||
TestBool: true,
|
||||
TestInt: -99,
|
||||
TestInt32: 999,
|
||||
TestInt64: 9999,
|
||||
TestUint: 99,
|
||||
TestFloat32: 9.9999,
|
||||
TestFloat64: 99.999999,
|
||||
TestString: "test",
|
||||
TestByteArray: []byte("bytes"),
|
||||
TestStringArray: []string{"test1", "test2", "test3", "test4"},
|
||||
TestIntArray: []int{0, 1, 12, 123, 1234, 12345},
|
||||
TestInt8Array: []int8{0, 1, 12, 123},
|
||||
TestFloatArray: []float64{0.1, 1.1, 1.2, 1.23, 1.234, 1.2345},
|
||||
TestSub: TestSubStruct{
|
||||
SubBool: true,
|
||||
SubInt: 2,
|
||||
SubString: "subtest",
|
||||
SubStringArray: []string{"sub1", "sub2", "sub3"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testObjectTime() *TestStructTime {
|
||||
t, _ := time.Parse("Jan 2, 2006 at 3:04pm", "Mar 3, 2003 at 5:03pm")
|
||||
return &TestStructTime{
|
||||
TestTime: t,
|
||||
}
|
||||
}
|
||||
|
||||
func testObjectWithZeroValues() *TestStruct {
|
||||
return &TestStruct{}
|
||||
}
|
||||
|
||||
func testObjectWithNilSets() *TestStruct {
|
||||
return &TestStruct{
|
||||
TestBool: true,
|
||||
TestInt: -99,
|
||||
TestInt32: 999,
|
||||
TestInt64: 9999,
|
||||
TestUint: 99,
|
||||
TestFloat32: 9.9999,
|
||||
TestFloat64: 99.999999,
|
||||
TestString: "test",
|
||||
TestByteArray: []byte("bytes"),
|
||||
TestStringArray: []string(nil),
|
||||
TestIntArray: []int(nil),
|
||||
TestFloatArray: []float64(nil),
|
||||
TestSub: TestSubStruct{
|
||||
SubBool: true,
|
||||
SubInt: 2,
|
||||
SubString: "subtest",
|
||||
SubStringArray: []string{"sub1", "sub2", "sub3"},
|
||||
},
|
||||
}
|
||||
}
|
||||
func testObjectWithEmptySets() *TestStruct {
|
||||
return &TestStruct{
|
||||
TestBool: true,
|
||||
TestInt: -99,
|
||||
TestInt32: 999,
|
||||
TestInt64: 9999,
|
||||
TestUint: 99,
|
||||
TestFloat32: 9.9999,
|
||||
TestFloat64: 99.999999,
|
||||
TestString: "test",
|
||||
TestByteArray: []byte("bytes"),
|
||||
TestStringArray: []string{},
|
||||
TestIntArray: []int{},
|
||||
TestFloatArray: []float64{},
|
||||
TestSub: TestSubStruct{
|
||||
SubBool: true,
|
||||
SubInt: 2,
|
||||
SubString: "subtest",
|
||||
SubStringArray: []string{"sub1", "sub2", "sub3"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testAttrs() []dynamodb.Attribute {
|
||||
return []dynamodb.Attribute{
|
||||
dynamodb.Attribute{Type: "N", Name: "TestBool", Value: "1", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt", Value: "-99", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt32", Value: "999", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt64", Value: "9999", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestUint", Value: "99", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestFloat32", Value: "9.9999", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestFloat64", Value: "99.999999", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "S", Name: "TestString", Value: "test", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "S", Name: "TestByteArray", Value: "Ynl0ZXM=", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "SS", Name: "TestStringArray", Value: "", SetValues: []string{"test1", "test2", "test3", "test4"}},
|
||||
dynamodb.Attribute{Type: "NS", Name: "TestIntArray", Value: "", SetValues: []string{"0", "1", "12", "123", "1234", "12345"}},
|
||||
dynamodb.Attribute{Type: "NS", Name: "TestInt8Array", Value: "", SetValues: []string{"0", "1", "12", "123"}},
|
||||
dynamodb.Attribute{Type: "NS", Name: "TestFloatArray", Value: "", SetValues: []string{"0.1", "1.1", "1.2", "1.23", "1.234", "1.2345"}},
|
||||
dynamodb.Attribute{Type: "S", Name: "TestSub", Value: `{"SubBool":true,"SubInt":2,"SubString":"subtest","SubStringArray":["sub1","sub2","sub3"]}`, SetValues: []string(nil)},
|
||||
}
|
||||
}
|
||||
|
||||
func testAttrsTime() []dynamodb.Attribute {
|
||||
return []dynamodb.Attribute{
|
||||
dynamodb.Attribute{Type: "S", Name: "TestTime", Value: "\"2003-03-03T17:03:00Z\"", SetValues: []string(nil)},
|
||||
}
|
||||
}
|
||||
|
||||
func testAttrsWithZeroValues() []dynamodb.Attribute {
|
||||
return []dynamodb.Attribute{
|
||||
dynamodb.Attribute{Type: "N", Name: "TestBool", Value: "0", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt", Value: "0", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt32", Value: "0", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt64", Value: "0", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestUint", Value: "0", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestFloat32", Value: "0", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestFloat64", Value: "0", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "S", Name: "TestSub", Value: `{"SubBool":false,"SubInt":0,"SubString":"","SubStringArray":null}`, SetValues: []string(nil)},
|
||||
}
|
||||
}
|
||||
|
||||
func testAttrsWithNilSets() []dynamodb.Attribute {
|
||||
return []dynamodb.Attribute{
|
||||
dynamodb.Attribute{Type: "N", Name: "TestBool", Value: "1", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt", Value: "-99", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt32", Value: "999", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestInt64", Value: "9999", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestUint", Value: "99", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestFloat32", Value: "9.9999", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "N", Name: "TestFloat64", Value: "99.999999", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "S", Name: "TestString", Value: "test", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "S", Name: "TestByteArray", Value: "Ynl0ZXM=", SetValues: []string(nil)},
|
||||
dynamodb.Attribute{Type: "S", Name: "TestSub", Value: `{"SubBool":true,"SubInt":2,"SubString":"subtest","SubStringArray":["sub1","sub2","sub3"]}`, SetValues: []string(nil)},
|
||||
}
|
||||
}
|
||||
|
||||
type MarshallerSuite struct {
|
||||
}
|
||||
|
||||
var _ = Suite(&MarshallerSuite{})
|
||||
|
||||
func (s *MarshallerSuite) TestMarshal(c *C) {
|
||||
testObj := testObject()
|
||||
attrs, err := dynamodb.MarshalAttributes(testObj)
|
||||
if err != nil {
|
||||
c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err)
|
||||
}
|
||||
|
||||
expected := testAttrs()
|
||||
c.Check(attrs, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *MarshallerSuite) TestUnmarshal(c *C) {
|
||||
testObj := &TestStruct{}
|
||||
|
||||
attrMap := map[string]*dynamodb.Attribute{}
|
||||
attrs := testAttrs()
|
||||
for i, _ := range attrs {
|
||||
attrMap[attrs[i].Name] = &attrs[i]
|
||||
}
|
||||
|
||||
err := dynamodb.UnmarshalAttributes(&attrMap, testObj)
|
||||
if err != nil {
|
||||
c.Fatalf("Error from dynamodb.UnmarshalAttributes: %#v (Built: %#v)", err, testObj)
|
||||
}
|
||||
|
||||
expected := testObject()
|
||||
c.Check(testObj, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *MarshallerSuite) TestMarshalTime(c *C) {
|
||||
testObj := testObjectTime()
|
||||
attrs, err := dynamodb.MarshalAttributes(testObj)
|
||||
if err != nil {
|
||||
c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err)
|
||||
}
|
||||
|
||||
expected := testAttrsTime()
|
||||
c.Check(attrs, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *MarshallerSuite) TestUnmarshalTime(c *C) {
|
||||
testObj := &TestStructTime{}
|
||||
|
||||
attrMap := map[string]*dynamodb.Attribute{}
|
||||
attrs := testAttrsTime()
|
||||
for i, _ := range attrs {
|
||||
attrMap[attrs[i].Name] = &attrs[i]
|
||||
}
|
||||
|
||||
err := dynamodb.UnmarshalAttributes(&attrMap, testObj)
|
||||
if err != nil {
|
||||
c.Fatalf("Error from dynamodb.UnmarshalAttributes: %#v (Built: %#v)", err, testObj)
|
||||
}
|
||||
|
||||
expected := testObjectTime()
|
||||
c.Check(testObj, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *MarshallerSuite) TestMarshalNilSets(c *C) {
|
||||
testObj := testObjectWithNilSets()
|
||||
attrs, err := dynamodb.MarshalAttributes(testObj)
|
||||
if err != nil {
|
||||
c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err)
|
||||
}
|
||||
|
||||
expected := testAttrsWithNilSets()
|
||||
c.Check(attrs, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *MarshallerSuite) TestMarshalZeroValues(c *C) {
|
||||
testObj := testObjectWithZeroValues()
|
||||
attrs, err := dynamodb.MarshalAttributes(testObj)
|
||||
if err != nil {
|
||||
c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err)
|
||||
}
|
||||
|
||||
expected := testAttrsWithZeroValues()
|
||||
c.Check(attrs, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *MarshallerSuite) TestMarshalEmptySets(c *C) {
|
||||
testObj := testObjectWithEmptySets()
|
||||
attrs, err := dynamodb.MarshalAttributes(testObj)
|
||||
if err != nil {
|
||||
c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err)
|
||||
}
|
||||
|
||||
expected := testAttrsWithNilSets()
|
||||
c.Check(attrs, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *MarshallerSuite) TestUnmarshalEmptySets(c *C) {
|
||||
testObj := &TestStruct{}
|
||||
|
||||
attrMap := map[string]*dynamodb.Attribute{}
|
||||
attrs := testAttrsWithNilSets()
|
||||
for i, _ := range attrs {
|
||||
attrMap[attrs[i].Name] = &attrs[i]
|
||||
}
|
||||
|
||||
err := dynamodb.UnmarshalAttributes(&attrMap, testObj)
|
||||
if err != nil {
|
||||
c.Fatalf("Error from dynamodb.UnmarshalAttributes: %#v (Built: %#v)", err, testObj)
|
||||
}
|
||||
|
||||
expected := testObjectWithNilSets()
|
||||
c.Check(testObj, DeepEquals, expected)
|
||||
}
|
||||
111
vendor/github.com/goamz/goamz/dynamodb/query.go
сгенерированный
поставляемый
111
vendor/github.com/goamz/goamz/dynamodb/query.go
сгенерированный
поставляемый
@@ -1,111 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
simplejson "github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
func (t *Table) Query(attributeComparisons []AttributeComparison) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKeyConditions(attributeComparisons)
|
||||
return runQuery(q, t)
|
||||
}
|
||||
|
||||
func (t *Table) QueryOnIndex(attributeComparisons []AttributeComparison, indexName string) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKeyConditions(attributeComparisons)
|
||||
q.AddIndex(indexName)
|
||||
return runQuery(q, t)
|
||||
}
|
||||
|
||||
func (t *Table) QueryOnIndexDescending(attributeComparisons []AttributeComparison, indexName string) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKeyConditions(attributeComparisons)
|
||||
q.AddIndex(indexName)
|
||||
q.ScanIndexDescending()
|
||||
return runQuery(q, t)
|
||||
}
|
||||
|
||||
func (t *Table) LimitedQuery(attributeComparisons []AttributeComparison, limit int64) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKeyConditions(attributeComparisons)
|
||||
q.AddLimit(limit)
|
||||
return runQuery(q, t)
|
||||
}
|
||||
|
||||
func (t *Table) LimitedQueryOnIndex(attributeComparisons []AttributeComparison, indexName string, limit int64) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKeyConditions(attributeComparisons)
|
||||
q.AddIndex(indexName)
|
||||
q.AddLimit(limit)
|
||||
return runQuery(q, t)
|
||||
}
|
||||
|
||||
func (t *Table) LimitedQueryDescending(attributeComparisons []AttributeComparison, limit int64) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKeyConditions(attributeComparisons)
|
||||
q.AddLimit(limit)
|
||||
q.ScanIndexDescending()
|
||||
return runQuery(q, t)
|
||||
}
|
||||
|
||||
func (t *Table) LimitedQueryOnIndexDescending(attributeComparisons []AttributeComparison, indexName string, limit int64) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKeyConditions(attributeComparisons)
|
||||
q.AddIndex(indexName)
|
||||
q.AddLimit(limit)
|
||||
q.ScanIndexDescending()
|
||||
return runQuery(q, t)
|
||||
}
|
||||
|
||||
func (t *Table) CountQuery(attributeComparisons []AttributeComparison) (int64, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddKeyConditions(attributeComparisons)
|
||||
q.AddSelect("COUNT")
|
||||
jsonResponse, err := t.Server.queryServer("DynamoDB_20120810.Query", q)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
itemCount, err := json.Get("Count").Int64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return itemCount, nil
|
||||
}
|
||||
|
||||
func runQuery(q *Query, t *Table) ([]map[string]*Attribute, error) {
|
||||
jsonResponse, err := t.Server.queryServer("DynamoDB_20120810.Query", q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemCount, err := json.Get("Count").Int()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
results := make([]map[string]*Attribute, itemCount)
|
||||
|
||||
for i, _ := range results {
|
||||
item, err := json.Get("Items").GetIndex(i).Map()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
results[i] = parseAttributes(item)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
362
vendor/github.com/goamz/goamz/dynamodb/query_builder.go
сгенерированный
поставляемый
362
vendor/github.com/goamz/goamz/dynamodb/query_builder.go
сгенерированный
поставляемый
@@ -1,362 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type msi map[string]interface{}
|
||||
type Query struct {
|
||||
buffer msi
|
||||
}
|
||||
|
||||
func NewEmptyQuery() *Query {
|
||||
return &Query{msi{}}
|
||||
}
|
||||
|
||||
func NewQuery(t *Table) *Query {
|
||||
q := &Query{msi{}}
|
||||
q.addTable(t)
|
||||
return q
|
||||
}
|
||||
|
||||
// This way of specifing the key is used when doing a Get.
|
||||
// If rangeKey is "", it is assumed to not want to be used
|
||||
func (q *Query) AddKey(t *Table, key *Key) {
|
||||
k := t.Key
|
||||
keymap := msi{
|
||||
k.KeyAttribute.Name: msi{
|
||||
k.KeyAttribute.Type: key.HashKey},
|
||||
}
|
||||
if k.HasRange() {
|
||||
keymap[k.RangeAttribute.Name] = msi{k.RangeAttribute.Type: key.RangeKey}
|
||||
}
|
||||
|
||||
q.buffer["Key"] = keymap
|
||||
}
|
||||
|
||||
func keyAttributes(t *Table, key *Key) msi {
|
||||
k := t.Key
|
||||
|
||||
out := msi{}
|
||||
out[k.KeyAttribute.Name] = msi{k.KeyAttribute.Type: key.HashKey}
|
||||
if k.HasRange() {
|
||||
out[k.RangeAttribute.Name] = msi{k.RangeAttribute.Type: key.RangeKey}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (q *Query) AddAttributesToGet(attributes []string) {
|
||||
if len(attributes) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
q.buffer["AttributesToGet"] = attributes
|
||||
}
|
||||
|
||||
func (q *Query) ConsistentRead(c bool) {
|
||||
if c == true {
|
||||
q.buffer["ConsistentRead"] = "true" //String "true", not bool true
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) AddGetRequestItems(tableKeys map[*Table][]Key) {
|
||||
requestitems := msi{}
|
||||
for table, keys := range tableKeys {
|
||||
keyslist := []msi{}
|
||||
for _, key := range keys {
|
||||
keyslist = append(keyslist, keyAttributes(table, &key))
|
||||
}
|
||||
requestitems[table.Name] = msi{"Keys": keyslist}
|
||||
}
|
||||
q.buffer["RequestItems"] = requestitems
|
||||
}
|
||||
|
||||
func (q *Query) AddWriteRequestItems(tableItems map[*Table]map[string][][]Attribute) {
|
||||
b := q.buffer
|
||||
|
||||
b["RequestItems"] = func() msi {
|
||||
out := msi{}
|
||||
for table, itemActions := range tableItems {
|
||||
out[table.Name] = func() interface{} {
|
||||
out2 := []interface{}{}
|
||||
|
||||
// here breaks an order of array....
|
||||
// For now, we iterate over sorted key by action for stable testing
|
||||
keys := []string{}
|
||||
for k := range itemActions {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for ki := range keys {
|
||||
action := keys[ki]
|
||||
items := itemActions[action]
|
||||
for _, attributes := range items {
|
||||
Item_or_Key := map[bool]string{true: "Item", false: "Key"}[action == "Put"]
|
||||
out2 = append(out2, msi{action + "Request": msi{Item_or_Key: attributeList(attributes)}})
|
||||
}
|
||||
}
|
||||
return out2
|
||||
}()
|
||||
}
|
||||
return out
|
||||
}()
|
||||
}
|
||||
|
||||
func (q *Query) AddCreateRequestTable(description TableDescriptionT) {
|
||||
b := q.buffer
|
||||
|
||||
attDefs := []interface{}{}
|
||||
for _, attr := range description.AttributeDefinitions {
|
||||
attDefs = append(attDefs, msi{
|
||||
"AttributeName": attr.Name,
|
||||
"AttributeType": attr.Type,
|
||||
})
|
||||
}
|
||||
b["AttributeDefinitions"] = attDefs
|
||||
b["KeySchema"] = description.KeySchema
|
||||
b["TableName"] = description.TableName
|
||||
b["ProvisionedThroughput"] = msi{
|
||||
"ReadCapacityUnits": int(description.ProvisionedThroughput.ReadCapacityUnits),
|
||||
"WriteCapacityUnits": int(description.ProvisionedThroughput.WriteCapacityUnits),
|
||||
}
|
||||
|
||||
if description.StreamSpecification.StreamEnabled {
|
||||
b["StreamSpecification"] = msi{
|
||||
"StreamEnabled": "true",
|
||||
"StreamViewType": description.StreamSpecification.StreamViewType,
|
||||
}
|
||||
}
|
||||
|
||||
localSecondaryIndexes := []interface{}{}
|
||||
|
||||
for _, ind := range description.LocalSecondaryIndexes {
|
||||
localSecondaryIndexes = append(localSecondaryIndexes, msi{
|
||||
"IndexName": ind.IndexName,
|
||||
"KeySchema": ind.KeySchema,
|
||||
"Projection": ind.Projection,
|
||||
})
|
||||
}
|
||||
|
||||
globalSecondaryIndexes := []interface{}{}
|
||||
intmax := func(x, y int64) int64 {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
for _, ind := range description.GlobalSecondaryIndexes {
|
||||
rec := msi{
|
||||
"IndexName": ind.IndexName,
|
||||
"KeySchema": ind.KeySchema,
|
||||
"Projection": ind.Projection,
|
||||
}
|
||||
// need at least one unit, and since go's max() is float based.
|
||||
rec["ProvisionedThroughput"] = msi{
|
||||
"ReadCapacityUnits": intmax(1, ind.ProvisionedThroughput.ReadCapacityUnits),
|
||||
"WriteCapacityUnits": intmax(1, ind.ProvisionedThroughput.WriteCapacityUnits),
|
||||
}
|
||||
globalSecondaryIndexes = append(globalSecondaryIndexes, rec)
|
||||
}
|
||||
|
||||
if len(localSecondaryIndexes) > 0 {
|
||||
b["LocalSecondaryIndexes"] = localSecondaryIndexes
|
||||
}
|
||||
|
||||
if len(globalSecondaryIndexes) > 0 {
|
||||
b["GlobalSecondaryIndexes"] = globalSecondaryIndexes
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) AddDeleteRequestTable(description TableDescriptionT) {
|
||||
b := q.buffer
|
||||
b["TableName"] = description.TableName
|
||||
}
|
||||
|
||||
func (q *Query) AddUpdateRequestTable(description TableDescriptionT) {
|
||||
b := q.buffer
|
||||
|
||||
attDefs := []interface{}{}
|
||||
for _, attr := range description.AttributeDefinitions {
|
||||
attDefs = append(attDefs, msi{
|
||||
"AttributeName": attr.Name,
|
||||
"AttributeType": attr.Type,
|
||||
})
|
||||
}
|
||||
if len(attDefs) > 0 {
|
||||
b["AttributeDefinitions"] = attDefs
|
||||
}
|
||||
b["TableName"] = description.TableName
|
||||
b["ProvisionedThroughput"] = msi{
|
||||
"ReadCapacityUnits": int(description.ProvisionedThroughput.ReadCapacityUnits),
|
||||
"WriteCapacityUnits": int(description.ProvisionedThroughput.WriteCapacityUnits),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (q *Query) AddKeyConditions(comparisons []AttributeComparison) {
|
||||
q.buffer["KeyConditions"] = buildComparisons(comparisons)
|
||||
}
|
||||
|
||||
func (q *Query) AddLimit(limit int64) {
|
||||
q.buffer["Limit"] = limit
|
||||
}
|
||||
func (q *Query) AddSelect(value string) {
|
||||
q.buffer["Select"] = value
|
||||
}
|
||||
|
||||
func (q *Query) AddIndex(value string) {
|
||||
q.buffer["IndexName"] = value
|
||||
}
|
||||
|
||||
func (q *Query) ScanIndexDescending() {
|
||||
q.buffer["ScanIndexForward"] = "false"
|
||||
}
|
||||
|
||||
/*
|
||||
"ScanFilter":{
|
||||
"AttributeName1":{"AttributeValueList":[{"S":"AttributeValue"}],"ComparisonOperator":"EQ"}
|
||||
},
|
||||
*/
|
||||
func (q *Query) AddScanFilter(comparisons []AttributeComparison) {
|
||||
q.buffer["ScanFilter"] = buildComparisons(comparisons)
|
||||
}
|
||||
|
||||
func (q *Query) AddParallelScanConfiguration(segment int, totalSegments int) {
|
||||
q.buffer["Segment"] = segment
|
||||
q.buffer["TotalSegments"] = totalSegments
|
||||
}
|
||||
|
||||
func buildComparisons(comparisons []AttributeComparison) msi {
|
||||
out := msi{}
|
||||
|
||||
for _, c := range comparisons {
|
||||
avlist := []interface{}{}
|
||||
for _, attributeValue := range c.AttributeValueList {
|
||||
avlist = append(avlist, msi{attributeValue.Type: attributeValue.Value})
|
||||
}
|
||||
out[c.AttributeName] = msi{
|
||||
"AttributeValueList": avlist,
|
||||
"ComparisonOperator": c.ComparisonOperator,
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// The primary key must be included in attributes.
|
||||
func (q *Query) AddItem(attributes []Attribute) {
|
||||
q.buffer["Item"] = attributeList(attributes)
|
||||
}
|
||||
|
||||
func (q *Query) AddUpdates(attributes []Attribute, action string) {
|
||||
updates := msi{}
|
||||
for _, a := range attributes {
|
||||
au := msi{
|
||||
"Value": msi{
|
||||
a.Type: map[bool]interface{}{true: a.SetValues, false: a.Value}[a.SetType()],
|
||||
},
|
||||
"Action": action,
|
||||
}
|
||||
// Delete 'Value' from AttributeUpdates if Type is not Set
|
||||
if action == "DELETE" && !a.SetType() {
|
||||
delete(au, "Value")
|
||||
}
|
||||
updates[a.Name] = au
|
||||
}
|
||||
|
||||
q.buffer["AttributeUpdates"] = updates
|
||||
}
|
||||
|
||||
func (q *Query) AddExpected(attributes []Attribute) {
|
||||
expected := msi{}
|
||||
for _, a := range attributes {
|
||||
value := msi{}
|
||||
if a.Exists != "" {
|
||||
value["Exists"] = a.Exists
|
||||
}
|
||||
// If set Exists to false, we must remove Value
|
||||
if value["Exists"] != "false" {
|
||||
value["Value"] = msi{a.Type: map[bool]interface{}{true: a.SetValues, false: a.Value}[a.SetType()]}
|
||||
}
|
||||
expected[a.Name] = value
|
||||
}
|
||||
q.buffer["Expected"] = expected
|
||||
}
|
||||
|
||||
// Add the ReturnValues parameter, used in UpdateItem queries.
|
||||
func (q *Query) AddReturnValues(returnValues ReturnValues) {
|
||||
q.buffer["ReturnValues"] = string(returnValues)
|
||||
}
|
||||
|
||||
// Add the UpdateExpression parameter, used in UpdateItem queries.
|
||||
func (q *Query) AddUpdateExpression(expression string) {
|
||||
q.buffer["UpdateExpression"] = expression
|
||||
}
|
||||
|
||||
// Add the ConditionExpression parameter, used in UpdateItem queries.
|
||||
func (q *Query) AddConditionExpression(expression string) {
|
||||
q.buffer["ConditionExpression"] = expression
|
||||
}
|
||||
|
||||
func (q *Query) AddExpressionAttributes(attributes []Attribute) {
|
||||
existing, ok := q.buffer["ExpressionAttributes"].(msi)
|
||||
if !ok {
|
||||
existing = msi{}
|
||||
q.buffer["ExpressionAttributes"] = existing
|
||||
}
|
||||
for key, val := range attributeList(attributes) {
|
||||
existing[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) AddExclusiveStartStreamArn(arn string) {
|
||||
q.buffer["ExclusiveStartStreamArn"] = arn
|
||||
}
|
||||
|
||||
func (q *Query) AddStreamArn(arn string) {
|
||||
q.buffer["StreamArn"] = arn
|
||||
}
|
||||
|
||||
func (q *Query) AddExclusiveStartShardId(shardId string) {
|
||||
q.buffer["ExclusiveStartShardId"] = shardId
|
||||
}
|
||||
|
||||
func (q *Query) AddShardId(shardId string) {
|
||||
q.buffer["ShardId"] = shardId
|
||||
}
|
||||
|
||||
func (q *Query) AddShardIteratorType(shardIteratorType string) {
|
||||
q.buffer["ShardIteratorType"] = shardIteratorType
|
||||
}
|
||||
|
||||
func (q *Query) AddSequenceNumber(sequenceNumber string) {
|
||||
q.buffer["SequenceNumber"] = sequenceNumber
|
||||
}
|
||||
|
||||
func (q *Query) AddShardIterator(shardIterator string) {
|
||||
q.buffer["ShardIterator"] = shardIterator
|
||||
}
|
||||
|
||||
func attributeList(attributes []Attribute) msi {
|
||||
b := msi{}
|
||||
for _, a := range attributes {
|
||||
//UGH!! (I miss the query operator)
|
||||
b[a.Name] = msi{a.Type: map[bool]interface{}{true: a.SetValues, false: a.Value}[a.SetType()]}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (q *Query) addTable(t *Table) {
|
||||
q.addTableByName(t.Name)
|
||||
}
|
||||
|
||||
func (q *Query) addTableByName(tableName string) {
|
||||
q.buffer["TableName"] = tableName
|
||||
}
|
||||
|
||||
func (q *Query) String() string {
|
||||
bytes, _ := json.Marshal(q.buffer)
|
||||
return string(bytes)
|
||||
}
|
||||
380
vendor/github.com/goamz/goamz/dynamodb/query_builder_test.go
сгенерированный
поставляемый
380
vendor/github.com/goamz/goamz/dynamodb/query_builder_test.go
сгенерированный
поставляемый
@@ -1,380 +0,0 @@
|
||||
package dynamodb_test
|
||||
|
||||
import (
|
||||
simplejson "github.com/bitly/go-simplejson"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/dynamodb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type QueryBuilderSuite struct {
|
||||
server *dynamodb.Server
|
||||
}
|
||||
|
||||
var _ = Suite(&QueryBuilderSuite{})
|
||||
|
||||
func (s *QueryBuilderSuite) SetUpSuite(c *C) {
|
||||
auth := &aws.Auth{AccessKey: "", SecretKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"}
|
||||
s.server = &dynamodb.Server{*auth, aws.USEast}
|
||||
}
|
||||
|
||||
func (s *QueryBuilderSuite) TestEmptyQuery(c *C) {
|
||||
q := dynamodb.NewEmptyQuery()
|
||||
queryString := q.String()
|
||||
expectedString := "{}"
|
||||
c.Check(queryString, Equals, expectedString)
|
||||
|
||||
if expectedString != queryString {
|
||||
c.Fatalf("Unexpected Query String : %s\n", queryString)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *QueryBuilderSuite) TestAddWriteRequestItems(c *C) {
|
||||
primary := dynamodb.NewStringAttribute("WidgetFoo", "")
|
||||
secondary := dynamodb.NewNumericAttribute("Created", "")
|
||||
key := dynamodb.PrimaryKey{primary, secondary}
|
||||
table := s.server.NewTable("FooData", key)
|
||||
|
||||
primary2 := dynamodb.NewStringAttribute("TestHashKey", "")
|
||||
secondary2 := dynamodb.NewNumericAttribute("TestRangeKey", "")
|
||||
key2 := dynamodb.PrimaryKey{primary2, secondary2}
|
||||
table2 := s.server.NewTable("TestTable", key2)
|
||||
|
||||
q := dynamodb.NewEmptyQuery()
|
||||
|
||||
attribute1 := dynamodb.NewNumericAttribute("testing", "4")
|
||||
attribute2 := dynamodb.NewNumericAttribute("testingbatch", "2111")
|
||||
attribute3 := dynamodb.NewStringAttribute("testingstrbatch", "mystr")
|
||||
item1 := []dynamodb.Attribute{*attribute1, *attribute2, *attribute3}
|
||||
|
||||
attribute4 := dynamodb.NewNumericAttribute("testing", "444")
|
||||
attribute5 := dynamodb.NewNumericAttribute("testingbatch", "93748249272")
|
||||
attribute6 := dynamodb.NewStringAttribute("testingstrbatch", "myotherstr")
|
||||
item2 := []dynamodb.Attribute{*attribute4, *attribute5, *attribute6}
|
||||
|
||||
attributeDel1 := dynamodb.NewStringAttribute("TestHashKeyDel", "DelKey")
|
||||
attributeDel2 := dynamodb.NewNumericAttribute("TestRangeKeyDel", "7777777")
|
||||
itemDel := []dynamodb.Attribute{*attributeDel1, *attributeDel2}
|
||||
|
||||
attributeTest1 := dynamodb.NewStringAttribute("TestHashKey", "MyKey")
|
||||
attributeTest2 := dynamodb.NewNumericAttribute("TestRangeKey", "0193820384293")
|
||||
itemTest := []dynamodb.Attribute{*attributeTest1, *attributeTest2}
|
||||
|
||||
tableItems := map[*dynamodb.Table]map[string][][]dynamodb.Attribute{}
|
||||
actionItems := make(map[string][][]dynamodb.Attribute)
|
||||
actionItems["Put"] = [][]dynamodb.Attribute{item1, item2}
|
||||
actionItems["Delete"] = [][]dynamodb.Attribute{itemDel}
|
||||
tableItems[table] = actionItems
|
||||
|
||||
actionItems2 := make(map[string][][]dynamodb.Attribute)
|
||||
actionItems2["Put"] = [][]dynamodb.Attribute{itemTest}
|
||||
tableItems[table2] = actionItems2
|
||||
|
||||
q.AddWriteRequestItems(tableItems)
|
||||
|
||||
queryJson, err := simplejson.NewJson([]byte(q.String()))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
expectedJson, err := simplejson.NewJson([]byte(`
|
||||
{
|
||||
"RequestItems": {
|
||||
"TestTable": [
|
||||
{
|
||||
"PutRequest": {
|
||||
"Item": {
|
||||
"TestRangeKey": {
|
||||
"N": "0193820384293"
|
||||
},
|
||||
"TestHashKey": {
|
||||
"S": "MyKey"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"FooData": [
|
||||
{
|
||||
"DeleteRequest": {
|
||||
"Key": {
|
||||
"TestRangeKeyDel": {
|
||||
"N": "7777777"
|
||||
},
|
||||
"TestHashKeyDel": {
|
||||
"S": "DelKey"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"PutRequest": {
|
||||
"Item": {
|
||||
"testingstrbatch": {
|
||||
"S": "mystr"
|
||||
},
|
||||
"testingbatch": {
|
||||
"N": "2111"
|
||||
},
|
||||
"testing": {
|
||||
"N": "4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"PutRequest": {
|
||||
"Item": {
|
||||
"testingstrbatch": {
|
||||
"S": "myotherstr"
|
||||
},
|
||||
"testingbatch": {
|
||||
"N": "93748249272"
|
||||
},
|
||||
"testing": {
|
||||
"N": "444"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(queryJson, DeepEquals, expectedJson)
|
||||
}
|
||||
|
||||
func (s *QueryBuilderSuite) TestAddExpectedQuery(c *C) {
|
||||
primary := dynamodb.NewStringAttribute("domain", "")
|
||||
key := dynamodb.PrimaryKey{primary, nil}
|
||||
table := s.server.NewTable("sites", key)
|
||||
|
||||
q := dynamodb.NewQuery(table)
|
||||
q.AddKey(table, &dynamodb.Key{HashKey: "test"})
|
||||
|
||||
expected := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("domain", "expectedTest").SetExists(true),
|
||||
*dynamodb.NewStringAttribute("testKey", "").SetExists(false),
|
||||
}
|
||||
q.AddExpected(expected)
|
||||
|
||||
queryJson, err := simplejson.NewJson([]byte(q.String()))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
expectedJson, err := simplejson.NewJson([]byte(`
|
||||
{
|
||||
"Expected": {
|
||||
"domain": {
|
||||
"Exists": "true",
|
||||
"Value": {
|
||||
"S": "expectedTest"
|
||||
}
|
||||
},
|
||||
"testKey": {
|
||||
"Exists": "false"
|
||||
}
|
||||
},
|
||||
"Key": {
|
||||
"domain": {
|
||||
"S": "test"
|
||||
}
|
||||
},
|
||||
"TableName": "sites"
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(queryJson, DeepEquals, expectedJson)
|
||||
}
|
||||
|
||||
func (s *QueryBuilderSuite) TestGetItemQuery(c *C) {
|
||||
primary := dynamodb.NewStringAttribute("domain", "")
|
||||
key := dynamodb.PrimaryKey{primary, nil}
|
||||
table := s.server.NewTable("sites", key)
|
||||
|
||||
q := dynamodb.NewQuery(table)
|
||||
q.AddKey(table, &dynamodb.Key{HashKey: "test"})
|
||||
|
||||
{
|
||||
queryJson, err := simplejson.NewJson([]byte(q.String()))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
expectedJson, err := simplejson.NewJson([]byte(`
|
||||
{
|
||||
"Key": {
|
||||
"domain": {
|
||||
"S": "test"
|
||||
}
|
||||
},
|
||||
"TableName": "sites"
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(queryJson, DeepEquals, expectedJson)
|
||||
}
|
||||
|
||||
// Use ConsistentRead
|
||||
{
|
||||
q.ConsistentRead(true)
|
||||
queryJson, err := simplejson.NewJson([]byte(q.String()))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
expectedJson, err := simplejson.NewJson([]byte(`
|
||||
{
|
||||
"ConsistentRead": "true",
|
||||
"Key": {
|
||||
"domain": {
|
||||
"S": "test"
|
||||
}
|
||||
},
|
||||
"TableName": "sites"
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(queryJson, DeepEquals, expectedJson)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *QueryBuilderSuite) TestUpdateQuery(c *C) {
|
||||
primary := dynamodb.NewStringAttribute("domain", "")
|
||||
rangek := dynamodb.NewNumericAttribute("time", "")
|
||||
key := dynamodb.PrimaryKey{primary, rangek}
|
||||
table := s.server.NewTable("sites", key)
|
||||
|
||||
countAttribute := dynamodb.NewNumericAttribute("count", "4")
|
||||
attributes := []dynamodb.Attribute{*countAttribute}
|
||||
|
||||
q := dynamodb.NewQuery(table)
|
||||
q.AddKey(table, &dynamodb.Key{HashKey: "test", RangeKey: "1234"})
|
||||
q.AddUpdates(attributes, "ADD")
|
||||
|
||||
queryJson, err := simplejson.NewJson([]byte(q.String()))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
expectedJson, err := simplejson.NewJson([]byte(`
|
||||
{
|
||||
"AttributeUpdates": {
|
||||
"count": {
|
||||
"Action": "ADD",
|
||||
"Value": {
|
||||
"N": "4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Key": {
|
||||
"domain": {
|
||||
"S": "test"
|
||||
},
|
||||
"time": {
|
||||
"N": "1234"
|
||||
}
|
||||
},
|
||||
"TableName": "sites"
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(queryJson, DeepEquals, expectedJson)
|
||||
}
|
||||
|
||||
func (s *QueryBuilderSuite) TestAddUpdates(c *C) {
|
||||
primary := dynamodb.NewStringAttribute("domain", "")
|
||||
key := dynamodb.PrimaryKey{primary, nil}
|
||||
table := s.server.NewTable("sites", key)
|
||||
|
||||
q := dynamodb.NewQuery(table)
|
||||
q.AddKey(table, &dynamodb.Key{HashKey: "test"})
|
||||
|
||||
attr := dynamodb.NewStringSetAttribute("StringSet", []string{"str", "str2"})
|
||||
|
||||
q.AddUpdates([]dynamodb.Attribute{*attr}, "ADD")
|
||||
|
||||
queryJson, err := simplejson.NewJson([]byte(q.String()))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
expectedJson, err := simplejson.NewJson([]byte(`
|
||||
{
|
||||
"AttributeUpdates": {
|
||||
"StringSet": {
|
||||
"Action": "ADD",
|
||||
"Value": {
|
||||
"SS": ["str", "str2"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Key": {
|
||||
"domain": {
|
||||
"S": "test"
|
||||
}
|
||||
},
|
||||
"TableName": "sites"
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(queryJson, DeepEquals, expectedJson)
|
||||
}
|
||||
|
||||
func (s *QueryBuilderSuite) TestAddKeyConditions(c *C) {
|
||||
primary := dynamodb.NewStringAttribute("domain", "")
|
||||
key := dynamodb.PrimaryKey{primary, nil}
|
||||
table := s.server.NewTable("sites", key)
|
||||
|
||||
q := dynamodb.NewQuery(table)
|
||||
acs := []dynamodb.AttributeComparison{
|
||||
*dynamodb.NewStringAttributeComparison("domain", "EQ", "example.com"),
|
||||
*dynamodb.NewStringAttributeComparison("path", "EQ", "/"),
|
||||
}
|
||||
q.AddKeyConditions(acs)
|
||||
queryJson, err := simplejson.NewJson([]byte(q.String()))
|
||||
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
expectedJson, err := simplejson.NewJson([]byte(`
|
||||
{
|
||||
"KeyConditions": {
|
||||
"domain": {
|
||||
"AttributeValueList": [
|
||||
{
|
||||
"S": "example.com"
|
||||
}
|
||||
],
|
||||
"ComparisonOperator": "EQ"
|
||||
},
|
||||
"path": {
|
||||
"AttributeValueList": [
|
||||
{
|
||||
"S": "/"
|
||||
}
|
||||
],
|
||||
"ComparisonOperator": "EQ"
|
||||
}
|
||||
},
|
||||
"TableName": "sites"
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(queryJson, DeepEquals, expectedJson)
|
||||
}
|
||||
51
vendor/github.com/goamz/goamz/dynamodb/scan.go
сгенерированный
поставляемый
51
vendor/github.com/goamz/goamz/dynamodb/scan.go
сгенерированный
поставляемый
@@ -1,51 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
simplejson "github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
func (t *Table) FetchResults(query *Query) ([]map[string]*Attribute, error) {
|
||||
jsonResponse, err := t.Server.queryServer(target("Scan"), query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemCount, err := json.Get("Count").Int()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
results := make([]map[string]*Attribute, itemCount)
|
||||
|
||||
for i, _ := range results {
|
||||
item, err := json.Get("Items").GetIndex(i).Map()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
results[i] = parseAttributes(item)
|
||||
}
|
||||
return results, nil
|
||||
|
||||
}
|
||||
|
||||
func (t *Table) Scan(attributeComparisons []AttributeComparison) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddScanFilter(attributeComparisons)
|
||||
return t.FetchResults(q)
|
||||
}
|
||||
|
||||
func (t *Table) ParallelScan(attributeComparisons []AttributeComparison, segment int, totalSegments int) ([]map[string]*Attribute, error) {
|
||||
q := NewQuery(t)
|
||||
q.AddScanFilter(attributeComparisons)
|
||||
q.AddParallelScanConfiguration(segment, totalSegments)
|
||||
return t.FetchResults(q)
|
||||
}
|
||||
307
vendor/github.com/goamz/goamz/dynamodb/stream.go
сгенерированный
поставляемый
307
vendor/github.com/goamz/goamz/dynamodb/stream.go
сгенерированный
поставляемый
@@ -1,307 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
simplejson "github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
type Stream struct {
|
||||
Server *Server
|
||||
Arn string
|
||||
}
|
||||
|
||||
type StreamListItemT struct {
|
||||
StreamArn string
|
||||
StreamLabel string
|
||||
TableName string
|
||||
}
|
||||
|
||||
type SequenceNumberRangeT struct {
|
||||
EndingSequenceNumber string
|
||||
StartingSequenceNumber string
|
||||
}
|
||||
|
||||
type ShardT struct {
|
||||
ParentShardId string
|
||||
SequenceNumberRange SequenceNumberRangeT
|
||||
ShardId string
|
||||
}
|
||||
|
||||
type StreamDescriptionT struct {
|
||||
CreationDateTime float64
|
||||
KeySchema []KeySchemaT
|
||||
LastEvaluatedShardId string
|
||||
Shards []ShardT
|
||||
StreamArn string
|
||||
StreamLabel string
|
||||
StreamStatus string
|
||||
StreamViewType string
|
||||
TableName string
|
||||
}
|
||||
|
||||
type RecordT struct {
|
||||
AwsRegion string
|
||||
EventID string
|
||||
EventName string
|
||||
EventSource string
|
||||
EventVersion string
|
||||
StreamRecord *StreamRecordT
|
||||
}
|
||||
|
||||
type StreamRecordT struct {
|
||||
Keys map[string]*Attribute
|
||||
NewImage map[string]*Attribute
|
||||
OldImage map[string]*Attribute
|
||||
SequenceNumber string
|
||||
StreamViewType string
|
||||
SizeBytes int64
|
||||
}
|
||||
|
||||
type listStreamsResponse struct {
|
||||
Streams []StreamListItemT
|
||||
}
|
||||
|
||||
type describeStreamResponse struct {
|
||||
StreamDescription StreamDescriptionT
|
||||
}
|
||||
|
||||
var ErrNoRecords = errors.New("No records")
|
||||
|
||||
func (s *Server) ListStreams(startArn string) ([]StreamListItemT, error) {
|
||||
return s.LimitedListTableStreams("", startArn, 0)
|
||||
}
|
||||
|
||||
func (s *Server) LimitedListStreams(startArn string, limit int64) ([]StreamListItemT, error) {
|
||||
return s.LimitedListTableStreams("", startArn, limit)
|
||||
}
|
||||
|
||||
func (s *Server) ListTableStreams(table, startArn string) ([]StreamListItemT, error) {
|
||||
return s.LimitedListTableStreams(table, startArn, 0)
|
||||
}
|
||||
|
||||
func (s *Server) LimitedListTableStreams(table, startArn string, limit int64) ([]StreamListItemT, error) {
|
||||
query := NewEmptyQuery()
|
||||
|
||||
if len(table) != 0 {
|
||||
query.addTableByName(table)
|
||||
}
|
||||
|
||||
if len(startArn) != 0 {
|
||||
query.AddExclusiveStartStreamArn(startArn)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
query.AddLimit(limit)
|
||||
}
|
||||
|
||||
jsonResponse, err := s.queryServer(streamsTarget("ListStreams"), query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var r listStreamsResponse
|
||||
err = json.Unmarshal(jsonResponse, &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.Streams, nil
|
||||
}
|
||||
|
||||
func (s *Server) DescribeStream(arn, startShardId string) (*StreamDescriptionT, error) {
|
||||
return s.LimitedDescribeStream(arn, startShardId, 0)
|
||||
}
|
||||
|
||||
func (s *Server) LimitedDescribeStream(arn, startShardId string, limit int64) (*StreamDescriptionT, error) {
|
||||
query := NewEmptyQuery()
|
||||
query.AddStreamArn(arn)
|
||||
|
||||
if len(startShardId) != 0 {
|
||||
query.AddExclusiveStartShardId(startShardId)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
query.AddLimit(limit)
|
||||
}
|
||||
|
||||
jsonResponse, err := s.queryServer(streamsTarget("DescribeStream"), query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var r describeStreamResponse
|
||||
err = json.Unmarshal(jsonResponse, &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &r.StreamDescription, nil
|
||||
}
|
||||
|
||||
func (s *Server) NewStream(streamArn string) *Stream {
|
||||
return &Stream{s, streamArn}
|
||||
}
|
||||
|
||||
func (s *Stream) DescribeStream(startShardId string) (*StreamDescriptionT, error) {
|
||||
return s.Server.DescribeStream(s.Arn, startShardId)
|
||||
}
|
||||
|
||||
func (s *Stream) LimitedDescribeStream(startShardId string, limit int64) (*StreamDescriptionT, error) {
|
||||
return s.Server.LimitedDescribeStream(s.Arn, startShardId, limit)
|
||||
}
|
||||
|
||||
func (s *Server) GetShardIterator(streamArn, shardId, shardIteratorType, sequenceNumber string) (string, error) {
|
||||
query := NewEmptyQuery()
|
||||
query.AddStreamArn(streamArn)
|
||||
query.AddShardId(shardId)
|
||||
query.AddShardIteratorType(shardIteratorType)
|
||||
|
||||
if len(sequenceNumber) != 0 {
|
||||
query.AddSequenceNumber(sequenceNumber)
|
||||
}
|
||||
|
||||
jsonResponse, err := s.queryServer(streamsTarget("GetShardIterator"), query)
|
||||
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
|
||||
return json.Get("ShardIterator").MustString(), nil
|
||||
}
|
||||
|
||||
func (s *Stream) GetShardIterator(shardId, shardIteratorType, sequenceNumber string) (string, error) {
|
||||
return s.Server.GetShardIterator(s.Arn, shardId, shardIteratorType, sequenceNumber)
|
||||
}
|
||||
|
||||
func (s *Server) GetRecords(shardIterator string) (string, []*RecordT, error) {
|
||||
return s.LimitedGetRecords(shardIterator, 0)
|
||||
}
|
||||
|
||||
func (s *Server) LimitedGetRecords(shardIterator string, limit int64) (string, []*RecordT, error) {
|
||||
query := NewEmptyQuery()
|
||||
query.AddShardIterator(shardIterator)
|
||||
|
||||
if limit > 0 {
|
||||
query.AddLimit(limit)
|
||||
}
|
||||
|
||||
jsonResponse, err := s.queryServer(streamsTarget("GetRecords"), query)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
jsonParsed, err := simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
nextShardIt := ""
|
||||
nextShardItJson, ok := jsonParsed.CheckGet("NextShardIterator")
|
||||
if ok {
|
||||
nextShardIt, err = nextShardItJson.String()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return "", nil, errors.New(message)
|
||||
}
|
||||
}
|
||||
|
||||
recordsJson, ok := jsonParsed.CheckGet("Records")
|
||||
if !ok {
|
||||
return nextShardIt, nil, ErrNoRecords
|
||||
}
|
||||
|
||||
recordsArray, err := recordsJson.Array()
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nextShardIt, nil, errors.New(message)
|
||||
}
|
||||
|
||||
var records []*RecordT
|
||||
for _, record := range recordsArray {
|
||||
if recordMap, ok := record.(map[string]interface{}); ok {
|
||||
r := parseRecord(recordMap)
|
||||
records = append(records, r)
|
||||
}
|
||||
}
|
||||
|
||||
return nextShardIt, records, nil
|
||||
}
|
||||
|
||||
func (s *Stream) GetRecords(shardIterator string) (string, []*RecordT, error) {
|
||||
return s.Server.GetRecords(shardIterator)
|
||||
}
|
||||
|
||||
func (s *Stream) LimitedGetRecords(shardIterator string, limit int64) (string, []*RecordT, error) {
|
||||
return s.Server.LimitedGetRecords(shardIterator, limit)
|
||||
}
|
||||
|
||||
func parseRecord(r map[string]interface{}) *RecordT {
|
||||
record := RecordT{}
|
||||
rValue := reflect.ValueOf(&record)
|
||||
|
||||
keys := []string{"awsRegion", "eventID", "eventName", "eventSource", "eventVersion"}
|
||||
for i, key := range keys {
|
||||
if value, ok := r[key]; ok {
|
||||
if valueStr, ok := value.(string); ok {
|
||||
rValue.Elem().Field(i).SetString(valueStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if streamRecord, ok := r["dynamodb"]; ok {
|
||||
if streamRecordMap, ok := streamRecord.(map[string]interface{}); ok {
|
||||
record.StreamRecord = parseStreamRecord(streamRecordMap)
|
||||
}
|
||||
}
|
||||
|
||||
return &record
|
||||
}
|
||||
|
||||
func parseStreamRecord(s map[string]interface{}) *StreamRecordT {
|
||||
sr := StreamRecordT{}
|
||||
rValue := reflect.ValueOf(&sr)
|
||||
|
||||
attrKeys := []string{"Keys", "NewImage", "OldImage"}
|
||||
numAttrKeys := len(attrKeys)
|
||||
for i, key := range attrKeys {
|
||||
if value, ok := s[key]; ok {
|
||||
if valueMap, ok := value.(map[string]interface{}); ok {
|
||||
attrs := parseAttributes(valueMap)
|
||||
rValue.Elem().Field(i).Set(reflect.ValueOf(attrs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strKeys := []string{"SequenceNumber", "StreamViewType"}
|
||||
numStrKeys := len(strKeys)
|
||||
for i, key := range strKeys {
|
||||
if value, ok := s[key]; ok {
|
||||
if valueStr, ok := value.(string); ok {
|
||||
rValue.Elem().Field(i + numAttrKeys).SetString(valueStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intKeys := []string{"SizeBytes"}
|
||||
for i, key := range intKeys {
|
||||
if value, ok := s[key]; ok {
|
||||
if valueNumber, ok := value.(json.Number); ok {
|
||||
if valueInt, err := valueNumber.Int64(); err == nil {
|
||||
rValue.Elem().Field(i + numAttrKeys + numStrKeys).SetInt(valueInt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &sr
|
||||
}
|
||||
198
vendor/github.com/goamz/goamz/dynamodb/stream_test.go
сгенерированный
поставляемый
198
vendor/github.com/goamz/goamz/dynamodb/stream_test.go
сгенерированный
поставляемый
@@ -1,198 +0,0 @@
|
||||
package dynamodb_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/goamz/goamz/dynamodb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type StreamSuite struct {
|
||||
TableDescriptionT dynamodb.TableDescriptionT
|
||||
DynamoDBTest
|
||||
}
|
||||
|
||||
func (s *StreamSuite) SetUpSuite(c *C) {
|
||||
setUpAuth(c)
|
||||
s.DynamoDBTest.TableDescriptionT = s.TableDescriptionT
|
||||
s.server = &dynamodb.Server{dynamodb_auth, dynamodb_region}
|
||||
pk, err := s.TableDescriptionT.BuildPrimaryKey()
|
||||
if err != nil {
|
||||
c.Skip(err.Error())
|
||||
}
|
||||
s.table = s.server.NewTable(s.TableDescriptionT.TableName, pk)
|
||||
|
||||
// Cleanup
|
||||
s.TearDownSuite(c)
|
||||
_, err = s.server.CreateTable(s.TableDescriptionT)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
s.WaitUntilStatus(c, "ACTIVE")
|
||||
}
|
||||
|
||||
var stream_suite_keys_only = &StreamSuite{
|
||||
TableDescriptionT: dynamodb.TableDescriptionT{
|
||||
TableName: "StreamTable",
|
||||
AttributeDefinitions: []dynamodb.AttributeDefinitionT{
|
||||
dynamodb.AttributeDefinitionT{"TestHashKey", "S"},
|
||||
dynamodb.AttributeDefinitionT{"TestRangeKey", "N"},
|
||||
},
|
||||
KeySchema: []dynamodb.KeySchemaT{
|
||||
dynamodb.KeySchemaT{"TestHashKey", "HASH"},
|
||||
dynamodb.KeySchemaT{"TestRangeKey", "RANGE"},
|
||||
},
|
||||
ProvisionedThroughput: dynamodb.ProvisionedThroughputT{
|
||||
ReadCapacityUnits: 1,
|
||||
WriteCapacityUnits: 1,
|
||||
},
|
||||
StreamSpecification: dynamodb.StreamSpecificationT{
|
||||
StreamEnabled: true,
|
||||
StreamViewType: "KEYS_ONLY",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var stream_suite_new_image = &StreamSuite{
|
||||
TableDescriptionT: dynamodb.TableDescriptionT{
|
||||
TableName: "StreamTable",
|
||||
AttributeDefinitions: []dynamodb.AttributeDefinitionT{
|
||||
dynamodb.AttributeDefinitionT{"TestHashKey", "S"},
|
||||
dynamodb.AttributeDefinitionT{"TestRangeKey", "N"},
|
||||
},
|
||||
KeySchema: []dynamodb.KeySchemaT{
|
||||
dynamodb.KeySchemaT{"TestHashKey", "HASH"},
|
||||
dynamodb.KeySchemaT{"TestRangeKey", "RANGE"},
|
||||
},
|
||||
ProvisionedThroughput: dynamodb.ProvisionedThroughputT{
|
||||
ReadCapacityUnits: 1,
|
||||
WriteCapacityUnits: 1,
|
||||
},
|
||||
StreamSpecification: dynamodb.StreamSpecificationT{
|
||||
StreamEnabled: true,
|
||||
StreamViewType: "NEW_IMAGE",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var _ = Suite(stream_suite_keys_only)
|
||||
var _ = Suite(stream_suite_new_image)
|
||||
|
||||
func (s *StreamSuite) TestStream(c *C) {
|
||||
checkStream(s.table, c)
|
||||
}
|
||||
|
||||
func checkStream(table *dynamodb.Table, c *C) {
|
||||
// list the table's streams
|
||||
streams, err := table.ListStreams("")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(len(streams), Not(Equals), 0)
|
||||
c.Check(streams[0].TableName, Equals, table.Name)
|
||||
|
||||
// stick a couple of items in the table
|
||||
attrs := []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("TestAttr", "0"),
|
||||
}
|
||||
if ok, err := table.PutItem("0", "0", attrs); !ok {
|
||||
c.Fatal(err)
|
||||
}
|
||||
attrs = []dynamodb.Attribute{
|
||||
*dynamodb.NewStringAttribute("TestAttr", "1"),
|
||||
}
|
||||
if ok, err := table.PutItem("1", "1", attrs); !ok {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// create a stream object
|
||||
stream := table.Server.NewStream(streams[0].StreamArn)
|
||||
|
||||
// describe the steam
|
||||
desc, err := stream.DescribeStream("")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
tableDesc, err := table.DescribeTable()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
c.Check(desc.KeySchema[0], Equals, tableDesc.KeySchema[0])
|
||||
c.Check(desc.StreamArn, Equals, streams[0].StreamArn)
|
||||
c.Check(desc.StreamStatus, Equals, "ENABLED")
|
||||
c.Check(desc.StreamViewType, Equals, tableDesc.StreamSpecification.StreamViewType)
|
||||
c.Check(desc.TableName, Equals, table.Name)
|
||||
c.Check(len(desc.Shards), Equals, 1)
|
||||
|
||||
// get a shard iterator
|
||||
shardIt, err := stream.GetShardIterator(desc.Shards[0].ShardId, "TRIM_HORIZON", "")
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(len(shardIt), Not(Equals), 0)
|
||||
|
||||
// poll for records
|
||||
nextIt, records, err := stream.GetRecords(shardIt)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(len(nextIt), Not(Equals), 0)
|
||||
c.Check(len(records), Equals, 2)
|
||||
|
||||
for index, record := range records {
|
||||
c.Check(record.EventSource, Equals, "aws:dynamodb")
|
||||
c.Check(record.EventName, Equals, "INSERT")
|
||||
c.Check(len(record.EventID), Not(Equals), 0)
|
||||
|
||||
// look at the actual record
|
||||
streamRec := record.StreamRecord
|
||||
c.Check(streamRec.StreamViewType, Equals, desc.StreamViewType)
|
||||
c.Check(len(streamRec.SequenceNumber), Not(Equals), 0)
|
||||
if streamRec.SizeBytes <= 0 {
|
||||
c.Errorf("Expected greater-than-zero size, got: %d", streamRec.SizeBytes)
|
||||
}
|
||||
// check the keys
|
||||
if streamRec.StreamViewType == "KEYS_ONLY" {
|
||||
checkKeys(streamRec.Keys, index, c)
|
||||
}
|
||||
// check the image
|
||||
if streamRec.StreamViewType == "NEW_IMAGE" {
|
||||
checkNewImage(streamRec.NewImage, index, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkKeys(keys map[string]*dynamodb.Attribute, expect int, c *C) {
|
||||
c.Check(len(keys), Equals, 2)
|
||||
value, err := strconv.Atoi(keys["TestHashKey"].Value)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(value, Equals, expect)
|
||||
value, err = strconv.Atoi(keys["TestRangeKey"].Value)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(value, Equals, expect)
|
||||
}
|
||||
|
||||
func checkNewImage(image map[string]*dynamodb.Attribute, expect int, c *C) {
|
||||
c.Check(len(image), Equals, 3)
|
||||
value, err := strconv.Atoi(image["TestHashKey"].Value)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(value, Equals, expect)
|
||||
value, err = strconv.Atoi(image["TestRangeKey"].Value)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(value, Equals, expect)
|
||||
value, err = strconv.Atoi(image["TestAttr"].Value)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(value, Equals, expect)
|
||||
}
|
||||
259
vendor/github.com/goamz/goamz/dynamodb/table.go
сгенерированный
поставляемый
259
vendor/github.com/goamz/goamz/dynamodb/table.go
сгенерированный
поставляемый
@@ -1,259 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
simplejson "github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
Server *Server
|
||||
Name string
|
||||
Key PrimaryKey
|
||||
}
|
||||
|
||||
type AttributeDefinitionT struct {
|
||||
Name string `json:"AttributeName"`
|
||||
Type string `json:"AttributeType"`
|
||||
}
|
||||
|
||||
type KeySchemaT struct {
|
||||
AttributeName string
|
||||
KeyType string
|
||||
}
|
||||
|
||||
type ProjectionT struct {
|
||||
ProjectionType string
|
||||
}
|
||||
|
||||
type GlobalSecondaryIndexT struct {
|
||||
IndexName string
|
||||
IndexSizeBytes int64
|
||||
ItemCount int64
|
||||
KeySchema []KeySchemaT
|
||||
Projection ProjectionT
|
||||
ProvisionedThroughput ProvisionedThroughputT
|
||||
}
|
||||
|
||||
type LocalSecondaryIndexT struct {
|
||||
IndexName string
|
||||
IndexSizeBytes int64
|
||||
ItemCount int64
|
||||
KeySchema []KeySchemaT
|
||||
Projection ProjectionT
|
||||
}
|
||||
|
||||
type ProvisionedThroughputT struct {
|
||||
NumberOfDecreasesToday int64
|
||||
ReadCapacityUnits int64
|
||||
WriteCapacityUnits int64
|
||||
}
|
||||
|
||||
type StreamSpecificationT struct {
|
||||
StreamEnabled bool
|
||||
StreamViewType string
|
||||
}
|
||||
|
||||
type TableDescriptionT struct {
|
||||
AttributeDefinitions []AttributeDefinitionT
|
||||
CreationDateTime float64
|
||||
ItemCount int64
|
||||
KeySchema []KeySchemaT
|
||||
GlobalSecondaryIndexes []GlobalSecondaryIndexT
|
||||
LocalSecondaryIndexes []LocalSecondaryIndexT
|
||||
ProvisionedThroughput ProvisionedThroughputT
|
||||
StreamSpecification StreamSpecificationT
|
||||
TableName string
|
||||
TableSizeBytes int64
|
||||
TableStatus string
|
||||
LatestStreamArn string
|
||||
LatestStreamLabel string
|
||||
}
|
||||
|
||||
type describeTableResponse struct {
|
||||
Table TableDescriptionT
|
||||
}
|
||||
|
||||
func findAttributeDefinitionByName(ads []AttributeDefinitionT, name string) *AttributeDefinitionT {
|
||||
for _, a := range ads {
|
||||
if a.Name == name {
|
||||
return &a
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AttributeDefinitionT) GetEmptyAttribute() *Attribute {
|
||||
switch a.Type {
|
||||
case "S":
|
||||
return NewStringAttribute(a.Name, "")
|
||||
case "N":
|
||||
return NewNumericAttribute(a.Name, "")
|
||||
case "B":
|
||||
return NewBinaryAttribute(a.Name, "")
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TableDescriptionT) BuildPrimaryKey() (pk PrimaryKey, err error) {
|
||||
for _, k := range t.KeySchema {
|
||||
var attr *Attribute
|
||||
ad := findAttributeDefinitionByName(t.AttributeDefinitions, k.AttributeName)
|
||||
if ad == nil {
|
||||
return pk, errors.New("An inconsistency found in TableDescriptionT")
|
||||
}
|
||||
attr = ad.GetEmptyAttribute()
|
||||
if attr == nil {
|
||||
return pk, errors.New("An inconsistency found in TableDescriptionT")
|
||||
}
|
||||
|
||||
switch k.KeyType {
|
||||
case "HASH":
|
||||
pk.KeyAttribute = attr
|
||||
case "RANGE":
|
||||
pk.RangeAttribute = attr
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Server) NewTable(name string, key PrimaryKey) *Table {
|
||||
return &Table{s, name, key}
|
||||
}
|
||||
|
||||
func (s *Server) ListTables() ([]string, error) {
|
||||
var tables []string
|
||||
|
||||
query := NewEmptyQuery()
|
||||
|
||||
jsonResponse, err := s.queryServer(target("ListTables"), query)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := json.Get("TableNames").Array()
|
||||
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected response %s", jsonResponse)
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
for _, value := range response {
|
||||
if t, ok := (value).(string); ok {
|
||||
tables = append(tables, t)
|
||||
}
|
||||
}
|
||||
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateTable(tableDescription TableDescriptionT) (string, error) {
|
||||
query := NewEmptyQuery()
|
||||
query.AddCreateRequestTable(tableDescription)
|
||||
|
||||
jsonResponse, err := s.queryServer(target("CreateTable"), query)
|
||||
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
|
||||
return json.Get("TableDescription").Get("TableStatus").MustString(), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteTable(tableDescription TableDescriptionT) (string, error) {
|
||||
query := NewEmptyQuery()
|
||||
query.AddDeleteRequestTable(tableDescription)
|
||||
|
||||
jsonResponse, err := s.queryServer(target("DeleteTable"), query)
|
||||
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
|
||||
return json.Get("TableDescription").Get("TableStatus").MustString(), nil
|
||||
}
|
||||
|
||||
func (t *Table) DescribeTable() (*TableDescriptionT, error) {
|
||||
return t.Server.DescribeTable(t.Name)
|
||||
}
|
||||
|
||||
func (s *Server) DescribeTable(name string) (*TableDescriptionT, error) {
|
||||
q := NewEmptyQuery()
|
||||
q.addTableByName(name)
|
||||
|
||||
jsonResponse, err := s.queryServer(target("DescribeTable"), q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var r describeTableResponse
|
||||
err = json.Unmarshal(jsonResponse, &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &r.Table, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateTable(tableDescription TableDescriptionT) (string, error) {
|
||||
query := NewEmptyQuery()
|
||||
query.AddUpdateRequestTable(tableDescription)
|
||||
|
||||
jsonResponse, err := s.queryServer(target("UpdateTable"), query)
|
||||
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
|
||||
json, err := simplejson.NewJson(jsonResponse)
|
||||
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
|
||||
return json.Get("TableDescription").Get("TableStatus").MustString(), nil
|
||||
}
|
||||
|
||||
func (t *Table) ListStreams(startArn string) ([]StreamListItemT, error) {
|
||||
return t.Server.ListTableStreams(t.Name, startArn)
|
||||
}
|
||||
|
||||
func (t *Table) LimitedListStreams(startArn string, limit int64) ([]StreamListItemT, error) {
|
||||
return t.Server.LimitedListTableStreams(t.Name, startArn, limit)
|
||||
}
|
||||
|
||||
func keyParam(k *PrimaryKey, hashKey string, rangeKey string) string {
|
||||
value := fmt.Sprintf("{\"HashKeyElement\":{%s}", keyValue(k.KeyAttribute.Type, hashKey))
|
||||
|
||||
if k.RangeAttribute != nil {
|
||||
value = fmt.Sprintf("%s,\"RangeKeyElement\":{%s}", value,
|
||||
keyValue(k.RangeAttribute.Type, rangeKey))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("\"Key\":%s}", value)
|
||||
}
|
||||
|
||||
func keyValue(key string, value string) string {
|
||||
return fmt.Sprintf("\"%s\":\"%s\"", key, value)
|
||||
}
|
||||
79
vendor/github.com/goamz/goamz/dynamodb/table_test.go
сгенерированный
поставляемый
79
vendor/github.com/goamz/goamz/dynamodb/table_test.go
сгенерированный
поставляемый
@@ -1,79 +0,0 @@
|
||||
package dynamodb_test
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/dynamodb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type TableSuite struct {
|
||||
TableDescriptionT dynamodb.TableDescriptionT
|
||||
DynamoDBTest
|
||||
}
|
||||
|
||||
func (s *TableSuite) SetUpSuite(c *C) {
|
||||
setUpAuth(c)
|
||||
s.DynamoDBTest.TableDescriptionT = s.TableDescriptionT
|
||||
s.server = &dynamodb.Server{dynamodb_auth, dynamodb_region}
|
||||
pk, err := s.TableDescriptionT.BuildPrimaryKey()
|
||||
if err != nil {
|
||||
c.Skip(err.Error())
|
||||
}
|
||||
s.table = s.server.NewTable(s.TableDescriptionT.TableName, pk)
|
||||
|
||||
// Cleanup
|
||||
s.TearDownSuite(c)
|
||||
}
|
||||
|
||||
var table_suite = &TableSuite{
|
||||
TableDescriptionT: dynamodb.TableDescriptionT{
|
||||
TableName: "DynamoDBTestMyTable",
|
||||
AttributeDefinitions: []dynamodb.AttributeDefinitionT{
|
||||
dynamodb.AttributeDefinitionT{"TestHashKey", "S"},
|
||||
dynamodb.AttributeDefinitionT{"TestRangeKey", "N"},
|
||||
dynamodb.AttributeDefinitionT{"TestSecKey", "N"},
|
||||
},
|
||||
KeySchema: []dynamodb.KeySchemaT{
|
||||
dynamodb.KeySchemaT{"TestHashKey", "HASH"},
|
||||
dynamodb.KeySchemaT{"TestRangeKey", "RANGE"},
|
||||
},
|
||||
GlobalSecondaryIndexes: []dynamodb.GlobalSecondaryIndexT{
|
||||
dynamodb.GlobalSecondaryIndexT{
|
||||
IndexName: "gsiTest",
|
||||
KeySchema: []dynamodb.KeySchemaT{
|
||||
dynamodb.KeySchemaT{"TestHashKey", "HASH"},
|
||||
dynamodb.KeySchemaT{"TestSecKey", "RANGE"},
|
||||
},
|
||||
Projection: dynamodb.ProjectionT{"ALL"},
|
||||
ProvisionedThroughput: dynamodb.ProvisionedThroughputT{
|
||||
ReadCapacityUnits: 1,
|
||||
WriteCapacityUnits: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionedThroughput: dynamodb.ProvisionedThroughputT{
|
||||
ReadCapacityUnits: 1,
|
||||
WriteCapacityUnits: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var _ = Suite(table_suite)
|
||||
|
||||
func (s *TableSuite) TestCreateListTable(c *C) {
|
||||
status, err := s.server.CreateTable(s.TableDescriptionT)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
if status != "ACTIVE" && status != "CREATING" {
|
||||
c.Error("Expect status to be ACTIVE or CREATING")
|
||||
}
|
||||
|
||||
s.WaitUntilStatus(c, "ACTIVE")
|
||||
|
||||
tables, err := s.server.ListTables()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Check(len(tables), Not(Equals), 0)
|
||||
c.Check(findTableByName(tables, s.TableDescriptionT.TableName), Equals, true)
|
||||
}
|
||||
94
vendor/github.com/goamz/goamz/dynamodb/update_item.go
сгенерированный
поставляемый
94
vendor/github.com/goamz/goamz/dynamodb/update_item.go
сгенерированный
поставляемый
@@ -1,94 +0,0 @@
|
||||
package dynamodb
|
||||
|
||||
import simplejson "github.com/bitly/go-simplejson"
|
||||
|
||||
/*
|
||||
Construct an update item query.
|
||||
|
||||
The query can be composed via chaining and then executed via Execute()
|
||||
|
||||
Usage:
|
||||
update := table.UpdateItem(key)
|
||||
.ReturnValues(dynamodb.UPDATED_NEW)
|
||||
.UpdateExpression("SET Counter = Counter + :incr")
|
||||
.UpdateCondition("Counter < :checkVal")
|
||||
.ExpressionAttributes(NewNumberAttribute(":incr", "1"), NewNumberAttribute(":checkVal", 42))
|
||||
result, err := update.Execute()
|
||||
if err == nil {
|
||||
log.Printf("Counter is now %v", result.Attributes["Counter"].Value)
|
||||
}
|
||||
|
||||
*/
|
||||
func (t *Table) UpdateItem(key *Key) *UpdateItem {
|
||||
q := NewQuery(t)
|
||||
q.AddKey(t, key)
|
||||
return &UpdateItem{table: t, query: q}
|
||||
}
|
||||
|
||||
type UpdateItem struct {
|
||||
table *Table
|
||||
query *Query
|
||||
hasReturnValues bool
|
||||
}
|
||||
|
||||
// Specify how return values are to be provided.
|
||||
func (u *UpdateItem) ReturnValues(returnValues ReturnValues) *UpdateItem {
|
||||
u.hasReturnValues = (returnValues != NONE)
|
||||
u.query.AddReturnValues(returnValues)
|
||||
return u
|
||||
}
|
||||
|
||||
/*
|
||||
Specify an update expression and optional attribute settings at the same time.
|
||||
|
||||
update.UpdateExpression("SET Foo = Foo + :incr", dynamodb.NewNumberAttribute(":incr", "7"))
|
||||
|
||||
is equivalent to
|
||||
|
||||
update.UpdateExpression("SET Foo = Foo + :incr")
|
||||
.ExpressionAttributes(NewNumberAttribute(":incr", "7"))
|
||||
|
||||
*/
|
||||
func (u *UpdateItem) UpdateExpression(expression string, attributes ...Attribute) *UpdateItem {
|
||||
u.query.AddUpdateExpression(expression)
|
||||
u.ExpressionAttributes(attributes...)
|
||||
return u
|
||||
}
|
||||
|
||||
// Specify attribute substitutions to be used in expressions.
|
||||
func (u *UpdateItem) ExpressionAttributes(attributes ...Attribute) *UpdateItem {
|
||||
u.query.AddExpressionAttributes(attributes)
|
||||
return u
|
||||
}
|
||||
|
||||
// Specify a check condition for conditional updates.
|
||||
func (u *UpdateItem) ConditionExpression(expression string) *UpdateItem {
|
||||
u.query.AddConditionExpression(expression)
|
||||
return u
|
||||
}
|
||||
|
||||
// Execute this query.
|
||||
func (u *UpdateItem) Execute() (*UpdateResult, error) {
|
||||
jsonResponse, err := u.table.Server.queryServer(target("UpdateItem"), u.query)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.hasReturnValues {
|
||||
resp, err := simplejson.NewJson(jsonResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attrib, err := resp.Get("Attributes").Map()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UpdateResult{parseAttributes(attrib)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type UpdateResult struct {
|
||||
Attributes map[string]*Attribute
|
||||
}
|
||||
2267
vendor/github.com/goamz/goamz/ec2/ec2.go
сгенерированный
поставляемый
2267
vendor/github.com/goamz/goamz/ec2/ec2.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1280
vendor/github.com/goamz/goamz/ec2/ec2_test.go
сгенерированный
поставляемый
1280
vendor/github.com/goamz/goamz/ec2/ec2_test.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
204
vendor/github.com/goamz/goamz/ec2/ec2i_test.go
сгенерированный
поставляемый
204
vendor/github.com/goamz/goamz/ec2/ec2i_test.go
сгенерированный
поставляемый
@@ -1,204 +0,0 @@
|
||||
package ec2_test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/ec2"
|
||||
"github.com/goamz/goamz/testutil"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
// AmazonServer represents an Amazon EC2 server.
|
||||
type AmazonServer struct {
|
||||
auth aws.Auth
|
||||
}
|
||||
|
||||
func (s *AmazonServer) SetUp(c *C) {
|
||||
auth, err := aws.EnvAuth()
|
||||
if err != nil {
|
||||
c.Fatal(err.Error())
|
||||
}
|
||||
s.auth = auth
|
||||
}
|
||||
|
||||
// Suite cost per run: 0.02 USD
|
||||
var _ = Suite(&AmazonClientSuite{})
|
||||
|
||||
// AmazonClientSuite tests the client against a live EC2 server.
|
||||
type AmazonClientSuite struct {
|
||||
srv AmazonServer
|
||||
ClientTests
|
||||
}
|
||||
|
||||
func (s *AmazonClientSuite) SetUpSuite(c *C) {
|
||||
if !testutil.Amazon {
|
||||
c.Skip("AmazonClientSuite tests not enabled")
|
||||
}
|
||||
s.srv.SetUp(c)
|
||||
s.ec2 = ec2.NewWithClient(s.srv.auth, aws.USEast, testutil.DefaultClient)
|
||||
}
|
||||
|
||||
// ClientTests defines integration tests designed to test the client.
|
||||
// It is not used as a test suite in itself, but embedded within
|
||||
// another type.
|
||||
type ClientTests struct {
|
||||
ec2 *ec2.EC2
|
||||
}
|
||||
|
||||
var imageId = "ami-ccf405a5" // Ubuntu Maverick, i386, EBS store
|
||||
|
||||
// Cost: 0.00 USD
|
||||
func (s *ClientTests) TestRunInstancesError(c *C) {
|
||||
options := ec2.RunInstancesOptions{
|
||||
ImageId: "ami-a6f504cf", // Ubuntu Maverick, i386, instance store
|
||||
InstanceType: "t1.micro", // Doesn't work with micro, results in 400.
|
||||
}
|
||||
|
||||
resp, err := s.ec2.RunInstances(&options)
|
||||
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, ErrorMatches, "AMI.*root device.*not supported.*")
|
||||
|
||||
ec2err, ok := err.(*ec2.Error)
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(ec2err.StatusCode, Equals, 400)
|
||||
c.Assert(ec2err.Code, Equals, "UnsupportedOperation")
|
||||
c.Assert(ec2err.Message, Matches, "AMI.*root device.*not supported.*")
|
||||
c.Assert(ec2err.RequestId, Matches, ".+")
|
||||
}
|
||||
|
||||
// Cost: 0.02 USD
|
||||
func (s *ClientTests) TestRunAndTerminate(c *C) {
|
||||
options := ec2.RunInstancesOptions{
|
||||
ImageId: imageId,
|
||||
InstanceType: "t1.micro",
|
||||
}
|
||||
resp1, err := s.ec2.RunInstances(&options)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(resp1.ReservationId, Matches, "r-[0-9a-f]*")
|
||||
c.Check(resp1.OwnerId, Matches, "[0-9]+")
|
||||
c.Check(resp1.Instances, HasLen, 1)
|
||||
c.Check(resp1.Instances[0].InstanceType, Equals, "t1.micro")
|
||||
|
||||
instId := resp1.Instances[0].InstanceId
|
||||
|
||||
resp2, err := s.ec2.DescribeInstances([]string{instId}, nil)
|
||||
c.Assert(err, IsNil)
|
||||
if c.Check(resp2.Reservations, HasLen, 1) && c.Check(len(resp2.Reservations[0].Instances), Equals, 1) {
|
||||
inst := resp2.Reservations[0].Instances[0]
|
||||
c.Check(inst.InstanceId, Equals, instId)
|
||||
}
|
||||
|
||||
resp3, err := s.ec2.TerminateInstances([]string{instId})
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(resp3.StateChanges, HasLen, 1)
|
||||
c.Check(resp3.StateChanges[0].InstanceId, Equals, instId)
|
||||
c.Check(resp3.StateChanges[0].CurrentState.Name, Equals, "shutting-down")
|
||||
c.Check(resp3.StateChanges[0].CurrentState.Code, Equals, 32)
|
||||
}
|
||||
|
||||
// Cost: 0.00 USD
|
||||
func (s *ClientTests) TestSecurityGroups(c *C) {
|
||||
name := "goamz-test"
|
||||
descr := "goamz security group for tests"
|
||||
|
||||
// Clean it up, if a previous test left it around and avoid leaving it around.
|
||||
s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name})
|
||||
defer s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name})
|
||||
|
||||
resp1, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: name, Description: descr})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp1.RequestId, Matches, ".+")
|
||||
c.Assert(resp1.Name, Equals, name)
|
||||
c.Assert(resp1.Id, Matches, ".+")
|
||||
|
||||
resp1, err = s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: name, Description: descr})
|
||||
ec2err, _ := err.(*ec2.Error)
|
||||
c.Assert(resp1, IsNil)
|
||||
c.Assert(ec2err, NotNil)
|
||||
c.Assert(ec2err.Code, Equals, "InvalidGroup.Duplicate")
|
||||
|
||||
perms := []ec2.IPPerm{{
|
||||
Protocol: "tcp",
|
||||
FromPort: 0,
|
||||
ToPort: 1024,
|
||||
SourceIPs: []string{"127.0.0.1/24"},
|
||||
}}
|
||||
|
||||
resp2, err := s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp2.RequestId, Matches, ".+")
|
||||
|
||||
resp3, err := s.ec2.SecurityGroups(ec2.SecurityGroupNames(name), nil)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp3.RequestId, Matches, ".+")
|
||||
c.Assert(resp3.Groups, HasLen, 1)
|
||||
|
||||
g0 := resp3.Groups[0]
|
||||
c.Assert(g0.Name, Equals, name)
|
||||
c.Assert(g0.Description, Equals, descr)
|
||||
c.Assert(g0.IPPerms, HasLen, 1)
|
||||
c.Assert(g0.IPPerms[0].Protocol, Equals, "tcp")
|
||||
c.Assert(g0.IPPerms[0].FromPort, Equals, 0)
|
||||
c.Assert(g0.IPPerms[0].ToPort, Equals, 1024)
|
||||
c.Assert(g0.IPPerms[0].SourceIPs, DeepEquals, []string{"127.0.0.1/24"})
|
||||
|
||||
resp2, err = s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp2.RequestId, Matches, ".+")
|
||||
}
|
||||
|
||||
var sessionId = func() string {
|
||||
buf := make([]byte, 8)
|
||||
// if we have no randomness, we'll just make do, so ignore the error.
|
||||
rand.Read(buf)
|
||||
return fmt.Sprintf("%x", buf)
|
||||
}()
|
||||
|
||||
// sessionName reutrns a name that is probably
|
||||
// unique to this test session.
|
||||
func sessionName(prefix string) string {
|
||||
return prefix + "-" + sessionId
|
||||
}
|
||||
|
||||
var allRegions = []aws.Region{
|
||||
aws.USEast,
|
||||
aws.USWest,
|
||||
aws.EUWest,
|
||||
aws.APSoutheast,
|
||||
aws.APNortheast,
|
||||
}
|
||||
|
||||
// Communicate with all EC2 endpoints to see if they are alive.
|
||||
func (s *ClientTests) TestRegions(c *C) {
|
||||
name := sessionName("goamz-region-test")
|
||||
perms := []ec2.IPPerm{{
|
||||
Protocol: "tcp",
|
||||
FromPort: 80,
|
||||
ToPort: 80,
|
||||
SourceIPs: []string{"127.0.0.1/32"},
|
||||
}}
|
||||
errs := make(chan error, len(allRegions))
|
||||
for _, region := range allRegions {
|
||||
go func(r aws.Region) {
|
||||
e := ec2.NewWithClient(s.ec2.Auth, r, testutil.DefaultClient)
|
||||
_, err := e.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms)
|
||||
errs <- err
|
||||
}(region)
|
||||
}
|
||||
for _ = range allRegions {
|
||||
err := <-errs
|
||||
if err != nil {
|
||||
ec2_err, ok := err.(*ec2.Error)
|
||||
if ok {
|
||||
c.Check(ec2_err.Code, Matches, "InvalidGroup.NotFound")
|
||||
} else {
|
||||
c.Errorf("Non-EC2 error: %s", err)
|
||||
}
|
||||
} else {
|
||||
c.Errorf("Test should have errored but it seems to have succeeded")
|
||||
}
|
||||
}
|
||||
}
|
||||
581
vendor/github.com/goamz/goamz/ec2/ec2t_test.go
сгенерированный
поставляемый
581
vendor/github.com/goamz/goamz/ec2/ec2t_test.go
сгенерированный
поставляемый
@@ -1,581 +0,0 @@
|
||||
package ec2_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/ec2"
|
||||
"github.com/goamz/goamz/ec2/ec2test"
|
||||
"github.com/goamz/goamz/testutil"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
// LocalServer represents a local ec2test fake server.
|
||||
type LocalServer struct {
|
||||
auth aws.Auth
|
||||
region aws.Region
|
||||
srv *ec2test.Server
|
||||
}
|
||||
|
||||
func (s *LocalServer) SetUp(c *C) {
|
||||
srv, err := ec2test.NewServer()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(srv, NotNil)
|
||||
|
||||
s.srv = srv
|
||||
s.region = aws.Region{EC2Endpoint: srv.URL()}
|
||||
}
|
||||
|
||||
// LocalServerSuite defines tests that will run
|
||||
// against the local ec2test server. It includes
|
||||
// selected tests from ClientTests;
|
||||
// when the ec2test functionality is sufficient, it should
|
||||
// include all of them, and ClientTests can be simply embedded.
|
||||
type LocalServerSuite struct {
|
||||
srv LocalServer
|
||||
ServerTests
|
||||
clientTests ClientTests
|
||||
}
|
||||
|
||||
var _ = Suite(&LocalServerSuite{})
|
||||
|
||||
func (s *LocalServerSuite) SetUpSuite(c *C) {
|
||||
s.srv.SetUp(c)
|
||||
s.ServerTests.ec2 = ec2.NewWithClient(s.srv.auth, s.srv.region, testutil.DefaultClient)
|
||||
s.clientTests.ec2 = ec2.NewWithClient(s.srv.auth, s.srv.region, testutil.DefaultClient)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestRunAndTerminate(c *C) {
|
||||
s.clientTests.TestRunAndTerminate(c)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestSecurityGroups(c *C) {
|
||||
s.clientTests.TestSecurityGroups(c)
|
||||
}
|
||||
|
||||
// TestUserData is not defined on ServerTests because it
|
||||
// requires the ec2test server to function.
|
||||
func (s *LocalServerSuite) TestUserData(c *C) {
|
||||
data := make([]byte, 256)
|
||||
for i := range data {
|
||||
data[i] = byte(i)
|
||||
}
|
||||
inst, err := s.ec2.RunInstances(&ec2.RunInstancesOptions{
|
||||
ImageId: imageId,
|
||||
InstanceType: "t1.micro",
|
||||
UserData: data,
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(inst, NotNil)
|
||||
c.Assert(inst.Instances[0].DNSName, Equals, inst.Instances[0].InstanceId+".example.com")
|
||||
|
||||
id := inst.Instances[0].InstanceId
|
||||
|
||||
defer s.ec2.TerminateInstances([]string{id})
|
||||
|
||||
tinst := s.srv.srv.Instance(id)
|
||||
c.Assert(tinst, NotNil)
|
||||
c.Assert(tinst.UserData, DeepEquals, data)
|
||||
}
|
||||
|
||||
// AmazonServerSuite runs the ec2test server tests against a live EC2 server.
|
||||
// It will only be activated if the -all flag is specified.
|
||||
type AmazonServerSuite struct {
|
||||
srv AmazonServer
|
||||
ServerTests
|
||||
}
|
||||
|
||||
var _ = Suite(&AmazonServerSuite{})
|
||||
|
||||
func (s *AmazonServerSuite) SetUpSuite(c *C) {
|
||||
if !testutil.Amazon {
|
||||
c.Skip("AmazonServerSuite tests not enabled")
|
||||
}
|
||||
s.srv.SetUp(c)
|
||||
s.ServerTests.ec2 = ec2.NewWithClient(s.srv.auth, aws.USEast, testutil.DefaultClient)
|
||||
}
|
||||
|
||||
// ServerTests defines a set of tests designed to test
|
||||
// the ec2test local fake ec2 server.
|
||||
// It is not used as a test suite in itself, but embedded within
|
||||
// another type.
|
||||
type ServerTests struct {
|
||||
ec2 *ec2.EC2
|
||||
}
|
||||
|
||||
func terminateInstances(c *C, e *ec2.EC2, insts []*ec2.Instance) {
|
||||
var ids []string
|
||||
for _, inst := range insts {
|
||||
if inst != nil {
|
||||
ids = append(ids, inst.InstanceId)
|
||||
}
|
||||
}
|
||||
_, err := e.TerminateInstances(ids)
|
||||
c.Check(err, IsNil, Commentf("%d INSTANCES LEFT RUNNING!!!", len(ids)))
|
||||
}
|
||||
|
||||
func (s *ServerTests) makeTestGroup(c *C, name, descr string) ec2.SecurityGroup {
|
||||
// Clean it up if a previous test left it around.
|
||||
_, err := s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name})
|
||||
if err != nil && err.(*ec2.Error).Code != "InvalidGroup.NotFound" {
|
||||
c.Fatalf("delete security group: %v", err)
|
||||
}
|
||||
|
||||
resp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: name, Description: descr})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.Name, Equals, name)
|
||||
return resp.SecurityGroup
|
||||
}
|
||||
|
||||
func (s *ServerTests) TestIPPerms(c *C) {
|
||||
g0 := s.makeTestGroup(c, "goamz-test0", "ec2test group 0")
|
||||
defer s.ec2.DeleteSecurityGroup(g0)
|
||||
|
||||
g1 := s.makeTestGroup(c, "goamz-test1", "ec2test group 1")
|
||||
defer s.ec2.DeleteSecurityGroup(g1)
|
||||
|
||||
resp, err := s.ec2.SecurityGroups([]ec2.SecurityGroup{g0, g1}, nil)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.Groups, HasLen, 2)
|
||||
c.Assert(resp.Groups[0].IPPerms, HasLen, 0)
|
||||
c.Assert(resp.Groups[1].IPPerms, HasLen, 0)
|
||||
|
||||
ownerId := resp.Groups[0].OwnerId
|
||||
|
||||
// test some invalid parameters
|
||||
// TODO more
|
||||
_, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{
|
||||
Protocol: "tcp",
|
||||
FromPort: 0,
|
||||
ToPort: 1024,
|
||||
SourceIPs: []string{"z127.0.0.1/24"},
|
||||
}})
|
||||
c.Assert(err, NotNil)
|
||||
c.Check(err.(*ec2.Error).Code, Equals, "InvalidPermission.Malformed")
|
||||
|
||||
// Check that AuthorizeSecurityGroup adds the correct authorizations.
|
||||
_, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{
|
||||
Protocol: "tcp",
|
||||
FromPort: 2000,
|
||||
ToPort: 2001,
|
||||
SourceIPs: []string{"127.0.0.0/24"},
|
||||
SourceGroups: []ec2.UserSecurityGroup{{
|
||||
Name: g1.Name,
|
||||
}, {
|
||||
Id: g0.Id,
|
||||
}},
|
||||
}, {
|
||||
Protocol: "tcp",
|
||||
FromPort: 2000,
|
||||
ToPort: 2001,
|
||||
SourceIPs: []string{"200.1.1.34/32"},
|
||||
}})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
resp, err = s.ec2.SecurityGroups([]ec2.SecurityGroup{g0}, nil)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.Groups, HasLen, 1)
|
||||
c.Assert(resp.Groups[0].IPPerms, HasLen, 1)
|
||||
|
||||
perm := resp.Groups[0].IPPerms[0]
|
||||
srcg := perm.SourceGroups
|
||||
c.Assert(srcg, HasLen, 2)
|
||||
|
||||
// Normalize so we don't care about returned order.
|
||||
if srcg[0].Name == g1.Name {
|
||||
srcg[0], srcg[1] = srcg[1], srcg[0]
|
||||
}
|
||||
c.Check(srcg[0].Name, Equals, g0.Name)
|
||||
c.Check(srcg[0].Id, Equals, g0.Id)
|
||||
c.Check(srcg[0].OwnerId, Equals, ownerId)
|
||||
c.Check(srcg[1].Name, Equals, g1.Name)
|
||||
c.Check(srcg[1].Id, Equals, g1.Id)
|
||||
c.Check(srcg[1].OwnerId, Equals, ownerId)
|
||||
|
||||
sort.Strings(perm.SourceIPs)
|
||||
c.Check(perm.SourceIPs, DeepEquals, []string{"127.0.0.0/24", "200.1.1.34/32"})
|
||||
|
||||
// Check that we can't delete g1 (because g0 is using it)
|
||||
_, err = s.ec2.DeleteSecurityGroup(g1)
|
||||
c.Assert(err, NotNil)
|
||||
c.Check(err.(*ec2.Error).Code, Equals, "InvalidGroup.InUse")
|
||||
|
||||
_, err = s.ec2.RevokeSecurityGroup(g0, []ec2.IPPerm{{
|
||||
Protocol: "tcp",
|
||||
FromPort: 2000,
|
||||
ToPort: 2001,
|
||||
SourceGroups: []ec2.UserSecurityGroup{{Id: g1.Id}},
|
||||
}, {
|
||||
Protocol: "tcp",
|
||||
FromPort: 2000,
|
||||
ToPort: 2001,
|
||||
SourceIPs: []string{"200.1.1.34/32"},
|
||||
}})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
resp, err = s.ec2.SecurityGroups([]ec2.SecurityGroup{g0}, nil)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.Groups, HasLen, 1)
|
||||
c.Assert(resp.Groups[0].IPPerms, HasLen, 1)
|
||||
|
||||
perm = resp.Groups[0].IPPerms[0]
|
||||
srcg = perm.SourceGroups
|
||||
c.Assert(srcg, HasLen, 1)
|
||||
c.Check(srcg[0].Name, Equals, g0.Name)
|
||||
c.Check(srcg[0].Id, Equals, g0.Id)
|
||||
c.Check(srcg[0].OwnerId, Equals, ownerId)
|
||||
|
||||
c.Check(perm.SourceIPs, DeepEquals, []string{"127.0.0.0/24"})
|
||||
|
||||
// We should be able to delete g1 now because we've removed its only use.
|
||||
_, err = s.ec2.DeleteSecurityGroup(g1)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
_, err = s.ec2.DeleteSecurityGroup(g0)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
f := ec2.NewFilter()
|
||||
f.Add("group-id", g0.Id, g1.Id)
|
||||
resp, err = s.ec2.SecurityGroups(nil, f)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.Groups, HasLen, 0)
|
||||
}
|
||||
|
||||
func (s *ServerTests) TestDuplicateIPPerm(c *C) {
|
||||
name := "goamz-test"
|
||||
descr := "goamz security group for tests"
|
||||
|
||||
// Clean it up, if a previous test left it around and avoid leaving it around.
|
||||
s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name})
|
||||
defer s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name})
|
||||
|
||||
resp1, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: name, Description: descr})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp1.Name, Equals, name)
|
||||
|
||||
perms := []ec2.IPPerm{{
|
||||
Protocol: "tcp",
|
||||
FromPort: 200,
|
||||
ToPort: 1024,
|
||||
SourceIPs: []string{"127.0.0.1/24"},
|
||||
}, {
|
||||
Protocol: "tcp",
|
||||
FromPort: 0,
|
||||
ToPort: 100,
|
||||
SourceIPs: []string{"127.0.0.1/24"},
|
||||
}}
|
||||
|
||||
_, err = s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms[0:1])
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
_, err = s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms[0:2])
|
||||
c.Assert(err, ErrorMatches, `.*\(InvalidPermission.Duplicate\)`)
|
||||
}
|
||||
|
||||
type filterSpec struct {
|
||||
name string
|
||||
values []string
|
||||
}
|
||||
|
||||
func (s *ServerTests) TestInstanceFiltering(c *C) {
|
||||
groupResp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName("testgroup1"), Description: "testgroup one description"})
|
||||
c.Assert(err, IsNil)
|
||||
group1 := groupResp.SecurityGroup
|
||||
defer s.ec2.DeleteSecurityGroup(group1)
|
||||
|
||||
groupResp, err = s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName("testgroup2"), Description: "testgroup two description"})
|
||||
c.Assert(err, IsNil)
|
||||
group2 := groupResp.SecurityGroup
|
||||
defer s.ec2.DeleteSecurityGroup(group2)
|
||||
|
||||
insts := make([]*ec2.Instance, 3)
|
||||
inst, err := s.ec2.RunInstances(&ec2.RunInstancesOptions{
|
||||
MinCount: 2,
|
||||
ImageId: imageId,
|
||||
InstanceType: "t1.micro",
|
||||
SecurityGroups: []ec2.SecurityGroup{group1},
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
insts[0] = &inst.Instances[0]
|
||||
insts[1] = &inst.Instances[1]
|
||||
defer terminateInstances(c, s.ec2, insts)
|
||||
|
||||
imageId2 := "ami-e358958a" // Natty server, i386, EBS store
|
||||
inst, err = s.ec2.RunInstances(&ec2.RunInstancesOptions{
|
||||
ImageId: imageId2,
|
||||
InstanceType: "t1.micro",
|
||||
SecurityGroups: []ec2.SecurityGroup{group2},
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
insts[2] = &inst.Instances[0]
|
||||
|
||||
ids := func(indices ...int) (instIds []string) {
|
||||
for _, index := range indices {
|
||||
instIds = append(instIds, insts[index].InstanceId)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
about string
|
||||
instanceIds []string // instanceIds argument to Instances method.
|
||||
filters []filterSpec // filters argument to Instances method.
|
||||
resultIds []string // set of instance ids of expected results.
|
||||
allowExtra bool // resultIds may be incomplete.
|
||||
err string // expected error.
|
||||
}{
|
||||
{
|
||||
about: "check that Instances returns all instances",
|
||||
resultIds: ids(0, 1, 2),
|
||||
allowExtra: true,
|
||||
}, {
|
||||
about: "check that specifying two instance ids returns them",
|
||||
instanceIds: ids(0, 2),
|
||||
resultIds: ids(0, 2),
|
||||
}, {
|
||||
about: "check that specifying a non-existent instance id gives an error",
|
||||
instanceIds: append(ids(0), "i-deadbeef"),
|
||||
err: `.*\(InvalidInstanceID\.NotFound\)`,
|
||||
}, {
|
||||
about: "check that a filter allowed both instances returns both of them",
|
||||
filters: []filterSpec{
|
||||
{"instance-id", ids(0, 2)},
|
||||
},
|
||||
resultIds: ids(0, 2),
|
||||
}, {
|
||||
about: "check that a filter allowing only one instance returns it",
|
||||
filters: []filterSpec{
|
||||
{"instance-id", ids(1)},
|
||||
},
|
||||
resultIds: ids(1),
|
||||
}, {
|
||||
about: "check that a filter allowing no instances returns none",
|
||||
filters: []filterSpec{
|
||||
{"instance-id", []string{"i-deadbeef12345"}},
|
||||
},
|
||||
}, {
|
||||
about: "check that filtering on group id works",
|
||||
filters: []filterSpec{
|
||||
{"group-id", []string{group1.Id}},
|
||||
},
|
||||
resultIds: ids(0, 1),
|
||||
}, {
|
||||
about: "check that filtering on group name works",
|
||||
filters: []filterSpec{
|
||||
{"group-name", []string{group1.Name}},
|
||||
},
|
||||
resultIds: ids(0, 1),
|
||||
}, {
|
||||
about: "check that filtering on image id works",
|
||||
filters: []filterSpec{
|
||||
{"image-id", []string{imageId}},
|
||||
},
|
||||
resultIds: ids(0, 1),
|
||||
allowExtra: true,
|
||||
}, {
|
||||
about: "combination filters 1",
|
||||
filters: []filterSpec{
|
||||
{"image-id", []string{imageId, imageId2}},
|
||||
{"group-name", []string{group1.Name}},
|
||||
},
|
||||
resultIds: ids(0, 1),
|
||||
}, {
|
||||
about: "combination filters 2",
|
||||
filters: []filterSpec{
|
||||
{"image-id", []string{imageId2}},
|
||||
{"group-name", []string{group1.Name}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, t := range tests {
|
||||
c.Logf("%d. %s", i, t.about)
|
||||
var f *ec2.Filter
|
||||
if t.filters != nil {
|
||||
f = ec2.NewFilter()
|
||||
for _, spec := range t.filters {
|
||||
f.Add(spec.name, spec.values...)
|
||||
}
|
||||
}
|
||||
resp, err := s.ec2.DescribeInstances(t.instanceIds, f)
|
||||
if t.err != "" {
|
||||
c.Check(err, ErrorMatches, t.err)
|
||||
continue
|
||||
}
|
||||
c.Assert(err, IsNil)
|
||||
insts := make(map[string]*ec2.Instance)
|
||||
for _, r := range resp.Reservations {
|
||||
for j := range r.Instances {
|
||||
inst := &r.Instances[j]
|
||||
c.Check(insts[inst.InstanceId], IsNil, Commentf("duplicate instance id: %q", inst.InstanceId))
|
||||
insts[inst.InstanceId] = inst
|
||||
}
|
||||
}
|
||||
if !t.allowExtra {
|
||||
c.Check(insts, HasLen, len(t.resultIds), Commentf("expected %d instances got %#v", len(t.resultIds), insts))
|
||||
}
|
||||
for j, id := range t.resultIds {
|
||||
c.Check(insts[id], NotNil, Commentf("instance id %d (%q) not found; got %#v", j, id, insts))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func idsOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup {
|
||||
for i := range gs {
|
||||
gs[i].Name = ""
|
||||
}
|
||||
return gs
|
||||
}
|
||||
|
||||
func namesOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup {
|
||||
for i := range gs {
|
||||
gs[i].Id = ""
|
||||
}
|
||||
return gs
|
||||
}
|
||||
|
||||
func (s *ServerTests) TestGroupFiltering(c *C) {
|
||||
g := make([]ec2.SecurityGroup, 4)
|
||||
for i := range g {
|
||||
resp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName(fmt.Sprintf("testgroup%d", i)), Description: fmt.Sprintf("testdescription%d", i)})
|
||||
c.Assert(err, IsNil)
|
||||
g[i] = resp.SecurityGroup
|
||||
c.Logf("group %d: %v", i, g[i])
|
||||
defer s.ec2.DeleteSecurityGroup(g[i])
|
||||
}
|
||||
|
||||
perms := [][]ec2.IPPerm{
|
||||
{{
|
||||
Protocol: "tcp",
|
||||
FromPort: 100,
|
||||
ToPort: 200,
|
||||
SourceIPs: []string{"1.2.3.4/32"},
|
||||
}},
|
||||
{{
|
||||
Protocol: "tcp",
|
||||
FromPort: 200,
|
||||
ToPort: 300,
|
||||
SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}},
|
||||
}},
|
||||
{{
|
||||
Protocol: "udp",
|
||||
FromPort: 200,
|
||||
ToPort: 400,
|
||||
SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}},
|
||||
}},
|
||||
}
|
||||
for i, ps := range perms {
|
||||
_, err := s.ec2.AuthorizeSecurityGroup(g[i], ps)
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
groups := func(indices ...int) (gs []ec2.SecurityGroup) {
|
||||
for _, index := range indices {
|
||||
gs = append(gs, g[index])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type groupTest struct {
|
||||
about string
|
||||
groups []ec2.SecurityGroup // groupIds argument to SecurityGroups method.
|
||||
filters []filterSpec // filters argument to SecurityGroups method.
|
||||
results []ec2.SecurityGroup // set of expected result groups.
|
||||
allowExtra bool // specified results may be incomplete.
|
||||
err string // expected error.
|
||||
}
|
||||
filterCheck := func(name, val string, gs []ec2.SecurityGroup) groupTest {
|
||||
return groupTest{
|
||||
about: "filter check " + name,
|
||||
filters: []filterSpec{{name, []string{val}}},
|
||||
results: gs,
|
||||
allowExtra: true,
|
||||
}
|
||||
}
|
||||
tests := []groupTest{
|
||||
{
|
||||
about: "check that SecurityGroups returns all groups",
|
||||
results: groups(0, 1, 2, 3),
|
||||
allowExtra: true,
|
||||
}, {
|
||||
about: "check that specifying two group ids returns them",
|
||||
groups: idsOnly(groups(0, 2)),
|
||||
results: groups(0, 2),
|
||||
}, {
|
||||
about: "check that specifying names only works",
|
||||
groups: namesOnly(groups(0, 2)),
|
||||
results: groups(0, 2),
|
||||
}, {
|
||||
about: "check that specifying a non-existent group id gives an error",
|
||||
groups: append(groups(0), ec2.SecurityGroup{Id: "sg-eeeeeeeee"}),
|
||||
err: `.*\(InvalidGroup\.NotFound\)`,
|
||||
}, {
|
||||
about: "check that a filter allowed two groups returns both of them",
|
||||
filters: []filterSpec{
|
||||
{"group-id", []string{g[0].Id, g[2].Id}},
|
||||
},
|
||||
results: groups(0, 2),
|
||||
},
|
||||
{
|
||||
about: "check that the previous filter works when specifying a list of ids",
|
||||
groups: groups(1, 2),
|
||||
filters: []filterSpec{
|
||||
{"group-id", []string{g[0].Id, g[2].Id}},
|
||||
},
|
||||
results: groups(2),
|
||||
}, {
|
||||
about: "check that a filter allowing no groups returns none",
|
||||
filters: []filterSpec{
|
||||
{"group-id", []string{"sg-eeeeeeeee"}},
|
||||
},
|
||||
},
|
||||
filterCheck("description", "testdescription1", groups(1)),
|
||||
filterCheck("group-name", g[2].Name, groups(2)),
|
||||
filterCheck("ip-permission.cidr", "1.2.3.4/32", groups(0)),
|
||||
filterCheck("ip-permission.group-name", g[1].Name, groups(1, 2)),
|
||||
filterCheck("ip-permission.protocol", "udp", groups(2)),
|
||||
filterCheck("ip-permission.from-port", "200", groups(1, 2)),
|
||||
filterCheck("ip-permission.to-port", "200", groups(0)),
|
||||
// TODO owner-id
|
||||
}
|
||||
for i, t := range tests {
|
||||
c.Logf("%d. %s", i, t.about)
|
||||
var f *ec2.Filter
|
||||
if t.filters != nil {
|
||||
f = ec2.NewFilter()
|
||||
for _, spec := range t.filters {
|
||||
f.Add(spec.name, spec.values...)
|
||||
}
|
||||
}
|
||||
resp, err := s.ec2.SecurityGroups(t.groups, f)
|
||||
if t.err != "" {
|
||||
c.Check(err, ErrorMatches, t.err)
|
||||
continue
|
||||
}
|
||||
c.Assert(err, IsNil)
|
||||
groups := make(map[string]*ec2.SecurityGroup)
|
||||
for j := range resp.Groups {
|
||||
group := &resp.Groups[j].SecurityGroup
|
||||
c.Check(groups[group.Id], IsNil, Commentf("duplicate group id: %q", group.Id))
|
||||
|
||||
groups[group.Id] = group
|
||||
}
|
||||
// If extra groups may be returned, eliminate all groups that
|
||||
// we did not create in this session apart from the default group.
|
||||
if t.allowExtra {
|
||||
namePat := regexp.MustCompile(sessionName("testgroup[0-9]"))
|
||||
for id, g := range groups {
|
||||
if !namePat.MatchString(g.Name) {
|
||||
delete(groups, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
c.Check(groups, HasLen, len(t.results))
|
||||
for j, g := range t.results {
|
||||
rg := groups[g.Id]
|
||||
c.Assert(rg, NotNil, Commentf("group %d (%v) not found; got %#v", j, g, groups))
|
||||
c.Check(rg.Name, Equals, g.Name, Commentf("group %d (%v)", j, g))
|
||||
}
|
||||
}
|
||||
}
|
||||
84
vendor/github.com/goamz/goamz/ec2/ec2test/filter.go
сгенерированный
поставляемый
84
vendor/github.com/goamz/goamz/ec2/ec2test/filter.go
сгенерированный
поставляемый
@@ -1,84 +0,0 @@
|
||||
package ec2test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// filter holds an ec2 filter. A filter maps an attribute to a set of
|
||||
// possible values for that attribute. For an item to pass through the
|
||||
// filter, every attribute of the item mentioned in the filter must match
|
||||
// at least one of its given values.
|
||||
type filter map[string][]string
|
||||
|
||||
// newFilter creates a new filter from the Filter fields in the url form.
|
||||
//
|
||||
// The filtering is specified through a map of name=>values, where the
|
||||
// name is a well-defined key identifying the data to be matched,
|
||||
// and the list of values holds the possible values the filtered
|
||||
// item can take for the key to be included in the
|
||||
// result set. For example:
|
||||
//
|
||||
// Filter.1.Name=instance-type
|
||||
// Filter.1.Value.1=m1.small
|
||||
// Filter.1.Value.2=m1.large
|
||||
//
|
||||
func newFilter(form url.Values) filter {
|
||||
// TODO return an error if the fields are not well formed?
|
||||
names := make(map[int]string)
|
||||
values := make(map[int][]string)
|
||||
maxId := 0
|
||||
for name, fvalues := range form {
|
||||
var rest string
|
||||
var id int
|
||||
if x, _ := fmt.Sscanf(name, "Filter.%d.%s", &id, &rest); x != 2 {
|
||||
continue
|
||||
}
|
||||
if id > maxId {
|
||||
maxId = id
|
||||
}
|
||||
if rest == "Name" {
|
||||
names[id] = fvalues[0]
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(rest, "Value.") {
|
||||
continue
|
||||
}
|
||||
values[id] = append(values[id], fvalues[0])
|
||||
}
|
||||
|
||||
f := make(filter)
|
||||
for id, name := range names {
|
||||
f[name] = values[id]
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func notDigit(r rune) bool {
|
||||
return r < '0' || r > '9'
|
||||
}
|
||||
|
||||
// filterable represents an object that can be passed through a filter.
|
||||
type filterable interface {
|
||||
// matchAttr returns true if given attribute of the
|
||||
// object matches value. It returns an error if the
|
||||
// attribute is not recognised or the value is malformed.
|
||||
matchAttr(attr, value string) (bool, error)
|
||||
}
|
||||
|
||||
// ok returns true if x passes through the filter.
|
||||
func (f filter) ok(x filterable) (bool, error) {
|
||||
next:
|
||||
for a, vs := range f {
|
||||
for _, v := range vs {
|
||||
if ok, err := x.matchAttr(a, v); ok {
|
||||
continue next
|
||||
} else if err != nil {
|
||||
return false, fmt.Errorf("bad attribute or value %q=%q for type %T: %v", a, v, x, err)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
993
vendor/github.com/goamz/goamz/ec2/ec2test/server.go
сгенерированный
поставляемый
993
vendor/github.com/goamz/goamz/ec2/ec2test/server.go
сгенерированный
поставляемый
@@ -1,993 +0,0 @@
|
||||
// The ec2test package implements a fake EC2 provider with
|
||||
// the capability of inducing errors on any given operation,
|
||||
// and retrospectively determining what operations have been
|
||||
// carried out.
|
||||
package ec2test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/ec2"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
// Action represents a request that changes the ec2 state.
|
||||
type Action struct {
|
||||
RequestId string
|
||||
|
||||
// Request holds the requested action as a url.Values instance
|
||||
Request url.Values
|
||||
|
||||
// If the action succeeded, Response holds the value that
|
||||
// was marshalled to build the XML response for the request.
|
||||
Response interface{}
|
||||
|
||||
// If the action failed, Err holds an error giving details of the failure.
|
||||
Err *ec2.Error
|
||||
}
|
||||
|
||||
// TODO possible other things:
|
||||
// - some virtual time stamp interface, so a client
|
||||
// can ask for all actions after a certain virtual time.
|
||||
|
||||
// Server implements an EC2 simulator for use in testing.
|
||||
type Server struct {
|
||||
url string
|
||||
listener net.Listener
|
||||
mu sync.Mutex
|
||||
reqs []*Action
|
||||
|
||||
instances map[string]*Instance // id -> instance
|
||||
reservations map[string]*reservation // id -> reservation
|
||||
groups map[string]*securityGroup // id -> group
|
||||
maxId counter
|
||||
reqId counter
|
||||
reservationId counter
|
||||
groupId counter
|
||||
initialInstanceState ec2.InstanceState
|
||||
}
|
||||
|
||||
// reservation holds a simulated ec2 reservation.
|
||||
type reservation struct {
|
||||
id string
|
||||
instances map[string]*Instance
|
||||
groups []*securityGroup
|
||||
}
|
||||
|
||||
// instance holds a simulated ec2 instance
|
||||
type Instance struct {
|
||||
// UserData holds the data that was passed to the RunInstances request
|
||||
// when the instance was started.
|
||||
UserData []byte
|
||||
id string
|
||||
imageId string
|
||||
reservation *reservation
|
||||
instType string
|
||||
state ec2.InstanceState
|
||||
}
|
||||
|
||||
// permKey represents permission for a given security
|
||||
// group or IP address (but not both) to access a given range of
|
||||
// ports. Equality of permKeys is used in the implementation of
|
||||
// permission sets, relying on the uniqueness of securityGroup
|
||||
// instances.
|
||||
type permKey struct {
|
||||
protocol string
|
||||
fromPort int
|
||||
toPort int
|
||||
group *securityGroup
|
||||
ipAddr string
|
||||
}
|
||||
|
||||
// securityGroup holds a simulated ec2 security group.
|
||||
// Instances of securityGroup should only be created through
|
||||
// Server.createSecurityGroup to ensure that groups can be
|
||||
// compared by pointer value.
|
||||
type securityGroup struct {
|
||||
id string
|
||||
name string
|
||||
description string
|
||||
|
||||
perms map[permKey]bool
|
||||
}
|
||||
|
||||
func (g *securityGroup) ec2SecurityGroup() ec2.SecurityGroup {
|
||||
return ec2.SecurityGroup{
|
||||
Name: g.name,
|
||||
Id: g.id,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *securityGroup) matchAttr(attr, value string) (ok bool, err error) {
|
||||
switch attr {
|
||||
case "description":
|
||||
return g.description == value, nil
|
||||
case "group-id":
|
||||
return g.id == value, nil
|
||||
case "group-name":
|
||||
return g.name == value, nil
|
||||
case "ip-permission.cidr":
|
||||
return g.hasPerm(func(k permKey) bool { return k.ipAddr == value }), nil
|
||||
case "ip-permission.group-name":
|
||||
return g.hasPerm(func(k permKey) bool {
|
||||
return k.group != nil && k.group.name == value
|
||||
}), nil
|
||||
case "ip-permission.from-port":
|
||||
port, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return g.hasPerm(func(k permKey) bool { return k.fromPort == port }), nil
|
||||
case "ip-permission.to-port":
|
||||
port, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return g.hasPerm(func(k permKey) bool { return k.toPort == port }), nil
|
||||
case "ip-permission.protocol":
|
||||
return g.hasPerm(func(k permKey) bool { return k.protocol == value }), nil
|
||||
case "owner-id":
|
||||
return value == ownerId, nil
|
||||
}
|
||||
return false, fmt.Errorf("unknown attribute %q", attr)
|
||||
}
|
||||
|
||||
func (g *securityGroup) hasPerm(test func(k permKey) bool) bool {
|
||||
for k := range g.perms {
|
||||
if test(k) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ec2Perms returns the list of EC2 permissions granted
|
||||
// to g. It groups permissions by port range and protocol.
|
||||
func (g *securityGroup) ec2Perms() (perms []ec2.IPPerm) {
|
||||
// The grouping is held in result. We use permKey for convenience,
|
||||
// (ensuring that the group and ipAddr of each key is zero). For
|
||||
// each protocol/port range combination, we build up the permission
|
||||
// set in the associated value.
|
||||
result := make(map[permKey]*ec2.IPPerm)
|
||||
for k := range g.perms {
|
||||
groupKey := k
|
||||
groupKey.group = nil
|
||||
groupKey.ipAddr = ""
|
||||
|
||||
ec2p := result[groupKey]
|
||||
if ec2p == nil {
|
||||
ec2p = &ec2.IPPerm{
|
||||
Protocol: k.protocol,
|
||||
FromPort: k.fromPort,
|
||||
ToPort: k.toPort,
|
||||
}
|
||||
result[groupKey] = ec2p
|
||||
}
|
||||
if k.group != nil {
|
||||
ec2p.SourceGroups = append(ec2p.SourceGroups,
|
||||
ec2.UserSecurityGroup{
|
||||
Id: k.group.id,
|
||||
Name: k.group.name,
|
||||
OwnerId: ownerId,
|
||||
})
|
||||
} else {
|
||||
ec2p.SourceIPs = append(ec2p.SourceIPs, k.ipAddr)
|
||||
}
|
||||
}
|
||||
for _, ec2p := range result {
|
||||
perms = append(perms, *ec2p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) interface{}{
|
||||
"RunInstances": (*Server).runInstances,
|
||||
"TerminateInstances": (*Server).terminateInstances,
|
||||
"DescribeInstances": (*Server).describeInstances,
|
||||
"CreateSecurityGroup": (*Server).createSecurityGroup,
|
||||
"DescribeSecurityGroups": (*Server).describeSecurityGroups,
|
||||
"DeleteSecurityGroup": (*Server).deleteSecurityGroup,
|
||||
"AuthorizeSecurityGroupIngress": (*Server).authorizeSecurityGroupIngress,
|
||||
"RevokeSecurityGroupIngress": (*Server).revokeSecurityGroupIngress,
|
||||
}
|
||||
|
||||
const ownerId = "9876"
|
||||
|
||||
// newAction allocates a new action and adds it to the
|
||||
// recorded list of server actions.
|
||||
func (srv *Server) newAction() *Action {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
|
||||
a := new(Action)
|
||||
srv.reqs = append(srv.reqs, a)
|
||||
return a
|
||||
}
|
||||
|
||||
// NewServer returns a new server.
|
||||
func NewServer() (*Server, error) {
|
||||
srv := &Server{
|
||||
instances: make(map[string]*Instance),
|
||||
groups: make(map[string]*securityGroup),
|
||||
reservations: make(map[string]*reservation),
|
||||
initialInstanceState: Pending,
|
||||
}
|
||||
|
||||
// Add default security group.
|
||||
g := &securityGroup{
|
||||
name: "default",
|
||||
description: "default group",
|
||||
id: fmt.Sprintf("sg-%d", srv.groupId.next()),
|
||||
}
|
||||
g.perms = map[permKey]bool{
|
||||
permKey{
|
||||
protocol: "icmp",
|
||||
fromPort: -1,
|
||||
toPort: -1,
|
||||
group: g,
|
||||
}: true,
|
||||
permKey{
|
||||
protocol: "tcp",
|
||||
fromPort: 0,
|
||||
toPort: 65535,
|
||||
group: g,
|
||||
}: true,
|
||||
permKey{
|
||||
protocol: "udp",
|
||||
fromPort: 0,
|
||||
toPort: 65535,
|
||||
group: g,
|
||||
}: true,
|
||||
}
|
||||
srv.groups[g.id] = g
|
||||
|
||||
l, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot listen on localhost: %v", err)
|
||||
}
|
||||
srv.listener = l
|
||||
|
||||
srv.url = "http://" + l.Addr().String()
|
||||
|
||||
// we use HandlerFunc rather than *Server directly so that we
|
||||
// can avoid exporting HandlerFunc from *Server.
|
||||
go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
srv.serveHTTP(w, req)
|
||||
}))
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// Quit closes down the server.
|
||||
func (srv *Server) Quit() {
|
||||
srv.listener.Close()
|
||||
}
|
||||
|
||||
// SetInitialInstanceState sets the state that any new instances will be started in.
|
||||
func (srv *Server) SetInitialInstanceState(state ec2.InstanceState) {
|
||||
srv.mu.Lock()
|
||||
srv.initialInstanceState = state
|
||||
srv.mu.Unlock()
|
||||
}
|
||||
|
||||
// URL returns the URL of the server.
|
||||
func (srv *Server) URL() string {
|
||||
return srv.url
|
||||
}
|
||||
|
||||
// serveHTTP serves the EC2 protocol.
|
||||
func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
req.ParseForm()
|
||||
|
||||
a := srv.newAction()
|
||||
a.RequestId = fmt.Sprintf("req%d", srv.reqId.next())
|
||||
a.Request = req.Form
|
||||
|
||||
// Methods on Server that deal with parsing user data
|
||||
// may fail. To save on error handling code, we allow these
|
||||
// methods to call fatalf, which will panic with an *ec2.Error
|
||||
// which will be caught here and returned
|
||||
// to the client as a properly formed EC2 error.
|
||||
defer func() {
|
||||
switch err := recover().(type) {
|
||||
case *ec2.Error:
|
||||
a.Err = err
|
||||
err.RequestId = a.RequestId
|
||||
writeError(w, err)
|
||||
case nil:
|
||||
default:
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
f := actions[req.Form.Get("Action")]
|
||||
if f == nil {
|
||||
fatalf(400, "InvalidParameterValue", "Unrecognized Action")
|
||||
}
|
||||
|
||||
response := f(srv, w, req, a.RequestId)
|
||||
a.Response = response
|
||||
|
||||
w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`)
|
||||
xmlMarshal(w, response)
|
||||
}
|
||||
|
||||
// Instance returns the instance for the given instance id.
|
||||
// It returns nil if there is no such instance.
|
||||
func (srv *Server) Instance(id string) *Instance {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
return srv.instances[id]
|
||||
}
|
||||
|
||||
// writeError writes an appropriate error response.
|
||||
// TODO how should we deal with errors when the
|
||||
// error itself is potentially generated by backend-agnostic
|
||||
// code?
|
||||
func writeError(w http.ResponseWriter, err *ec2.Error) {
|
||||
// Error encapsulates an error returned by EC2.
|
||||
// TODO merge with ec2.Error when xml supports ignoring a field.
|
||||
type ec2error struct {
|
||||
Code string // EC2 error code ("UnsupportedOperation", ...)
|
||||
Message string // The human-oriented error message
|
||||
RequestId string
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
RequestId string
|
||||
Errors []ec2error `xml:"Errors>Error"`
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`)
|
||||
w.WriteHeader(err.StatusCode)
|
||||
xmlMarshal(w, Response{
|
||||
RequestId: err.RequestId,
|
||||
Errors: []ec2error{{
|
||||
Code: err.Code,
|
||||
Message: err.Message,
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
// xmlMarshal is the same as xml.Marshal except that
|
||||
// it panics on error. The marshalling should not fail,
|
||||
// but we want to know if it does.
|
||||
func xmlMarshal(w io.Writer, x interface{}) {
|
||||
if err := xml.NewEncoder(w).Encode(x); err != nil {
|
||||
panic(fmt.Errorf("error marshalling %#v: %v", x, err))
|
||||
}
|
||||
}
|
||||
|
||||
// formToGroups parses a set of SecurityGroup form values
|
||||
// as found in a RunInstances request, and returns the resulting
|
||||
// slice of security groups.
|
||||
// It calls fatalf if a group is not found.
|
||||
func (srv *Server) formToGroups(form url.Values) []*securityGroup {
|
||||
var groups []*securityGroup
|
||||
for name, values := range form {
|
||||
switch {
|
||||
case strings.HasPrefix(name, "SecurityGroupId."):
|
||||
if g := srv.groups[values[0]]; g != nil {
|
||||
groups = append(groups, g)
|
||||
} else {
|
||||
fatalf(400, "InvalidGroup.NotFound", "unknown group id %q", values[0])
|
||||
}
|
||||
case strings.HasPrefix(name, "SecurityGroup."):
|
||||
var found *securityGroup
|
||||
for _, g := range srv.groups {
|
||||
if g.name == values[0] {
|
||||
found = g
|
||||
}
|
||||
}
|
||||
if found == nil {
|
||||
fatalf(400, "InvalidGroup.NotFound", "unknown group name %q", values[0])
|
||||
}
|
||||
groups = append(groups, found)
|
||||
}
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// runInstances implements the EC2 RunInstances entry point.
|
||||
func (srv *Server) runInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
min := atoi(req.Form.Get("MinCount"))
|
||||
max := atoi(req.Form.Get("MaxCount"))
|
||||
if min < 0 || max < 1 {
|
||||
fatalf(400, "InvalidParameterValue", "bad values for MinCount or MaxCount")
|
||||
}
|
||||
if min > max {
|
||||
fatalf(400, "InvalidParameterCombination", "MinCount is greater than MaxCount")
|
||||
}
|
||||
var userData []byte
|
||||
if data := req.Form.Get("UserData"); data != "" {
|
||||
var err error
|
||||
userData, err = b64.DecodeString(data)
|
||||
if err != nil {
|
||||
fatalf(400, "InvalidParameterValue", "bad UserData value: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO attributes still to consider:
|
||||
// ImageId: accept anything, we can verify later
|
||||
// KeyName ?
|
||||
// InstanceType ?
|
||||
// KernelId ?
|
||||
// RamdiskId ?
|
||||
// AvailabilityZone ?
|
||||
// GroupName tag
|
||||
// Monitoring ignore?
|
||||
// SubnetId ?
|
||||
// DisableAPITermination bool
|
||||
// ShutdownBehavior string
|
||||
// PrivateIPAddress string
|
||||
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
|
||||
// make sure that form fields are correct before creating the reservation.
|
||||
instType := req.Form.Get("InstanceType")
|
||||
imageId := req.Form.Get("ImageId")
|
||||
|
||||
r := srv.newReservation(srv.formToGroups(req.Form))
|
||||
|
||||
var resp ec2.RunInstancesResp
|
||||
resp.RequestId = reqId
|
||||
resp.ReservationId = r.id
|
||||
resp.OwnerId = ownerId
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState)
|
||||
inst.UserData = userData
|
||||
resp.Instances = append(resp.Instances, inst.ec2instance())
|
||||
}
|
||||
return &resp
|
||||
}
|
||||
|
||||
func (srv *Server) group(group ec2.SecurityGroup) *securityGroup {
|
||||
if group.Id != "" {
|
||||
return srv.groups[group.Id]
|
||||
}
|
||||
for _, g := range srv.groups {
|
||||
if g.name == group.Name {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewInstances creates n new instances in srv with the given instance type,
|
||||
// image ID, initial state and security groups. If any group does not already
|
||||
// exist, it will be created. NewInstances returns the ids of the new instances.
|
||||
func (srv *Server) NewInstances(n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
|
||||
rgroups := make([]*securityGroup, len(groups))
|
||||
for i, group := range groups {
|
||||
g := srv.group(group)
|
||||
if g == nil {
|
||||
fatalf(400, "InvalidGroup.NotFound", "no such group %v", g)
|
||||
}
|
||||
rgroups[i] = g
|
||||
}
|
||||
r := srv.newReservation(rgroups)
|
||||
|
||||
ids := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
inst := srv.newInstance(r, instType, imageId, state)
|
||||
ids[i] = inst.id
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState) *Instance {
|
||||
inst := &Instance{
|
||||
id: fmt.Sprintf("i-%d", srv.maxId.next()),
|
||||
instType: instType,
|
||||
imageId: imageId,
|
||||
state: state,
|
||||
reservation: r,
|
||||
}
|
||||
srv.instances[inst.id] = inst
|
||||
r.instances[inst.id] = inst
|
||||
return inst
|
||||
}
|
||||
|
||||
func (srv *Server) newReservation(groups []*securityGroup) *reservation {
|
||||
r := &reservation{
|
||||
id: fmt.Sprintf("r-%d", srv.reservationId.next()),
|
||||
instances: make(map[string]*Instance),
|
||||
groups: groups,
|
||||
}
|
||||
|
||||
srv.reservations[r.id] = r
|
||||
return r
|
||||
}
|
||||
|
||||
func (srv *Server) terminateInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
var resp ec2.TerminateInstancesResp
|
||||
resp.RequestId = reqId
|
||||
var insts []*Instance
|
||||
for attr, vals := range req.Form {
|
||||
if strings.HasPrefix(attr, "InstanceId.") {
|
||||
id := vals[0]
|
||||
inst := srv.instances[id]
|
||||
if inst == nil {
|
||||
fatalf(400, "InvalidInstanceID.NotFound", "no such instance id %q", id)
|
||||
}
|
||||
insts = append(insts, inst)
|
||||
}
|
||||
}
|
||||
for _, inst := range insts {
|
||||
resp.StateChanges = append(resp.StateChanges, inst.terminate())
|
||||
}
|
||||
return &resp
|
||||
}
|
||||
|
||||
func (inst *Instance) terminate() (d ec2.InstanceStateChange) {
|
||||
d.PreviousState = inst.state
|
||||
inst.state = ShuttingDown
|
||||
d.CurrentState = inst.state
|
||||
d.InstanceId = inst.id
|
||||
return d
|
||||
}
|
||||
|
||||
func (inst *Instance) ec2instance() ec2.Instance {
|
||||
return ec2.Instance{
|
||||
InstanceId: inst.id,
|
||||
InstanceType: inst.instType,
|
||||
ImageId: inst.imageId,
|
||||
DNSName: fmt.Sprintf("%s.example.com", inst.id),
|
||||
// TODO the rest
|
||||
}
|
||||
}
|
||||
|
||||
func (inst *Instance) matchAttr(attr, value string) (ok bool, err error) {
|
||||
switch attr {
|
||||
case "architecture":
|
||||
return value == "i386", nil
|
||||
case "instance-id":
|
||||
return inst.id == value, nil
|
||||
case "group-id":
|
||||
for _, g := range inst.reservation.groups {
|
||||
if g.id == value {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
case "group-name":
|
||||
for _, g := range inst.reservation.groups {
|
||||
if g.name == value {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
case "image-id":
|
||||
return value == inst.imageId, nil
|
||||
case "instance-state-code":
|
||||
code, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return code&0xff == inst.state.Code, nil
|
||||
case "instance-state-name":
|
||||
return value == inst.state.Name, nil
|
||||
}
|
||||
return false, fmt.Errorf("unknown attribute %q", attr)
|
||||
}
|
||||
|
||||
var (
|
||||
Pending = ec2.InstanceState{0, "pending"}
|
||||
Running = ec2.InstanceState{16, "running"}
|
||||
ShuttingDown = ec2.InstanceState{32, "shutting-down"}
|
||||
Terminated = ec2.InstanceState{16, "terminated"}
|
||||
Stopped = ec2.InstanceState{16, "stopped"}
|
||||
)
|
||||
|
||||
func (srv *Server) createSecurityGroup(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
name := req.Form.Get("GroupName")
|
||||
if name == "" {
|
||||
fatalf(400, "InvalidParameterValue", "empty security group name")
|
||||
}
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
if srv.group(ec2.SecurityGroup{Name: name}) != nil {
|
||||
fatalf(400, "InvalidGroup.Duplicate", "group %q already exists", name)
|
||||
}
|
||||
g := &securityGroup{
|
||||
name: name,
|
||||
description: req.Form.Get("GroupDescription"),
|
||||
id: fmt.Sprintf("sg-%d", srv.groupId.next()),
|
||||
perms: make(map[permKey]bool),
|
||||
}
|
||||
srv.groups[g.id] = g
|
||||
// we define a local type for this because ec2.CreateSecurityGroupResp
|
||||
// contains SecurityGroup, but the response to this request
|
||||
// should not contain the security group name.
|
||||
type CreateSecurityGroupResponse struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
Return bool `xml:"return"`
|
||||
GroupId string `xml:"groupId"`
|
||||
}
|
||||
r := &CreateSecurityGroupResponse{
|
||||
RequestId: reqId,
|
||||
Return: true,
|
||||
GroupId: g.id,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (srv *Server) notImplemented(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
fatalf(500, "InternalError", "not implemented")
|
||||
panic("not reached")
|
||||
}
|
||||
|
||||
func (srv *Server) describeInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
insts := make(map[*Instance]bool)
|
||||
for name, vals := range req.Form {
|
||||
if !strings.HasPrefix(name, "InstanceId.") {
|
||||
continue
|
||||
}
|
||||
inst := srv.instances[vals[0]]
|
||||
if inst == nil {
|
||||
fatalf(400, "InvalidInstanceID.NotFound", "instance %q not found", vals[0])
|
||||
}
|
||||
insts[inst] = true
|
||||
}
|
||||
|
||||
f := newFilter(req.Form)
|
||||
|
||||
var resp ec2.DescribeInstancesResp
|
||||
resp.RequestId = reqId
|
||||
for _, r := range srv.reservations {
|
||||
var instances []ec2.Instance
|
||||
for _, inst := range r.instances {
|
||||
if len(insts) > 0 && !insts[inst] {
|
||||
continue
|
||||
}
|
||||
ok, err := f.ok(inst)
|
||||
if ok {
|
||||
instances = append(instances, inst.ec2instance())
|
||||
} else if err != nil {
|
||||
fatalf(400, "InvalidParameterValue", "describe instances: %v", err)
|
||||
}
|
||||
}
|
||||
if len(instances) > 0 {
|
||||
var groups []ec2.SecurityGroup
|
||||
for _, g := range r.groups {
|
||||
groups = append(groups, g.ec2SecurityGroup())
|
||||
}
|
||||
resp.Reservations = append(resp.Reservations, ec2.Reservation{
|
||||
ReservationId: r.id,
|
||||
OwnerId: ownerId,
|
||||
Instances: instances,
|
||||
SecurityGroups: groups,
|
||||
})
|
||||
}
|
||||
}
|
||||
return &resp
|
||||
}
|
||||
|
||||
func (srv *Server) describeSecurityGroups(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
// BUG similar bug to describeInstances, but for GroupName and GroupId
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
|
||||
var groups []*securityGroup
|
||||
for name, vals := range req.Form {
|
||||
var g ec2.SecurityGroup
|
||||
switch {
|
||||
case strings.HasPrefix(name, "GroupName."):
|
||||
g.Name = vals[0]
|
||||
case strings.HasPrefix(name, "GroupId."):
|
||||
g.Id = vals[0]
|
||||
default:
|
||||
continue
|
||||
}
|
||||
sg := srv.group(g)
|
||||
if sg == nil {
|
||||
fatalf(400, "InvalidGroup.NotFound", "no such group %v", g)
|
||||
}
|
||||
groups = append(groups, sg)
|
||||
}
|
||||
if len(groups) == 0 {
|
||||
for _, g := range srv.groups {
|
||||
groups = append(groups, g)
|
||||
}
|
||||
}
|
||||
|
||||
f := newFilter(req.Form)
|
||||
var resp ec2.SecurityGroupsResp
|
||||
resp.RequestId = reqId
|
||||
for _, group := range groups {
|
||||
ok, err := f.ok(group)
|
||||
if ok {
|
||||
resp.Groups = append(resp.Groups, ec2.SecurityGroupInfo{
|
||||
OwnerId: ownerId,
|
||||
SecurityGroup: group.ec2SecurityGroup(),
|
||||
Description: group.description,
|
||||
IPPerms: group.ec2Perms(),
|
||||
})
|
||||
} else if err != nil {
|
||||
fatalf(400, "InvalidParameterValue", "describe security groups: %v", err)
|
||||
}
|
||||
}
|
||||
return &resp
|
||||
}
|
||||
|
||||
func (srv *Server) authorizeSecurityGroupIngress(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
g := srv.group(ec2.SecurityGroup{
|
||||
Name: req.Form.Get("GroupName"),
|
||||
Id: req.Form.Get("GroupId"),
|
||||
})
|
||||
if g == nil {
|
||||
fatalf(400, "InvalidGroup.NotFound", "group not found")
|
||||
}
|
||||
perms := srv.parsePerms(req)
|
||||
|
||||
for _, p := range perms {
|
||||
if g.perms[p] {
|
||||
fatalf(400, "InvalidPermission.Duplicate", "Permission has already been authorized on the specified group")
|
||||
}
|
||||
}
|
||||
for _, p := range perms {
|
||||
g.perms[p] = true
|
||||
}
|
||||
return &ec2.SimpleResp{
|
||||
XMLName: xml.Name{"", "AuthorizeSecurityGroupIngressResponse"},
|
||||
RequestId: reqId,
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) revokeSecurityGroupIngress(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
g := srv.group(ec2.SecurityGroup{
|
||||
Name: req.Form.Get("GroupName"),
|
||||
Id: req.Form.Get("GroupId"),
|
||||
})
|
||||
if g == nil {
|
||||
fatalf(400, "InvalidGroup.NotFound", "group not found")
|
||||
}
|
||||
perms := srv.parsePerms(req)
|
||||
|
||||
// Note EC2 does not give an error if asked to revoke an authorization
|
||||
// that does not exist.
|
||||
for _, p := range perms {
|
||||
delete(g.perms, p)
|
||||
}
|
||||
return &ec2.SimpleResp{
|
||||
XMLName: xml.Name{"", "RevokeSecurityGroupIngressResponse"},
|
||||
RequestId: reqId,
|
||||
}
|
||||
}
|
||||
|
||||
var secGroupPat = regexp.MustCompile(`^sg-[a-z0-9]+$`)
|
||||
var ipPat = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$`)
|
||||
var ownerIdPat = regexp.MustCompile(`^[0-9]+$`)
|
||||
|
||||
// parsePerms returns a slice of permKey values extracted
|
||||
// from the permission fields in req.
|
||||
func (srv *Server) parsePerms(req *http.Request) []permKey {
|
||||
// perms maps an index found in the form to its associated
|
||||
// IPPerm. For instance, the form value with key
|
||||
// "IpPermissions.3.FromPort" will be stored in perms[3].FromPort
|
||||
perms := make(map[int]ec2.IPPerm)
|
||||
|
||||
type subgroupKey struct {
|
||||
id1, id2 int
|
||||
}
|
||||
// Each IPPerm can have many source security groups. The form key
|
||||
// for a source security group contains two indices: the index
|
||||
// of the IPPerm and the sub-index of the security group. The
|
||||
// sourceGroups map maps from a subgroupKey containing these
|
||||
// two indices to the associated security group. For instance,
|
||||
// the form value with key "IPPermissions.3.Groups.2.GroupName"
|
||||
// will be stored in sourceGroups[subgroupKey{3, 2}].Name.
|
||||
sourceGroups := make(map[subgroupKey]ec2.UserSecurityGroup)
|
||||
|
||||
// For each value in the form we store its associated information in the
|
||||
// above maps. The maps are necessary because the form keys may
|
||||
// arrive in any order, and the indices are not
|
||||
// necessarily sequential or even small.
|
||||
for name, vals := range req.Form {
|
||||
val := vals[0]
|
||||
var id1 int
|
||||
var rest string
|
||||
if x, _ := fmt.Sscanf(name, "IpPermissions.%d.%s", &id1, &rest); x != 2 {
|
||||
continue
|
||||
}
|
||||
ec2p := perms[id1]
|
||||
switch {
|
||||
case rest == "FromPort":
|
||||
ec2p.FromPort = atoi(val)
|
||||
case rest == "ToPort":
|
||||
ec2p.ToPort = atoi(val)
|
||||
case rest == "IpProtocol":
|
||||
switch val {
|
||||
case "tcp", "udp", "icmp":
|
||||
ec2p.Protocol = val
|
||||
default:
|
||||
// check it's a well formed number
|
||||
atoi(val)
|
||||
ec2p.Protocol = val
|
||||
}
|
||||
case strings.HasPrefix(rest, "Groups."):
|
||||
k := subgroupKey{id1: id1}
|
||||
if x, _ := fmt.Sscanf(rest[len("Groups."):], "%d.%s", &k.id2, &rest); x != 2 {
|
||||
continue
|
||||
}
|
||||
g := sourceGroups[k]
|
||||
switch rest {
|
||||
case "UserId":
|
||||
// BUG if the user id is blank, this does not conform to the
|
||||
// way that EC2 handles it - a specified but blank owner id
|
||||
// can cause RevokeSecurityGroupIngress to fail with
|
||||
// "group not found" even if the security group id has been
|
||||
// correctly specified.
|
||||
// By failing here, we ensure that we fail early in this case.
|
||||
if !ownerIdPat.MatchString(val) {
|
||||
fatalf(400, "InvalidUserID.Malformed", "Invalid user ID: %q", val)
|
||||
}
|
||||
g.OwnerId = val
|
||||
case "GroupName":
|
||||
g.Name = val
|
||||
case "GroupId":
|
||||
if !secGroupPat.MatchString(val) {
|
||||
fatalf(400, "InvalidGroupId.Malformed", "Invalid group ID: %q", val)
|
||||
}
|
||||
g.Id = val
|
||||
default:
|
||||
fatalf(400, "UnknownParameter", "unknown parameter %q", name)
|
||||
}
|
||||
sourceGroups[k] = g
|
||||
case strings.HasPrefix(rest, "IpRanges."):
|
||||
var id2 int
|
||||
if x, _ := fmt.Sscanf(rest[len("IpRanges."):], "%d.%s", &id2, &rest); x != 2 {
|
||||
continue
|
||||
}
|
||||
switch rest {
|
||||
case "CidrIp":
|
||||
if !ipPat.MatchString(val) {
|
||||
fatalf(400, "InvalidPermission.Malformed", "Invalid IP range: %q", val)
|
||||
}
|
||||
ec2p.SourceIPs = append(ec2p.SourceIPs, val)
|
||||
default:
|
||||
fatalf(400, "UnknownParameter", "unknown parameter %q", name)
|
||||
}
|
||||
default:
|
||||
fatalf(400, "UnknownParameter", "unknown parameter %q", name)
|
||||
}
|
||||
perms[id1] = ec2p
|
||||
}
|
||||
// Associate each set of source groups with its IPPerm.
|
||||
for k, g := range sourceGroups {
|
||||
p := perms[k.id1]
|
||||
p.SourceGroups = append(p.SourceGroups, g)
|
||||
perms[k.id1] = p
|
||||
}
|
||||
|
||||
// Now that we have built up the IPPerms we need, we check for
|
||||
// parameter errors and build up a permKey for each permission,
|
||||
// looking up security groups from srv as we do so.
|
||||
var result []permKey
|
||||
for _, p := range perms {
|
||||
if p.FromPort > p.ToPort {
|
||||
fatalf(400, "InvalidParameterValue", "invalid port range")
|
||||
}
|
||||
k := permKey{
|
||||
protocol: p.Protocol,
|
||||
fromPort: p.FromPort,
|
||||
toPort: p.ToPort,
|
||||
}
|
||||
for _, g := range p.SourceGroups {
|
||||
if g.OwnerId != "" && g.OwnerId != ownerId {
|
||||
fatalf(400, "InvalidGroup.NotFound", "group %q not found", g.Name)
|
||||
}
|
||||
var ec2g ec2.SecurityGroup
|
||||
switch {
|
||||
case g.Id != "":
|
||||
ec2g.Id = g.Id
|
||||
case g.Name != "":
|
||||
ec2g.Name = g.Name
|
||||
}
|
||||
k.group = srv.group(ec2g)
|
||||
if k.group == nil {
|
||||
fatalf(400, "InvalidGroup.NotFound", "group %v not found", g)
|
||||
}
|
||||
result = append(result, k)
|
||||
}
|
||||
k.group = nil
|
||||
for _, ip := range p.SourceIPs {
|
||||
k.ipAddr = ip
|
||||
result = append(result, k)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (srv *Server) deleteSecurityGroup(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
g := srv.group(ec2.SecurityGroup{
|
||||
Name: req.Form.Get("GroupName"),
|
||||
Id: req.Form.Get("GroupId"),
|
||||
})
|
||||
if g == nil {
|
||||
fatalf(400, "InvalidGroup.NotFound", "group not found")
|
||||
}
|
||||
for _, r := range srv.reservations {
|
||||
for _, h := range r.groups {
|
||||
if h == g && r.hasRunningMachine() {
|
||||
fatalf(500, "InvalidGroup.InUse", "group is currently in use by a running instance")
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, sg := range srv.groups {
|
||||
// If a group refers to itself, it's ok to delete it.
|
||||
if sg == g {
|
||||
continue
|
||||
}
|
||||
for k := range sg.perms {
|
||||
if k.group == g {
|
||||
fatalf(500, "InvalidGroup.InUse", "group is currently in use by group %q", sg.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete(srv.groups, g.id)
|
||||
return &ec2.SimpleResp{
|
||||
XMLName: xml.Name{"", "DeleteSecurityGroupResponse"},
|
||||
RequestId: reqId,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reservation) hasRunningMachine() bool {
|
||||
for _, inst := range r.instances {
|
||||
if inst.state.Code != ShuttingDown.Code && inst.state.Code != Terminated.Code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type counter int
|
||||
|
||||
func (c *counter) next() (i int) {
|
||||
i = int(*c)
|
||||
(*c)++
|
||||
return
|
||||
}
|
||||
|
||||
// atoi is like strconv.Atoi but is fatal if the
|
||||
// string is not well formed.
|
||||
func atoi(s string) int {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
fatalf(400, "InvalidParameterValue", "bad number: %v", err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func fatalf(statusCode int, code string, f string, a ...interface{}) {
|
||||
panic(&ec2.Error{
|
||||
StatusCode: statusCode,
|
||||
Code: code,
|
||||
Message: fmt.Sprintf(f, a...),
|
||||
})
|
||||
}
|
||||
22
vendor/github.com/goamz/goamz/ec2/export_test.go
сгенерированный
поставляемый
22
vendor/github.com/goamz/goamz/ec2/export_test.go
сгенерированный
поставляемый
@@ -1,22 +0,0 @@
|
||||
package ec2
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Sign(auth aws.Auth, method, path string, params map[string]string, host string) {
|
||||
sign(auth, method, path, params, host)
|
||||
}
|
||||
|
||||
func fixedTime() time.Time {
|
||||
return time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
func FakeTime(fakeIt bool) {
|
||||
if fakeIt {
|
||||
timeNow = fixedTime
|
||||
} else {
|
||||
timeNow = time.Now
|
||||
}
|
||||
}
|
||||
1084
vendor/github.com/goamz/goamz/ec2/responses_test.go
сгенерированный
поставляемый
1084
vendor/github.com/goamz/goamz/ec2/responses_test.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
45
vendor/github.com/goamz/goamz/ec2/sign.go
сгенерированный
поставляемый
45
vendor/github.com/goamz/goamz/ec2/sign.go
сгенерированный
поставляемый
@@ -1,45 +0,0 @@
|
||||
package ec2
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// EC2 signing (http://goo.gl/fQmAN)
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
func sign(auth aws.Auth, method, path string, params map[string]string, host string) {
|
||||
params["AWSAccessKeyId"] = auth.AccessKey
|
||||
params["SignatureVersion"] = "2"
|
||||
params["SignatureMethod"] = "HmacSHA256"
|
||||
if auth.Token() != "" {
|
||||
params["SecurityToken"] = auth.Token()
|
||||
}
|
||||
|
||||
// AWS specifies that the parameters in a signed request must
|
||||
// be provided in the natural order of the keys. This is distinct
|
||||
// from the natural order of the encoded value of key=value.
|
||||
// Percent and equals affect the sorting order.
|
||||
var keys, sarray []string
|
||||
for k, _ := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(params[k]))
|
||||
}
|
||||
joined := strings.Join(sarray, "&")
|
||||
payload := method + "\n" + host + "\n" + path + "\n" + joined
|
||||
hash := hmac.New(sha256.New, []byte(auth.SecretKey))
|
||||
hash.Write([]byte(payload))
|
||||
signature := make([]byte, b64.EncodedLen(hash.Size()))
|
||||
b64.Encode(signature, hash.Sum(nil))
|
||||
|
||||
params["Signature"] = string(signature)
|
||||
}
|
||||
68
vendor/github.com/goamz/goamz/ec2/sign_test.go
сгенерированный
поставляемый
68
vendor/github.com/goamz/goamz/ec2/sign_test.go
сгенерированный
поставляемый
@@ -1,68 +0,0 @@
|
||||
package ec2_test
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/ec2"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
// EC2 ReST authentication docs: http://goo.gl/fQmAN
|
||||
|
||||
var testAuth = aws.Auth{AccessKey: "user", SecretKey: "secret"}
|
||||
|
||||
func (s *S) TestBasicSignature(c *C) {
|
||||
params := map[string]string{}
|
||||
ec2.Sign(testAuth, "GET", "/path", params, "localhost")
|
||||
c.Assert(params["SignatureVersion"], Equals, "2")
|
||||
c.Assert(params["SignatureMethod"], Equals, "HmacSHA256")
|
||||
expected := "6lSe5QyXum0jMVc7cOUz32/52ZnL7N5RyKRk/09yiK4="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestParamSignature(c *C) {
|
||||
params := map[string]string{
|
||||
"param1": "value1",
|
||||
"param2": "value2",
|
||||
"param3": "value3",
|
||||
}
|
||||
ec2.Sign(testAuth, "GET", "/path", params, "localhost")
|
||||
expected := "XWOR4+0lmK8bD8CGDGZ4kfuSPbb2JibLJiCl/OPu1oU="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestManyParams(c *C) {
|
||||
params := map[string]string{
|
||||
"param1": "value10",
|
||||
"param2": "value2",
|
||||
"param3": "value3",
|
||||
"param4": "value4",
|
||||
"param5": "value5",
|
||||
"param6": "value6",
|
||||
"param7": "value7",
|
||||
"param8": "value8",
|
||||
"param9": "value9",
|
||||
"param10": "value1",
|
||||
}
|
||||
ec2.Sign(testAuth, "GET", "/path", params, "localhost")
|
||||
expected := "di0sjxIvezUgQ1SIL6i+C/H8lL+U0CQ9frLIak8jkVg="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestEscaping(c *C) {
|
||||
params := map[string]string{"Nonce": "+ +"}
|
||||
ec2.Sign(testAuth, "GET", "/path", params, "localhost")
|
||||
c.Assert(params["Nonce"], Equals, "+ +")
|
||||
expected := "bqffDELReIqwjg/W0DnsnVUmfLK4wXVLO4/LuG+1VFA="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestSignatureExample1(c *C) {
|
||||
params := map[string]string{
|
||||
"Timestamp": "2009-02-01T12:53:20+00:00",
|
||||
"Version": "2007-11-07",
|
||||
"Action": "ListDomains",
|
||||
}
|
||||
ec2.Sign(aws.Auth{AccessKey: "access", SecretKey: "secret"}, "GET", "/", params, "sdb.amazonaws.com")
|
||||
expected := "okj96/5ucWBSc1uR2zXVfm6mDHtgfNv657rRtt/aunQ="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
399
vendor/github.com/goamz/goamz/ec2/vpc.go
сгенерированный
поставляемый
399
vendor/github.com/goamz/goamz/ec2/vpc.go
сгенерированный
поставляемый
@@ -1,399 +0,0 @@
|
||||
package ec2
|
||||
|
||||
// RouteTable describes a route table which contains a set of rules, called routes
|
||||
// that are used to determine where network traffic is directed.
|
||||
//
|
||||
// See http://goo.gl/bI9hkg for more details.
|
||||
type RouteTable struct {
|
||||
Id string `xml:"routeTableId"`
|
||||
VpcId string `xml:"vpcId"`
|
||||
Routes []Route `xml:"routeSet>item"`
|
||||
Associations []RouteTableAssociation `xml:"associationSet>item"`
|
||||
PropagatingVgws []PropagatingVgw `xml:"propagatingVgwSet>item"`
|
||||
Tags []Tag `xml:"tagSet>item"`
|
||||
}
|
||||
|
||||
// Route describes a route in a route table.
|
||||
//
|
||||
// See http://goo.gl/hE5Kxe for more details.
|
||||
type Route struct {
|
||||
DestinationCidrBlock string `xml:"destinationCidrBlock"` // The CIDR block used for the destination match.
|
||||
GatewayId string `xml:"gatewayId"` // The ID of a gateway attached to your VPC.
|
||||
InstanceId string `xml:"instanceId"` // The ID of a NAT instance in your VPC.
|
||||
InstanceOwnerId string `xml:"instanceOwnerId"` // The AWS account ID of the owner of the instance.
|
||||
NetworkInterfaceId string `xml:"networkInterfaceId"` // The ID of the network interface.
|
||||
State string `xml:"state"` // The state of the route. Valid values: active | blackhole
|
||||
Origin string `xml:"origin"` // Describes how the route was created. Valid values: Valid values: CreateRouteTable | CreateRoute | EnableVgwRoutePropagation
|
||||
VpcPeeringConnectionId string `xml:"vpcPeeringConnectionId"` // The ID of the VPC peering connection.
|
||||
}
|
||||
|
||||
// RouteTableAssociation describes an association between a route table and a subnet.
|
||||
//
|
||||
// See http://goo.gl/BZB8o8 for more details.
|
||||
type RouteTableAssociation struct {
|
||||
Id string `xml:"routeTableAssociationId"` // The ID of the association between a route table and a subnet.
|
||||
RouteTableId string `xml:"routeTableId"` // The ID of the route table.
|
||||
SubnetId string `xml:"subnetId"` // The ID of the subnet.
|
||||
Main bool `xml:"main"` // Indicates whether this is the main route table.
|
||||
}
|
||||
|
||||
// PropagatingVgw describes a virtual private gateway propagating route.
|
||||
//
|
||||
// See http://goo.gl/myGQtG for more details.
|
||||
type PropagatingVgw struct {
|
||||
GatewayId string `xml:"gatewayID"`
|
||||
}
|
||||
|
||||
// CreateRouteTableResp represents a response from a CreateRouteTable request
|
||||
//
|
||||
// See http://goo.gl/LD0TqP for more details.
|
||||
type CreateRouteTableResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
RouteTable RouteTable `xml:"routeTable"`
|
||||
}
|
||||
|
||||
// CreateRouteTable creates a route table for the specified VPC.
|
||||
// After you create a route table, you can add routes and associate the table with a subnet.
|
||||
//
|
||||
// See http://goo.gl/V9h6gE for more details..
|
||||
func (ec2 *EC2) CreateRouteTable(vpcId string) (resp *CreateRouteTableResp, err error) {
|
||||
params := makeParams("CreateRouteTable")
|
||||
params["VpcId"] = vpcId
|
||||
resp = &CreateRouteTableResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DescribeRouteTablesResp represents a response from a DescribeRouteTables call
|
||||
//
|
||||
// See http://goo.gl/T3tVsg for more details.
|
||||
type DescribeRouteTablesResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
RouteTables []RouteTable `xml:"routeTableSet>item"`
|
||||
}
|
||||
|
||||
// DescribeRouteTables describes one or more of your route tables
|
||||
//
|
||||
// See http://goo.gl/S0RVos for more details.
|
||||
func (ec2 *EC2) DescribeRouteTables(routeTableIds []string, filter *Filter) (resp *DescribeRouteTablesResp, err error) {
|
||||
params := makeParams("DescribeRouteTables")
|
||||
addParamsList(params, "RouteTableId", routeTableIds)
|
||||
filter.addParams(params)
|
||||
resp = &DescribeRouteTablesResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AssociateRouteTableResp represents a response from an AssociateRouteTable call
|
||||
//
|
||||
// See http://goo.gl/T4KlYk for more details.
|
||||
type AssociateRouteTableResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
AssociationId string `xml:"associationId"`
|
||||
}
|
||||
|
||||
// AssociateRouteTable associates a subnet with a route table.
|
||||
//
|
||||
// The subnet and route table must be in the same VPC. This association causes
|
||||
// traffic originating from the subnet to be routed according to the routes
|
||||
// in the route table. The action returns an association ID, which you need in
|
||||
// order to disassociate the route table from the subnet later.
|
||||
// A route table can be associated with multiple subnets.
|
||||
//
|
||||
// See http://goo.gl/bfnONU for more details.
|
||||
func (ec2 *EC2) AssociateRouteTable(routeTableId, subnetId string) (resp *AssociateRouteTableResp, err error) {
|
||||
params := makeParams("AssociateRouteTable")
|
||||
params["RouteTableId"] = routeTableId
|
||||
params["SubnetId"] = subnetId
|
||||
resp = &AssociateRouteTableResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DisassociateRouteTableResp represents the response from a DisassociateRouteTable request
|
||||
//
|
||||
// See http://goo.gl/1v4reT for more details.
|
||||
type DisassociateRouteTableResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
Return bool `xml:"return"` // True if the request succeeds
|
||||
}
|
||||
|
||||
// DisassociateRouteTable disassociates a subnet from a route table.
|
||||
//
|
||||
// See http://goo.gl/A4NJum for more details.
|
||||
func (ec2 *EC2) DisassociateRouteTable(associationId string) (resp *DisassociateRouteTableResp, err error) {
|
||||
params := makeParams("DisassociateRouteTable")
|
||||
params["AssociationId"] = associationId
|
||||
resp = &DisassociateRouteTableResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReplaceRouteTableAssociationResp represents a response from a ReplaceRouteTableAssociation call
|
||||
//
|
||||
// See http://goo.gl/VhILGe for more details.
|
||||
type ReplaceRouteTableAssociationResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
NewAssociationId string `xml:"newAssociationId"`
|
||||
}
|
||||
|
||||
// ReplaceRouteTableAssociation changes the route table associated with a given subnet in a VPC.
|
||||
//
|
||||
// See http://goo.gl/kiit8j for more details.
|
||||
func (ec2 *EC2) ReplaceRouteTableAssociation(associationId, routeTableId string) (resp *ReplaceRouteTableAssociationResp, err error) {
|
||||
params := makeParams("ReplaceRouteTableAssociation")
|
||||
params["AssociationId"] = associationId
|
||||
params["RouteTableId"] = routeTableId
|
||||
resp = &ReplaceRouteTableAssociationResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteRouteTableResp represents a response from a DeleteRouteTable request
|
||||
//
|
||||
// See http://goo.gl/b8usig for more details.
|
||||
type DeleteRouteTableResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
Return bool `xml:"return"` // True if the request succeeds
|
||||
}
|
||||
|
||||
// DeleteRouteTable deletes the specified route table.
|
||||
// You must disassociate the route table from any subnets before you can delete it.
|
||||
// You can't delete the main route table.
|
||||
//
|
||||
// See http://goo.gl/crHxT2 for more details.
|
||||
func (ec2 *EC2) DeleteRouteTable(routeTableId string) (resp *DeleteRouteTableResp, err error) {
|
||||
params := makeParams("DeleteRouteTable")
|
||||
params["RouteTableId"] = routeTableId
|
||||
resp = &DeleteRouteTableResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// VPC describes a VPC.
|
||||
//
|
||||
// See http://goo.gl/WjX0Es for more details.
|
||||
type VPC struct {
|
||||
CidrBlock string `xml:"cidrBlock"`
|
||||
DHCPOptionsID string `xml:"dhcpOptionsId"`
|
||||
State string `xml:"state"`
|
||||
VpcId string `xml:"vpcId"`
|
||||
InstanceTenancy string `xml:"instanceTenancy"`
|
||||
IsDefault bool `xml:"isDefault"`
|
||||
Tags []Tag `xml:"tagSet>item"`
|
||||
}
|
||||
|
||||
// CreateVpcResp represents a response from a CreateVpcResp request
|
||||
//
|
||||
// See http://goo.gl/QoK11F for more details.
|
||||
type CreateVpcResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
VPC VPC `xml:"vpc"` // Information about the VPC.
|
||||
}
|
||||
|
||||
// CreateVpc creates a VPC with the specified CIDR block.
|
||||
//
|
||||
// The smallest VPC you can create uses a /28 netmask (16 IP addresses),
|
||||
// and the largest uses a /16 netmask (65,536 IP addresses).
|
||||
//
|
||||
// By default, each instance you launch in the VPC has the default DHCP options,
|
||||
// which includes only a default DNS server that Amazon provides (AmazonProvidedDNS).
|
||||
//
|
||||
// See http://goo.gl/QoK11F for more details.
|
||||
func (ec2 *EC2) CreateVpc(cidrBlock, instanceTenancy string) (resp *CreateVpcResp, err error) {
|
||||
params := makeParams("CreateVpc")
|
||||
params["CidrBlock"] = cidrBlock
|
||||
|
||||
if instanceTenancy != "" {
|
||||
params["InstanceTenancy"] = instanceTenancy
|
||||
}
|
||||
|
||||
resp = &CreateVpcResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteVpcResp represents a response from a DeleteVpc request
|
||||
//
|
||||
// See http://goo.gl/qawyrz for more details.
|
||||
type DeleteVpcResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
Return bool `xml:"return"` // True if the request succeeds
|
||||
}
|
||||
|
||||
// DeleteVpc deletes the specified VPC.
|
||||
//
|
||||
// You must detach or delete all gateways and resources that are associated with
|
||||
// the VPC before you can delete it. For example, you must terminate all
|
||||
// instances running in the VPC, delete all security groups associated with
|
||||
// the VPC (except the default one), delete all route tables associated with
|
||||
// the VPC (except the default one), and so on.
|
||||
//
|
||||
// See http://goo.gl/qawyrz for more details.
|
||||
func (ec2 *EC2) DeleteVpc(vpcId string) (resp *DeleteVpcResp, err error) {
|
||||
params := makeParams("DeleteVpc")
|
||||
params["VpcId"] = vpcId
|
||||
|
||||
resp = &DeleteVpcResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DescribeVpcsResp represents a response from a DescribeVpcs request
|
||||
//
|
||||
// See http://goo.gl/DWQWvZ for more details.
|
||||
type DescribeVpcsResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
VPCs []VPC `xml:"vpcSet>item"` // Information about one or more VPCs.
|
||||
}
|
||||
|
||||
// DescribeVpcs describes one or more of your VPCs.
|
||||
//
|
||||
// See http://goo.gl/DWQWvZ for more details.
|
||||
func (ec2 *EC2) DescribeVpcs(vpcIds []string, filter *Filter) (resp *DescribeVpcsResp, err error) {
|
||||
params := makeParams("DescribeVpcs")
|
||||
addParamsList(params, "VpcId", vpcIds)
|
||||
filter.addParams(params)
|
||||
resp = &DescribeVpcsResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteRouteResp represents a response from a DeleteRoute request
|
||||
//
|
||||
// See http://goo.gl/Uqyt3w for more details.
|
||||
type DeleteRouteResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
Return bool `xml:"return"` // True if the request succeeds
|
||||
}
|
||||
|
||||
// DeleteRoute deletes the specified route from the specified route table.
|
||||
//
|
||||
// See http://goo.gl/Uqyt3w for more details.
|
||||
func (ec2 *EC2) DeleteRoute(routeTableId, destinationCidrBlock string) (resp *DeleteRouteResp, err error) {
|
||||
params := makeParams("DeleteRoute")
|
||||
params["RouteTableId"] = routeTableId
|
||||
params["DestinationCidrBlock"] = destinationCidrBlock
|
||||
resp = &DeleteRouteResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateRouteResp represents a response from a CreateRoute request
|
||||
//
|
||||
// See http://goo.gl/c6Bg7e for more details.
|
||||
type CreateRouteResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
Return bool `xml:"return"` // True if the request succeeds
|
||||
}
|
||||
|
||||
// CreateRoute structure contains the options for a CreateRoute API call.
|
||||
type CreateRoute struct {
|
||||
DestinationCidrBlock string
|
||||
GatewayId string
|
||||
InstanceId string
|
||||
NetworkInterfaceId string
|
||||
VpcPeeringConnectionId string
|
||||
}
|
||||
|
||||
// CreateRoute creates a route in a route table within a VPC.
|
||||
// You must specify one of the following targets: Internet gateway or virtual
|
||||
// private gateway, NAT instance, VPC peering connection, or network interface.
|
||||
//
|
||||
// See http://goo.gl/c6Bg7e for more details.
|
||||
func (ec2 *EC2) CreateRoute(routeTableId string, options *CreateRoute) (resp *CreateRouteResp, err error) {
|
||||
params := makeParams("CreateRoute")
|
||||
params["RouteTableId"] = routeTableId
|
||||
if options.DestinationCidrBlock != "" {
|
||||
params["DestinationCidrBlock"] = options.DestinationCidrBlock
|
||||
}
|
||||
if options.GatewayId != "" {
|
||||
params["GatewayId"] = options.GatewayId
|
||||
}
|
||||
if options.InstanceId != "" {
|
||||
params["InstanceId"] = options.InstanceId
|
||||
}
|
||||
if options.NetworkInterfaceId != "" {
|
||||
params["NetworkInterfaceId"] = options.NetworkInterfaceId
|
||||
}
|
||||
if options.VpcPeeringConnectionId != "" {
|
||||
params["VpcPeeringConnectionId"] = options.VpcPeeringConnectionId
|
||||
}
|
||||
resp = &CreateRouteResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Subnet describes a subnet
|
||||
//
|
||||
// See http://goo.gl/bifW4R
|
||||
type Subnet struct {
|
||||
AvailabilityZone string `xml:"availabilityZone"`
|
||||
AvailableIpAddressCount int `xml:"availableIpAddressCount"`
|
||||
CidrBlock string `xml:"cidrBlock"`
|
||||
DefaultForAZ bool `xml:"defaultForAz"`
|
||||
MapPublicIpOnLaunch bool `xml:"mapPublicIpOnLaunch"`
|
||||
State string `xml:"state"`
|
||||
SubnetId string `xml:"subnetId"`
|
||||
Tags []Tag `xml:"tagSet>item"`
|
||||
VpcId string `xml:"vpcId"`
|
||||
}
|
||||
|
||||
// DescribeSubnetsResp represents a response from a DescribeSubnets request
|
||||
//
|
||||
// See https://goo.gl/1s0UQd for more details.
|
||||
type DescribeSubnetsResp struct {
|
||||
RequestId string `xml:"requestId"`
|
||||
Subnets []Subnet `xml:"subnetSet>item"`
|
||||
}
|
||||
|
||||
// DescribeSubnets describes one or more Subnets.
|
||||
//
|
||||
// See https://goo.gl/1s0UQd for more details.
|
||||
func (ec2 *EC2) DescribeSubnets(subnetIds []string, filter *Filter) (resp *DescribeSubnetsResp, err error) {
|
||||
params := makeParams("DescribeSubnets")
|
||||
addParamsList(params, "SubnetId", subnetIds)
|
||||
filter.addParams(params)
|
||||
resp = &DescribeSubnetsResp{}
|
||||
err = ec2.query(params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
224
vendor/github.com/goamz/goamz/ec2/vpc_test.go
сгенерированный
поставляемый
224
vendor/github.com/goamz/goamz/ec2/vpc_test.go
сгенерированный
поставляемый
@@ -1,224 +0,0 @@
|
||||
package ec2_test
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/ec2"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func (s *S) TestCreateRouteTable(c *C) {
|
||||
testServer.Response(200, nil, CreateRouteTableExample)
|
||||
|
||||
resp, err := s.ec2.CreateRouteTable("vpc-11ad4878")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"CreateRouteTable"})
|
||||
c.Assert(req.Form["VpcId"], DeepEquals, []string{"vpc-11ad4878"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "59abcd43-35bd-4eac-99ed-be587EXAMPLE")
|
||||
c.Assert(resp.RouteTable.Id, Equals, "rtb-f9ad4890")
|
||||
c.Assert(resp.RouteTable.VpcId, Equals, "vpc-11ad4878")
|
||||
c.Assert(resp.RouteTable.Routes, HasLen, 1)
|
||||
c.Assert(resp.RouteTable.Routes[0], DeepEquals, ec2.Route{
|
||||
DestinationCidrBlock: "10.0.0.0/22",
|
||||
GatewayId: "local",
|
||||
State: "active",
|
||||
})
|
||||
c.Assert(resp.RouteTable.Associations, HasLen, 0)
|
||||
c.Assert(resp.RouteTable.Tags, HasLen, 0)
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeRouteTables(c *C) {
|
||||
testServer.Response(200, nil, DescribeRouteTablesExample)
|
||||
|
||||
filter := ec2.NewFilter()
|
||||
filter.Add("key1", "value1")
|
||||
filter.Add("key2", "value2", "value3")
|
||||
|
||||
resp, err := s.ec2.DescribeRouteTables([]string{"rt1", "rt2"}, nil)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeRouteTables"})
|
||||
c.Assert(req.Form["RouteTableId.1"], DeepEquals, []string{"rt1"})
|
||||
c.Assert(req.Form["RouteTableId.2"], DeepEquals, []string{"rt2"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "6f570b0b-9c18-4b07-bdec-73740dcf861aEXAMPLE")
|
||||
c.Assert(resp.RouteTables, HasLen, 2)
|
||||
|
||||
rt1 := resp.RouteTables[0]
|
||||
c.Assert(rt1.Id, Equals, "rtb-13ad487a")
|
||||
c.Assert(rt1.VpcId, Equals, "vpc-11ad4878")
|
||||
c.Assert(rt1.Routes, DeepEquals, []ec2.Route{
|
||||
{DestinationCidrBlock: "10.0.0.0/22", GatewayId: "local", State: "active", Origin: "CreateRouteTable"},
|
||||
})
|
||||
c.Assert(rt1.Associations, DeepEquals, []ec2.RouteTableAssociation{
|
||||
{Id: "rtbassoc-12ad487b", RouteTableId: "rtb-13ad487a", Main: true},
|
||||
})
|
||||
|
||||
rt2 := resp.RouteTables[1]
|
||||
c.Assert(rt2.Id, Equals, "rtb-f9ad4890")
|
||||
c.Assert(rt2.VpcId, Equals, "vpc-11ad4878")
|
||||
c.Assert(rt2.Routes, DeepEquals, []ec2.Route{
|
||||
{DestinationCidrBlock: "10.0.0.0/22", GatewayId: "local", State: "active", Origin: "CreateRouteTable"},
|
||||
{DestinationCidrBlock: "0.0.0.0/0", GatewayId: "igw-eaad4883", State: "active"},
|
||||
})
|
||||
c.Assert(rt2.Associations, DeepEquals, []ec2.RouteTableAssociation{
|
||||
{Id: "rtbassoc-faad4893", RouteTableId: "rtb-f9ad4890", SubnetId: "subnet-15ad487c"},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *S) TestAssociateRouteTable(c *C) {
|
||||
testServer.Response(200, nil, AssociateRouteTableExample)
|
||||
|
||||
resp, err := s.ec2.AssociateRouteTable("rtb-e4ad488d", "subnet-15ad487c")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"AssociateRouteTable"})
|
||||
c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-e4ad488d"})
|
||||
c.Assert(req.Form["SubnetId"], DeepEquals, []string{"subnet-15ad487c"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
|
||||
c.Assert(resp.AssociationId, Equals, "rtbassoc-f8ad4891")
|
||||
}
|
||||
|
||||
func (s *S) TestDisassociateRouteTable(c *C) {
|
||||
testServer.Response(200, nil, DisassociateRouteTableExample)
|
||||
|
||||
resp, err := s.ec2.DisassociateRouteTable("rtbassoc-f8ad4891")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"DisassociateRouteTable"})
|
||||
c.Assert(req.Form["AssociationId"], DeepEquals, []string{"rtbassoc-f8ad4891"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
|
||||
c.Assert(resp.Return, Equals, true)
|
||||
}
|
||||
|
||||
func (s *S) TestReplaceRouteTableAssociation(c *C) {
|
||||
testServer.Response(200, nil, ReplaceRouteTableAssociationExample)
|
||||
|
||||
resp, err := s.ec2.ReplaceRouteTableAssociation("rtbassoc-f8ad4891", "rtb-f9ad4890")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"ReplaceRouteTableAssociation"})
|
||||
c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-f9ad4890"})
|
||||
c.Assert(req.Form["AssociationId"], DeepEquals, []string{"rtbassoc-f8ad4891"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-88ed-be587EXAMPLE")
|
||||
c.Assert(resp.NewAssociationId, Equals, "rtbassoc-faad2958")
|
||||
}
|
||||
|
||||
func (s *S) TestDeleteRouteTable(c *C) {
|
||||
testServer.Response(200, nil, DeleteRouteTableExample)
|
||||
|
||||
resp, err := s.ec2.DeleteRouteTable("rtb-f9ad4890")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteRouteTable"})
|
||||
c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-f9ad4890"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "49dbff89-35bd-4eac-99ed-be587EXAMPLE")
|
||||
c.Assert(resp.Return, Equals, true)
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeVpcs(c *C) {
|
||||
testServer.Response(200, nil, DescribeVpcsExample)
|
||||
|
||||
filter := ec2.NewFilter()
|
||||
filter.Add("key1", "value1")
|
||||
filter.Add("key2", "value2", "value3")
|
||||
|
||||
resp, err := s.ec2.DescribeVpcs([]string{"id1", "id2"}, filter)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeVpcs"})
|
||||
c.Assert(req.Form["VpcId.1"], DeepEquals, []string{"id1"})
|
||||
c.Assert(req.Form["VpcId.2"], DeepEquals, []string{"id2"})
|
||||
c.Assert(req.Form["VpcId.3"], IsNil)
|
||||
c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"key1"})
|
||||
c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"})
|
||||
c.Assert(req.Form["Filter.1.Value.2"], IsNil)
|
||||
c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"key2"})
|
||||
c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"})
|
||||
c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE")
|
||||
c.Assert(resp.VPCs[0].VpcId, Equals, "vpc-1a2b3c4d")
|
||||
c.Assert(resp.VPCs, HasLen, 1)
|
||||
}
|
||||
|
||||
func (s *S) TestCreateVpc(c *C) {
|
||||
testServer.Response(200, nil, CreateVpcExample)
|
||||
|
||||
resp, err := s.ec2.CreateVpc("foo", "bar")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["CidrBlock"], DeepEquals, []string{"foo"})
|
||||
c.Assert(req.Form["InstanceTenancy"], DeepEquals, []string{"bar"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE")
|
||||
c.Assert(resp.VPC.VpcId, Equals, "vpc-1a2b3c4d")
|
||||
c.Assert(resp.VPC.State, Equals, "pending")
|
||||
c.Assert(resp.VPC.CidrBlock, Equals, "10.0.0.0/16")
|
||||
c.Assert(resp.VPC.DHCPOptionsID, Equals, "dopt-1a2b3c4d2")
|
||||
c.Assert(resp.VPC.InstanceTenancy, Equals, "default")
|
||||
}
|
||||
|
||||
func (s *S) TestDeleteVpc(c *C) {
|
||||
testServer.Response(200, nil, DeleteVpcExample)
|
||||
|
||||
resp, err := s.ec2.DeleteVpc("id1")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["VpcId"], DeepEquals, []string{"id1"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestCreateRoute(c *C) {
|
||||
testServer.Response(200, nil, CreateRouteExample)
|
||||
|
||||
options := ec2.CreateRoute{
|
||||
DestinationCidrBlock: "12.34.56.78/90",
|
||||
GatewayId: "foo",
|
||||
InstanceId: "i-bar",
|
||||
NetworkInterfaceId: "foobar",
|
||||
VpcPeeringConnectionId: "barfoo",
|
||||
}
|
||||
|
||||
resp, err := s.ec2.CreateRoute("rtb-deadbeef", &options)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-deadbeef"})
|
||||
c.Assert(req.Form["DestinationCidrBlock"], DeepEquals, []string{"12.34.56.78/90"})
|
||||
c.Assert(req.Form["GatewayId"], DeepEquals, []string{"foo"})
|
||||
c.Assert(req.Form["InstanceId"], DeepEquals, []string{"i-bar"})
|
||||
c.Assert(req.Form["NetworkInterfaceId"], DeepEquals, []string{"foobar"})
|
||||
c.Assert(req.Form["VpcPeeringConnectionId"], DeepEquals, []string{"barfoo"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "b4998629-3000-437f-b382-cc96fEXAMPLE")
|
||||
c.Assert(resp.Return, Equals, true)
|
||||
}
|
||||
|
||||
func (s *S) TestDeleteRoute(c *C) {
|
||||
testServer.Response(200, nil, DeleteRouteExample)
|
||||
|
||||
resp, err := s.ec2.DeleteRoute("rtb-baddcafe", "foobar")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-baddcafe"})
|
||||
c.Assert(req.Form["DestinationCidrBlock"], DeepEquals, []string{"foobar"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
|
||||
c.Assert(resp.Return, Equals, true)
|
||||
}
|
||||
1075
vendor/github.com/goamz/goamz/ecs/ecs.go
сгенерированный
поставляемый
1075
vendor/github.com/goamz/goamz/ecs/ecs.go
сгенерированный
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
806
vendor/github.com/goamz/goamz/ecs/ecs_test.go
сгенерированный
поставляемый
806
vendor/github.com/goamz/goamz/ecs/ecs_test.go
сгенерированный
поставляемый
@@ -1,806 +0,0 @@
|
||||
package ecs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/testutil"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
type S struct {
|
||||
ecs *ECS
|
||||
}
|
||||
|
||||
var testServer = testutil.NewHTTPServer()
|
||||
|
||||
var mockTest bool
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
testServer.Start()
|
||||
auth := aws.Auth{AccessKey: "abc", SecretKey: "123"}
|
||||
s.ecs = New(auth, aws.Region{ECSEndpoint: testServer.URL})
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
testServer.Flush()
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Detailed Unit Tests
|
||||
|
||||
func (s *S) TestCreateCluster(c *C) {
|
||||
testServer.Response(200, nil, CreateClusterResponse)
|
||||
req := &CreateClusterReq{
|
||||
ClusterName: "default",
|
||||
}
|
||||
resp, err := s.ecs.CreateCluster(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "CreateCluster")
|
||||
c.Assert(values.Get("clusterName"), Equals, "default")
|
||||
|
||||
c.Assert(resp.Cluster.ClusterArn, Equals, "arn:aws:ecs:region:aws_account_id:cluster/default")
|
||||
c.Assert(resp.Cluster.ClusterName, Equals, "default")
|
||||
c.Assert(resp.Cluster.Status, Equals, "ACTIVE")
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDeregisterContainerInstance(c *C) {
|
||||
testServer.Response(200, nil, DeregisterContainerInstanceResponse)
|
||||
req := &DeregisterContainerInstanceReq{
|
||||
Cluster: "default",
|
||||
ContainerInstance: "uuid",
|
||||
Force: true,
|
||||
}
|
||||
resp, err := s.ecs.DeregisterContainerInstance(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "DeregisterContainerInstance")
|
||||
c.Assert(values.Get("cluster"), Equals, "default")
|
||||
c.Assert(values.Get("containerInstance"), Equals, "uuid")
|
||||
c.Assert(values.Get("force"), Equals, "true")
|
||||
|
||||
expectedResource := []Resource{
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 2048,
|
||||
LongValue: 0,
|
||||
Name: "CPU",
|
||||
Type: "INTEGER",
|
||||
},
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 3955,
|
||||
LongValue: 0,
|
||||
Name: "MEMORY",
|
||||
Type: "INTEGER",
|
||||
},
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 0,
|
||||
LongValue: 0,
|
||||
Name: "PORTS",
|
||||
StringSetValue: []string{"2376", "22", "51678", "2375"},
|
||||
Type: "STRINGSET",
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.ContainerInstance.AgentConnected, Equals, false)
|
||||
c.Assert(resp.ContainerInstance.ContainerInstanceArn, Equals, "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID")
|
||||
c.Assert(resp.ContainerInstance.Status, Equals, "INACTIVE")
|
||||
c.Assert(resp.ContainerInstance.Ec2InstanceId, Equals, "instance_id")
|
||||
c.Assert(resp.ContainerInstance.RegisteredResources, DeepEquals, expectedResource)
|
||||
c.Assert(resp.ContainerInstance.RemainingResources, DeepEquals, expectedResource)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDeregisterTaskDefinition(c *C) {
|
||||
testServer.Response(200, nil, DeregisterTaskDefinitionResponse)
|
||||
req := &DeregisterTaskDefinitionReq{
|
||||
TaskDefinition: "sleep360:2",
|
||||
}
|
||||
resp, err := s.ecs.DeregisterTaskDefinition(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "DeregisterTaskDefinition")
|
||||
c.Assert(values.Get("taskDefinition"), Equals, "sleep360:2")
|
||||
|
||||
expected := TaskDefinition{
|
||||
Family: "sleep360",
|
||||
Revision: 2,
|
||||
TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2",
|
||||
ContainerDefinitions: []ContainerDefinition{
|
||||
{
|
||||
Command: []string{"sleep", "360"},
|
||||
Cpu: 10,
|
||||
EntryPoint: []string{"/bin/sh"},
|
||||
Environment: []KeyValuePair{
|
||||
{
|
||||
Name: "envVar",
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
Essential: true,
|
||||
Image: "busybox",
|
||||
Memory: 10,
|
||||
Name: "sleep",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.TaskDefinition, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeClusters(c *C) {
|
||||
testServer.Response(200, nil, DescribeClustersResponse)
|
||||
req := &DescribeClustersReq{
|
||||
Clusters: []string{"test", "default"},
|
||||
}
|
||||
resp, err := s.ecs.DescribeClusters(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeClusters")
|
||||
c.Assert(values.Get("clusters.member.1"), Equals, "test")
|
||||
c.Assert(values.Get("clusters.member.2"), Equals, "default")
|
||||
|
||||
expected := []Cluster{
|
||||
{
|
||||
ClusterName: "test",
|
||||
ClusterArn: "arn:aws:ecs:us-east-1:aws_account_id:cluster/test",
|
||||
Status: "ACTIVE",
|
||||
},
|
||||
{
|
||||
ClusterName: "default",
|
||||
ClusterArn: "arn:aws:ecs:us-east-1:aws_account_id:cluster/default",
|
||||
Status: "ACTIVE",
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.Clusters, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeContainerInstances(c *C) {
|
||||
testServer.Response(200, nil, DescribeContainerInstancesResponse)
|
||||
req := &DescribeContainerInstancesReq{
|
||||
Cluster: "test",
|
||||
ContainerInstances: []string{"arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID"},
|
||||
}
|
||||
resp, err := s.ecs.DescribeContainerInstances(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeContainerInstances")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("containerInstances.member.1"),
|
||||
Equals, "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID")
|
||||
|
||||
expected := []ContainerInstance{
|
||||
ContainerInstance{
|
||||
AgentConnected: true,
|
||||
ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID",
|
||||
Status: "ACTIVE",
|
||||
Ec2InstanceId: "instance_id",
|
||||
RegisteredResources: []Resource{
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 2048,
|
||||
LongValue: 0,
|
||||
Name: "CPU",
|
||||
Type: "INTEGER",
|
||||
},
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 3955,
|
||||
LongValue: 0,
|
||||
Name: "MEMORY",
|
||||
Type: "INTEGER",
|
||||
},
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 0,
|
||||
LongValue: 0,
|
||||
Name: "PORTS",
|
||||
StringSetValue: []string{"2376", "22", "51678", "2375"},
|
||||
Type: "STRINGSET",
|
||||
},
|
||||
},
|
||||
RemainingResources: []Resource{
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 2048,
|
||||
LongValue: 0,
|
||||
Name: "CPU",
|
||||
Type: "INTEGER",
|
||||
},
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 3955,
|
||||
LongValue: 0,
|
||||
Name: "MEMORY",
|
||||
Type: "INTEGER",
|
||||
},
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 0,
|
||||
LongValue: 0,
|
||||
Name: "PORTS",
|
||||
StringSetValue: []string{"2376", "22", "51678", "2375"},
|
||||
Type: "STRINGSET",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.ContainerInstances, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeTaskDefinition(c *C) {
|
||||
testServer.Response(200, nil, DescribeTaskDefinitionResponse)
|
||||
req := &DescribeTaskDefinitionReq{
|
||||
TaskDefinition: "sleep360:2",
|
||||
}
|
||||
resp, err := s.ecs.DescribeTaskDefinition(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeTaskDefinition")
|
||||
c.Assert(values.Get("taskDefinition"), Equals, "sleep360:2")
|
||||
|
||||
expected := TaskDefinition{
|
||||
Family: "sleep360",
|
||||
Revision: 2,
|
||||
TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2",
|
||||
ContainerDefinitions: []ContainerDefinition{
|
||||
{
|
||||
Command: []string{"sleep", "360"},
|
||||
Cpu: 10,
|
||||
EntryPoint: []string{"/bin/sh"},
|
||||
Environment: []KeyValuePair{
|
||||
{
|
||||
Name: "envVar",
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
Essential: true,
|
||||
Image: "busybox",
|
||||
Memory: 10,
|
||||
Name: "sleep",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.TaskDefinition, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeTasks(c *C) {
|
||||
testServer.Response(200, nil, DescribeTasksResponse)
|
||||
req := &DescribeTasksReq{
|
||||
Cluster: "test",
|
||||
Tasks: []string{"arn:aws:ecs:us-east-1:aws_account_id:task/UUID"},
|
||||
}
|
||||
resp, err := s.ecs.DescribeTasks(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeTasks")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("tasks.member.1"),
|
||||
Equals, "arn:aws:ecs:us-east-1:aws_account_id:task/UUID")
|
||||
|
||||
expected := []Task{
|
||||
Task{
|
||||
Containers: []Container{
|
||||
{
|
||||
TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
Name: "sleep",
|
||||
ContainerArn: "arn:aws:ecs:us-east-1:aws_account_id:container/UUID",
|
||||
LastStatus: "RUNNING",
|
||||
},
|
||||
},
|
||||
Overrides: TaskOverride{
|
||||
ContainerOverrides: []ContainerOverride{
|
||||
{
|
||||
Name: "sleep",
|
||||
},
|
||||
},
|
||||
},
|
||||
DesiredStatus: "RUNNING",
|
||||
TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID",
|
||||
LastStatus: "RUNNING",
|
||||
TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2",
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.Tasks, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestDiscoverPollEndpoint(c *C) {
|
||||
testServer.Response(200, nil, DiscoverPollEndpointResponse)
|
||||
req := &DiscoverPollEndpointReq{
|
||||
ContainerInstance: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID",
|
||||
}
|
||||
resp, err := s.ecs.DiscoverPollEndpoint(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "DiscoverPollEndpoint")
|
||||
c.Assert(values.Get("containerInstance"),
|
||||
Equals, "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID")
|
||||
|
||||
c.Assert(resp.Endpoint, Equals, "https://ecs-x-1.us-east-1.amazonaws.com/")
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestListClusters(c *C) {
|
||||
testServer.Response(200, nil, ListClustersResponse)
|
||||
req := &ListClustersReq{
|
||||
MaxResults: 2,
|
||||
NextToken: "Token_UUID",
|
||||
}
|
||||
resp, err := s.ecs.ListClusters(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "ListClusters")
|
||||
c.Assert(values.Get("maxResults"), Equals, "2")
|
||||
c.Assert(values.Get("nextToken"), Equals, "Token_UUID")
|
||||
|
||||
c.Assert(resp.ClusterArns, DeepEquals, []string{"arn:aws:ecs:us-east-1:aws_account_id:cluster/default",
|
||||
"arn:aws:ecs:us-east-1:aws_account_id:cluster/test"})
|
||||
c.Assert(resp.NextToken, Equals, "token_UUID")
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestListContainerInstances(c *C) {
|
||||
testServer.Response(200, nil, ListContainerInstancesResponse)
|
||||
req := &ListContainerInstancesReq{
|
||||
MaxResults: 2,
|
||||
NextToken: "Token_UUID",
|
||||
Cluster: "test",
|
||||
}
|
||||
resp, err := s.ecs.ListContainerInstances(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "ListContainerInstances")
|
||||
c.Assert(values.Get("maxResults"), Equals, "2")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("nextToken"), Equals, "Token_UUID")
|
||||
|
||||
c.Assert(resp.ContainerInstanceArns, DeepEquals, []string{
|
||||
"arn:aws:ecs:us-east-1:aws_account_id:container-instance/uuid-1",
|
||||
"arn:aws:ecs:us-east-1:aws_account_id:container-instance/uuid-2"})
|
||||
c.Assert(resp.NextToken, Equals, "token_UUID")
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestListTaskDefinitions(c *C) {
|
||||
testServer.Response(200, nil, ListTaskDefinitionsResponse)
|
||||
req := &ListTaskDefinitionsReq{
|
||||
MaxResults: 2,
|
||||
NextToken: "Token_UUID",
|
||||
FamilyPrefix: "sleep360",
|
||||
}
|
||||
resp, err := s.ecs.ListTaskDefinitions(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "ListTaskDefinitions")
|
||||
c.Assert(values.Get("maxResults"), Equals, "2")
|
||||
c.Assert(values.Get("familyPrefix"), Equals, "sleep360")
|
||||
c.Assert(values.Get("nextToken"), Equals, "Token_UUID")
|
||||
|
||||
c.Assert(resp.TaskDefinitionArns, DeepEquals, []string{
|
||||
"arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:1",
|
||||
"arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2"})
|
||||
c.Assert(resp.NextToken, Equals, "token_UUID")
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestListTasks(c *C) {
|
||||
testServer.Response(200, nil, ListTasksResponse)
|
||||
req := &ListTasksReq{
|
||||
MaxResults: 2,
|
||||
NextToken: "Token_UUID",
|
||||
Family: "sleep360",
|
||||
Cluster: "test",
|
||||
ContainerInstance: "container_uuid",
|
||||
}
|
||||
resp, err := s.ecs.ListTasks(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "ListTasks")
|
||||
c.Assert(values.Get("maxResults"), Equals, "2")
|
||||
c.Assert(values.Get("family"), Equals, "sleep360")
|
||||
c.Assert(values.Get("containerInstance"), Equals, "container_uuid")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("nextToken"), Equals, "Token_UUID")
|
||||
|
||||
c.Assert(resp.TaskArns, DeepEquals, []string{
|
||||
"arn:aws:ecs:us-east-1:aws_account_id:task/uuid_1",
|
||||
"arn:aws:ecs:us-east-1:aws_account_id:task/uuid_2"})
|
||||
c.Assert(resp.NextToken, Equals, "token_UUID")
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestRegisterContainerInstance(c *C) {
|
||||
testServer.Response(200, nil, RegisterContainerInstanceResponse)
|
||||
|
||||
resources := []Resource{
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 2048,
|
||||
LongValue: 0,
|
||||
Name: "CPU",
|
||||
Type: "INTEGER",
|
||||
},
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 3955,
|
||||
LongValue: 0,
|
||||
Name: "MEMORY",
|
||||
Type: "INTEGER",
|
||||
},
|
||||
{
|
||||
DoubleValue: 0.0,
|
||||
IntegerValue: 0,
|
||||
LongValue: 0,
|
||||
Name: "PORTS",
|
||||
StringSetValue: []string{"2376", "22", "51678", "2375"},
|
||||
Type: "STRINGSET",
|
||||
},
|
||||
}
|
||||
|
||||
req := &RegisterContainerInstanceReq{
|
||||
Cluster: "default",
|
||||
InstanceIdentityDocument: "foo",
|
||||
InstanceIdentityDocumentSignature: "baz",
|
||||
TotalResources: resources,
|
||||
}
|
||||
|
||||
resp, err := s.ecs.RegisterContainerInstance(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "RegisterContainerInstance")
|
||||
c.Assert(values.Get("cluster"), Equals, "default")
|
||||
c.Assert(values.Get("instanceIdentityDocument"), Equals, "foo")
|
||||
c.Assert(values.Get("instanceIdentityDocumentSignature"), Equals, "baz")
|
||||
c.Assert(values.Get("totalResources.member.1.doubleValue"), Equals, "0.0")
|
||||
c.Assert(values.Get("totalResources.member.1.integerValue"), Equals, "2048")
|
||||
c.Assert(values.Get("totalResources.member.1.longValue"), Equals, "0")
|
||||
c.Assert(values.Get("totalResources.member.1.name"), Equals, "CPU")
|
||||
c.Assert(values.Get("totalResources.member.1.type"), Equals, "INTEGER")
|
||||
c.Assert(values.Get("totalResources.member.2.doubleValue"), Equals, "0.0")
|
||||
c.Assert(values.Get("totalResources.member.2.integerValue"), Equals, "3955")
|
||||
c.Assert(values.Get("totalResources.member.2.longValue"), Equals, "0")
|
||||
c.Assert(values.Get("totalResources.member.2.name"), Equals, "MEMORY")
|
||||
c.Assert(values.Get("totalResources.member.2.type"), Equals, "INTEGER")
|
||||
c.Assert(values.Get("totalResources.member.3.doubleValue"), Equals, "0.0")
|
||||
c.Assert(values.Get("totalResources.member.3.integerValue"), Equals, "0")
|
||||
c.Assert(values.Get("totalResources.member.3.longValue"), Equals, "0")
|
||||
c.Assert(values.Get("totalResources.member.3.name"), Equals, "PORTS")
|
||||
c.Assert(values.Get("totalResources.member.3.stringSetValue.member.1"), Equals, "2376")
|
||||
c.Assert(values.Get("totalResources.member.3.stringSetValue.member.2"), Equals, "22")
|
||||
c.Assert(values.Get("totalResources.member.3.stringSetValue.member.3"), Equals, "51678")
|
||||
c.Assert(values.Get("totalResources.member.3.stringSetValue.member.4"), Equals, "2375")
|
||||
c.Assert(values.Get("totalResources.member.3.type"), Equals, "STRINGSET")
|
||||
|
||||
c.Assert(resp.ContainerInstance.AgentConnected, Equals, true)
|
||||
c.Assert(resp.ContainerInstance.ContainerInstanceArn, Equals, "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID")
|
||||
c.Assert(resp.ContainerInstance.Status, Equals, "ACTIVE")
|
||||
c.Assert(resp.ContainerInstance.Ec2InstanceId, Equals, "instance_id")
|
||||
c.Assert(resp.ContainerInstance.RegisteredResources, DeepEquals, resources)
|
||||
c.Assert(resp.ContainerInstance.RemainingResources, DeepEquals, resources)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestRegisterTaskDefinition(c *C) {
|
||||
testServer.Response(200, nil, RegisterTaskDefinitionResponse)
|
||||
|
||||
CDefinitions := []ContainerDefinition{
|
||||
{
|
||||
Command: []string{"sleep", "360"},
|
||||
Cpu: 10,
|
||||
EntryPoint: []string{"/bin/sh"},
|
||||
Environment: []KeyValuePair{
|
||||
{
|
||||
Name: "envVar",
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
Essential: true,
|
||||
Image: "busybox",
|
||||
Memory: 10,
|
||||
Name: "sleep",
|
||||
MountPoints: []MountPoint{
|
||||
{
|
||||
ContainerPath: "/tmp/myfile",
|
||||
ReadOnly: false,
|
||||
SourceVolume: "/srv/myfile",
|
||||
},
|
||||
{
|
||||
ContainerPath: "/tmp/myfile2",
|
||||
ReadOnly: true,
|
||||
SourceVolume: "/srv/myfile2",
|
||||
},
|
||||
},
|
||||
VolumesFrom: []VolumeFrom{
|
||||
{
|
||||
ReadOnly: true,
|
||||
SourceContainer: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
req := &RegisterTaskDefinitionReq{
|
||||
Family: "sleep360",
|
||||
ContainerDefinitions: CDefinitions,
|
||||
Volumes: []Volume{
|
||||
{
|
||||
Name: "/srv/myfile",
|
||||
Host: HostVolumeProperties{
|
||||
SourcePath: "/srv/myfile",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
resp, err := s.ecs.RegisterTaskDefinition(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "RegisterTaskDefinition")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.command.member.1"), Equals, "sleep")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.command.member.2"), Equals, "360")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.cpu"), Equals, "10")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.memory"), Equals, "10")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.entryPoint.member.1"), Equals, "/bin/sh")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.environment.member.1.name"), Equals, "envVar")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.environment.member.1.value"), Equals, "foo")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.essential"), Equals, "true")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.image"), Equals, "busybox")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.memory"), Equals, "10")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.name"), Equals, "sleep")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.1.containerPath"), Equals, "/tmp/myfile")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.1.readOnly"), Equals, "false")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.1.sourceVolume"), Equals, "/srv/myfile")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.2.containerPath"), Equals, "/tmp/myfile2")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.2.readOnly"), Equals, "true")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.2.sourceVolume"), Equals, "/srv/myfile2")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.volumesFrom.member.1.readOnly"), Equals, "true")
|
||||
c.Assert(values.Get("containerDefinitions.member.1.volumesFrom.member.1.sourceContainer"), Equals, "foo")
|
||||
|
||||
c.Assert(values.Get("family"), Equals, "sleep360")
|
||||
c.Assert(values.Get("volumes.member.1.name"), Equals, "/srv/myfile")
|
||||
c.Assert(values.Get("volumes.member.1.host.sourcePath"), Equals, "/srv/myfile")
|
||||
|
||||
expected := TaskDefinition{
|
||||
Family: "sleep360",
|
||||
Revision: 2,
|
||||
TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2",
|
||||
ContainerDefinitions: CDefinitions,
|
||||
Volumes: []Volume{
|
||||
{
|
||||
Name: "/srv/myfile",
|
||||
Host: HostVolumeProperties{
|
||||
SourcePath: "/srv/myfile",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.TaskDefinition, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestRunTask(c *C) {
|
||||
testServer.Response(200, nil, RunTaskResponse)
|
||||
req := &RunTaskReq{
|
||||
Cluster: "test",
|
||||
Count: 1,
|
||||
TaskDefinition: "sleep360:2",
|
||||
}
|
||||
resp, err := s.ecs.RunTask(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "RunTask")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("count"), Equals, "1")
|
||||
c.Assert(values.Get("taskDefinition"), Equals, "sleep360:2")
|
||||
|
||||
expected := []Task{
|
||||
Task{
|
||||
Containers: []Container{
|
||||
{
|
||||
TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
Name: "sleep",
|
||||
ContainerArn: "arn:aws:ecs:us-east-1:aws_account_id:container/UUID",
|
||||
LastStatus: "RUNNING",
|
||||
},
|
||||
},
|
||||
Overrides: TaskOverride{
|
||||
ContainerOverrides: []ContainerOverride{
|
||||
{
|
||||
Name: "sleep",
|
||||
},
|
||||
},
|
||||
},
|
||||
DesiredStatus: "RUNNING",
|
||||
TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID",
|
||||
LastStatus: "PENDING",
|
||||
TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2",
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.Tasks, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestStartTask(c *C) {
|
||||
testServer.Response(200, nil, StartTaskResponse)
|
||||
req := &StartTaskReq{
|
||||
Cluster: "test",
|
||||
ContainerInstances: []string{"containerUUID"},
|
||||
TaskDefinition: "sleep360:2",
|
||||
}
|
||||
resp, err := s.ecs.StartTask(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "StartTask")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("taskDefinition"), Equals, "sleep360:2")
|
||||
c.Assert(values.Get("containerInstances.member.1"), Equals, "containerUUID")
|
||||
|
||||
expected := []Task{
|
||||
Task{
|
||||
Containers: []Container{
|
||||
{
|
||||
TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
Name: "sleep",
|
||||
ContainerArn: "arn:aws:ecs:us-east-1:aws_account_id:container/UUID",
|
||||
LastStatus: "RUNNING",
|
||||
},
|
||||
},
|
||||
Overrides: TaskOverride{
|
||||
ContainerOverrides: []ContainerOverride{
|
||||
{
|
||||
Name: "sleep",
|
||||
},
|
||||
},
|
||||
},
|
||||
DesiredStatus: "RUNNING",
|
||||
TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID",
|
||||
LastStatus: "PENDING",
|
||||
TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2",
|
||||
},
|
||||
}
|
||||
|
||||
c.Assert(resp.Tasks, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestStopTask(c *C) {
|
||||
testServer.Response(200, nil, StopTaskResponse)
|
||||
req := &StopTaskReq{
|
||||
Cluster: "test",
|
||||
Task: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
}
|
||||
resp, err := s.ecs.StopTask(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "StopTask")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("task"), Equals, "arn:aws:ecs:us-east-1:aws_account_id:task/UUID")
|
||||
|
||||
expected := Task{
|
||||
Containers: []Container{
|
||||
{
|
||||
TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
Name: "sleep",
|
||||
ContainerArn: "arn:aws:ecs:us-east-1:aws_account_id:container/UUID",
|
||||
LastStatus: "RUNNING",
|
||||
},
|
||||
},
|
||||
Overrides: TaskOverride{
|
||||
ContainerOverrides: []ContainerOverride{
|
||||
{
|
||||
Name: "sleep",
|
||||
},
|
||||
},
|
||||
},
|
||||
DesiredStatus: "STOPPED",
|
||||
TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID",
|
||||
ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID",
|
||||
LastStatus: "RUNNING",
|
||||
TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2",
|
||||
}
|
||||
|
||||
c.Assert(resp.Task, DeepEquals, expected)
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestSubmitContainerStateChange(c *C) {
|
||||
testServer.Response(200, nil, SubmitContainerStateChangeResponse)
|
||||
networkBindings := []NetworkBinding{
|
||||
{
|
||||
BindIp: "127.0.0.1",
|
||||
ContainerPort: 80,
|
||||
HostPort: 80,
|
||||
},
|
||||
}
|
||||
req := &SubmitContainerStateChangeReq{
|
||||
Cluster: "test",
|
||||
ContainerName: "container",
|
||||
ExitCode: 0,
|
||||
Reason: "reason",
|
||||
Status: "status",
|
||||
Task: "taskUUID",
|
||||
NetworkBindings: networkBindings,
|
||||
}
|
||||
|
||||
resp, err := s.ecs.SubmitContainerStateChange(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "SubmitContainerStateChange")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("containerName"), Equals, "container")
|
||||
c.Assert(values.Get("exitCode"), Equals, "0")
|
||||
c.Assert(values.Get("reason"), Equals, "reason")
|
||||
c.Assert(values.Get("status"), Equals, "status")
|
||||
c.Assert(values.Get("task"), Equals, "taskUUID")
|
||||
c.Assert(values.Get("networkBindings.member.1.bindIp"), Equals, "127.0.0.1")
|
||||
c.Assert(values.Get("networkBindings.member.1.containerPort"), Equals, "80")
|
||||
c.Assert(values.Get("networkBindings.member.1.hostPort"), Equals, "80")
|
||||
|
||||
c.Assert(resp.Acknowledgment, Equals, "ACK")
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestSubmitTaskStateChange(c *C) {
|
||||
testServer.Response(200, nil, SubmitTaskStateChangeResponse)
|
||||
req := &SubmitTaskStateChangeReq{
|
||||
Cluster: "test",
|
||||
Reason: "reason",
|
||||
Status: "status",
|
||||
Task: "taskUUID",
|
||||
}
|
||||
|
||||
resp, err := s.ecs.SubmitTaskStateChange(req)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().PostForm
|
||||
c.Assert(values.Get("Version"), Equals, "2014-11-13")
|
||||
c.Assert(values.Get("Action"), Equals, "SubmitTaskStateChange")
|
||||
c.Assert(values.Get("cluster"), Equals, "test")
|
||||
c.Assert(values.Get("reason"), Equals, "reason")
|
||||
c.Assert(values.Get("status"), Equals, "status")
|
||||
c.Assert(values.Get("task"), Equals, "taskUUID")
|
||||
|
||||
c.Assert(resp.Acknowledgment, Equals, "ACK")
|
||||
c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE")
|
||||
}
|
||||
637
vendor/github.com/goamz/goamz/ecs/responses_test.go
сгенерированный
поставляемый
637
vendor/github.com/goamz/goamz/ecs/responses_test.go
сгенерированный
поставляемый
@@ -1,637 +0,0 @@
|
||||
package ecs
|
||||
|
||||
var CreateClusterResponse = `
|
||||
<CreateClusterResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
<CreateClusterResult>
|
||||
<cluster>
|
||||
<clusterArn>arn:aws:ecs:region:aws_account_id:cluster/default</clusterArn>
|
||||
<clusterName>default</clusterName>
|
||||
<status>ACTIVE</status>
|
||||
</cluster>
|
||||
</CreateClusterResult>
|
||||
</CreateClusterResponse>
|
||||
`
|
||||
var DeregisterContainerInstanceResponse = `
|
||||
<DeregisterContainerInstanceResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
<DeregisterContainerInstanceResult>
|
||||
<containerInstance>
|
||||
<agentConnected>False</agentConnected>
|
||||
<containerInstanceArn>arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID</containerInstanceArn>
|
||||
<ec2InstanceId>instance_id</ec2InstanceId>
|
||||
<status>INACTIVE</status>
|
||||
<registeredResources>
|
||||
<member>
|
||||
<integerValue>2048</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>CPU</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>3955</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>MEMORY</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>0</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>STRINGSET</type>
|
||||
<stringSetValue>
|
||||
<member>2376</member>
|
||||
<member>22</member>
|
||||
<member>51678</member>
|
||||
<member>2375</member>
|
||||
</stringSetValue>
|
||||
<name>PORTS</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
</registeredResources>
|
||||
<remainingResources>
|
||||
<member>
|
||||
<integerValue>2048</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>CPU</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>3955</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>MEMORY</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>0</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>STRINGSET</type>
|
||||
<stringSetValue>
|
||||
<member>2376</member>
|
||||
<member>22</member>
|
||||
<member>51678</member>
|
||||
<member>2375</member>
|
||||
</stringSetValue>
|
||||
<name>PORTS</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
</remainingResources>
|
||||
</containerInstance>
|
||||
</DeregisterContainerInstanceResult>
|
||||
</DeregisterContainerInstanceResponse>
|
||||
`
|
||||
|
||||
var DeregisterTaskDefinitionResponse = `
|
||||
<DeregisterTaskDefinitionResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<DeregisterTaskDefinitionResult>
|
||||
<taskDefinition>
|
||||
<revision>2</revision>
|
||||
<family>sleep360</family>
|
||||
<containerDefinitions>
|
||||
<member>
|
||||
<portMappings/>
|
||||
<essential>true</essential>
|
||||
<environment>
|
||||
<member>
|
||||
<name>envVar</name>
|
||||
<value>foo</value>
|
||||
</member>
|
||||
</environment>
|
||||
<entryPoint>
|
||||
<member>/bin/sh</member>
|
||||
</entryPoint>
|
||||
<name>sleep</name>
|
||||
<command>
|
||||
<member>sleep</member>
|
||||
<member>360</member>
|
||||
</command>
|
||||
<cpu>10</cpu>
|
||||
<image>busybox</image>
|
||||
<memory>10</memory>
|
||||
</member>
|
||||
</containerDefinitions>
|
||||
<taskDefinitionArn>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2</taskDefinitionArn>
|
||||
</taskDefinition>
|
||||
</DeregisterTaskDefinitionResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeregisterTaskDefinitionResponse>
|
||||
`
|
||||
|
||||
var DescribeClustersResponse = `
|
||||
<DescribeClustersResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<DescribeClustersResult>
|
||||
<failures/>
|
||||
<clusters>
|
||||
<member>
|
||||
<clusterName>test</clusterName>
|
||||
<clusterArn>arn:aws:ecs:us-east-1:aws_account_id:cluster/test</clusterArn>
|
||||
<status>ACTIVE</status>
|
||||
</member>
|
||||
<member>
|
||||
<clusterName>default</clusterName>
|
||||
<clusterArn>arn:aws:ecs:us-east-1:aws_account_id:cluster/default</clusterArn>
|
||||
<status>ACTIVE</status>
|
||||
</member>
|
||||
</clusters>
|
||||
</DescribeClustersResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeClustersResponse>
|
||||
`
|
||||
|
||||
var DescribeContainerInstancesResponse = `
|
||||
<DescribeContainerInstancesResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
<DescribeContainerInstancesResult>
|
||||
<failures/>
|
||||
<containerInstances>
|
||||
<member>
|
||||
<agentConnected>true</agentConnected>
|
||||
<containerInstanceArn>arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID</containerInstanceArn>
|
||||
<ec2InstanceId>instance_id</ec2InstanceId>
|
||||
<status>ACTIVE</status>
|
||||
<registeredResources>
|
||||
<member>
|
||||
<integerValue>2048</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>CPU</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>3955</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>MEMORY</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>0</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>STRINGSET</type>
|
||||
<stringSetValue>
|
||||
<member>2376</member>
|
||||
<member>22</member>
|
||||
<member>51678</member>
|
||||
<member>2375</member>
|
||||
</stringSetValue>
|
||||
<name>PORTS</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
</registeredResources>
|
||||
<remainingResources>
|
||||
<member>
|
||||
<integerValue>2048</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>CPU</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>3955</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>MEMORY</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>0</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>STRINGSET</type>
|
||||
<stringSetValue>
|
||||
<member>2376</member>
|
||||
<member>22</member>
|
||||
<member>51678</member>
|
||||
<member>2375</member>
|
||||
</stringSetValue>
|
||||
<name>PORTS</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
</remainingResources>
|
||||
</member>
|
||||
</containerInstances>
|
||||
</DescribeContainerInstancesResult>
|
||||
</DescribeContainerInstancesResponse>
|
||||
`
|
||||
|
||||
var DescribeTaskDefinitionResponse = `
|
||||
<DescribeTaskDefinitionResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<DescribeTaskDefinitionResult>
|
||||
<taskDefinition>
|
||||
<revision>2</revision>
|
||||
<family>sleep360</family>
|
||||
<containerDefinitions>
|
||||
<member>
|
||||
<portMappings/>
|
||||
<essential>true</essential>
|
||||
<environment>
|
||||
<member>
|
||||
<name>envVar</name>
|
||||
<value>foo</value>
|
||||
</member>
|
||||
</environment>
|
||||
<entryPoint>
|
||||
<member>/bin/sh</member>
|
||||
</entryPoint>
|
||||
<name>sleep</name>
|
||||
<command>
|
||||
<member>sleep</member>
|
||||
<member>360</member>
|
||||
</command>
|
||||
<cpu>10</cpu>
|
||||
<image>busybox</image>
|
||||
<memory>10</memory>
|
||||
</member>
|
||||
</containerDefinitions>
|
||||
<taskDefinitionArn>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2</taskDefinitionArn>
|
||||
</taskDefinition>
|
||||
</DescribeTaskDefinitionResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeTaskDefinitionResponse>
|
||||
`
|
||||
|
||||
var DescribeTasksResponse = `
|
||||
<DescribeTasksResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<DescribeTasksResult>
|
||||
<failures/>
|
||||
<tasks>
|
||||
<member>
|
||||
<containers>
|
||||
<member>
|
||||
<taskArn>arn:aws:ecs:us-east-1:aws_account_id:task/UUID</taskArn>
|
||||
<name>sleep</name>
|
||||
<containerArn>arn:aws:ecs:us-east-1:aws_account_id:container/UUID</containerArn>
|
||||
<networkBindings/>
|
||||
<lastStatus>RUNNING</lastStatus>
|
||||
</member>
|
||||
</containers>
|
||||
<overrides>
|
||||
<containerOverrides>
|
||||
<member>
|
||||
<name>sleep</name>
|
||||
</member>
|
||||
</containerOverrides>
|
||||
</overrides>
|
||||
<desiredStatus>RUNNING</desiredStatus>
|
||||
<taskArn>arn:aws:ecs:us-east-1:aws_account_id:task/UUID</taskArn>
|
||||
<containerInstanceArn>arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID</containerInstanceArn>
|
||||
<lastStatus>RUNNING</lastStatus>
|
||||
<taskDefinitionArn>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2</taskDefinitionArn>
|
||||
</member>
|
||||
</tasks>
|
||||
</DescribeTasksResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeTasksResponse>
|
||||
`
|
||||
|
||||
var DiscoverPollEndpointResponse = `
|
||||
<DiscoverPollEndpointResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<DiscoverPollEndpointResult>
|
||||
<endpoint>https://ecs-x-1.us-east-1.amazonaws.com/</endpoint>
|
||||
</DiscoverPollEndpointResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DiscoverPollEndpointResponse>
|
||||
`
|
||||
|
||||
var ListClustersResponse = `
|
||||
<ListClustersResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<ListClustersResult>
|
||||
<clusterArns>
|
||||
<member>arn:aws:ecs:us-east-1:aws_account_id:cluster/default</member>
|
||||
<member>arn:aws:ecs:us-east-1:aws_account_id:cluster/test</member>
|
||||
</clusterArns>
|
||||
<nextToken>token_UUID</nextToken>
|
||||
</ListClustersResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListClustersResponse>
|
||||
`
|
||||
|
||||
var ListContainerInstancesResponse = `
|
||||
<ListContainerInstancesResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<ListContainerInstancesResult>
|
||||
<containerInstanceArns>
|
||||
<member>arn:aws:ecs:us-east-1:aws_account_id:container-instance/uuid-1</member>
|
||||
<member>arn:aws:ecs:us-east-1:aws_account_id:container-instance/uuid-2</member>
|
||||
</containerInstanceArns>
|
||||
<nextToken>token_UUID</nextToken>
|
||||
</ListContainerInstancesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListContainerInstancesResponse>
|
||||
`
|
||||
|
||||
var ListTaskDefinitionsResponse = `
|
||||
<ListTaskDefinitionsResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<ListTaskDefinitionsResult>
|
||||
<taskDefinitionArns>
|
||||
<member>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:1</member>
|
||||
<member>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2</member>
|
||||
</taskDefinitionArns>
|
||||
<nextToken>token_UUID</nextToken>
|
||||
</ListTaskDefinitionsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListTaskDefinitionsResponse>
|
||||
`
|
||||
|
||||
var ListTasksResponse = `
|
||||
<ListTasksResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<ListTasksResult>
|
||||
<taskArns>
|
||||
<member>arn:aws:ecs:us-east-1:aws_account_id:task/uuid_1</member>
|
||||
<member>arn:aws:ecs:us-east-1:aws_account_id:task/uuid_2</member>
|
||||
</taskArns>
|
||||
<nextToken>token_UUID</nextToken>
|
||||
</ListTasksResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListTasksResponse>
|
||||
`
|
||||
|
||||
var RegisterContainerInstanceResponse = `
|
||||
<RegisterContainerInstanceResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
<RegisterContainerInstanceResult>
|
||||
<containerInstance>
|
||||
<agentConnected>True</agentConnected>
|
||||
<containerInstanceArn>arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID</containerInstanceArn>
|
||||
<ec2InstanceId>instance_id</ec2InstanceId>
|
||||
<status>ACTIVE</status>
|
||||
<registeredResources>
|
||||
<member>
|
||||
<integerValue>2048</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>CPU</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>3955</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>MEMORY</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>0</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>STRINGSET</type>
|
||||
<stringSetValue>
|
||||
<member>2376</member>
|
||||
<member>22</member>
|
||||
<member>51678</member>
|
||||
<member>2375</member>
|
||||
</stringSetValue>
|
||||
<name>PORTS</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
</registeredResources>
|
||||
<remainingResources>
|
||||
<member>
|
||||
<integerValue>2048</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>CPU</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>3955</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>INTEGER</type>
|
||||
<name>MEMORY</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
<member>
|
||||
<integerValue>0</integerValue>
|
||||
<longValue>0</longValue>
|
||||
<type>STRINGSET</type>
|
||||
<stringSetValue>
|
||||
<member>2376</member>
|
||||
<member>22</member>
|
||||
<member>51678</member>
|
||||
<member>2375</member>
|
||||
</stringSetValue>
|
||||
<name>PORTS</name>
|
||||
<doubleValue>0.0</doubleValue>
|
||||
</member>
|
||||
</remainingResources>
|
||||
</containerInstance>
|
||||
</RegisterContainerInstanceResult>
|
||||
</RegisterContainerInstanceResponse>
|
||||
`
|
||||
|
||||
var RegisterTaskDefinitionResponse = `
|
||||
<RegisterTaskDefinitionResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<RegisterTaskDefinitionResult>
|
||||
<taskDefinition>
|
||||
<revision>2</revision>
|
||||
<family>sleep360</family>
|
||||
<containerDefinitions>
|
||||
<member>
|
||||
<portMappings/>
|
||||
<essential>true</essential>
|
||||
<environment>
|
||||
<member>
|
||||
<name>envVar</name>
|
||||
<value>foo</value>
|
||||
</member>
|
||||
</environment>
|
||||
<entryPoint>
|
||||
<member>/bin/sh</member>
|
||||
</entryPoint>
|
||||
<name>sleep</name>
|
||||
<command>
|
||||
<member>sleep</member>
|
||||
<member>360</member>
|
||||
</command>
|
||||
<cpu>10</cpu>
|
||||
<image>busybox</image>
|
||||
<memory>10</memory>
|
||||
<mountPoints>
|
||||
<member>
|
||||
<containerPath>/tmp/myfile</containerPath>
|
||||
<readOnly>false</readOnly>
|
||||
<sourceVolume>/srv/myfile</sourceVolume>
|
||||
</member>
|
||||
<member>
|
||||
<containerPath>/tmp/myfile2</containerPath>
|
||||
<readOnly>true</readOnly>
|
||||
<sourceVolume>/srv/myfile2</sourceVolume>
|
||||
</member>
|
||||
</mountPoints>
|
||||
<volumesFrom>
|
||||
<member>
|
||||
<readOnly>true</readOnly>
|
||||
<sourceContainer>foo</sourceContainer>
|
||||
</member>
|
||||
</volumesFrom>
|
||||
</member>
|
||||
</containerDefinitions>
|
||||
<taskDefinitionArn>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2</taskDefinitionArn>
|
||||
<volumes>
|
||||
<member>
|
||||
<name>/srv/myfile</name>
|
||||
<host>
|
||||
<sourcePath>/srv/myfile</sourcePath>
|
||||
</host>
|
||||
</member>
|
||||
</volumes>
|
||||
</taskDefinition>
|
||||
</RegisterTaskDefinitionResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</RegisterTaskDefinitionResponse>
|
||||
`
|
||||
|
||||
var RunTaskResponse = `
|
||||
<RunTaskResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<RunTaskResult>
|
||||
<failures/>
|
||||
<tasks>
|
||||
<member>
|
||||
<containers>
|
||||
<member>
|
||||
<taskArn>arn:aws:ecs:us-east-1:aws_account_id:task/UUID</taskArn>
|
||||
<name>sleep</name>
|
||||
<containerArn>arn:aws:ecs:us-east-1:aws_account_id:container/UUID</containerArn>
|
||||
<networkBindings/>
|
||||
<lastStatus>RUNNING</lastStatus>
|
||||
</member>
|
||||
</containers>
|
||||
<overrides>
|
||||
<containerOverrides>
|
||||
<member>
|
||||
<name>sleep</name>
|
||||
</member>
|
||||
</containerOverrides>
|
||||
</overrides>
|
||||
<desiredStatus>RUNNING</desiredStatus>
|
||||
<taskArn>arn:aws:ecs:us-east-1:aws_account_id:task/UUID</taskArn>
|
||||
<containerInstanceArn>arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID</containerInstanceArn>
|
||||
<lastStatus>PENDING</lastStatus>
|
||||
<taskDefinitionArn>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2</taskDefinitionArn>
|
||||
</member>
|
||||
</tasks>
|
||||
</RunTaskResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</RunTaskResponse>
|
||||
`
|
||||
|
||||
var StartTaskResponse = `
|
||||
<StartTaskResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<StartTaskResult>
|
||||
<failures/>
|
||||
<tasks>
|
||||
<member>
|
||||
<containers>
|
||||
<member>
|
||||
<taskArn>arn:aws:ecs:us-east-1:aws_account_id:task/UUID</taskArn>
|
||||
<name>sleep</name>
|
||||
<containerArn>arn:aws:ecs:us-east-1:aws_account_id:container/UUID</containerArn>
|
||||
<networkBindings/>
|
||||
<lastStatus>RUNNING</lastStatus>
|
||||
</member>
|
||||
</containers>
|
||||
<overrides>
|
||||
<containerOverrides>
|
||||
<member>
|
||||
<name>sleep</name>
|
||||
</member>
|
||||
</containerOverrides>
|
||||
</overrides>
|
||||
<desiredStatus>RUNNING</desiredStatus>
|
||||
<taskArn>arn:aws:ecs:us-east-1:aws_account_id:task/UUID</taskArn>
|
||||
<containerInstanceArn>arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID</containerInstanceArn>
|
||||
<lastStatus>PENDING</lastStatus>
|
||||
<taskDefinitionArn>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2</taskDefinitionArn>
|
||||
</member>
|
||||
</tasks>
|
||||
</StartTaskResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</StartTaskResponse>
|
||||
`
|
||||
|
||||
var StopTaskResponse = `
|
||||
<StopTaskResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<StopTaskResult>
|
||||
<task>
|
||||
<containers>
|
||||
<member>
|
||||
<taskArn>arn:aws:ecs:us-east-1:aws_account_id:task/UUID</taskArn>
|
||||
<name>sleep</name>
|
||||
<containerArn>arn:aws:ecs:us-east-1:aws_account_id:container/UUID</containerArn>
|
||||
<networkBindings/>
|
||||
<lastStatus>RUNNING</lastStatus>
|
||||
</member>
|
||||
</containers>
|
||||
<overrides>
|
||||
<containerOverrides>
|
||||
<member>
|
||||
<name>sleep</name>
|
||||
</member>
|
||||
</containerOverrides>
|
||||
</overrides>
|
||||
<desiredStatus>STOPPED</desiredStatus>
|
||||
<taskArn>arn:aws:ecs:us-east-1:aws_account_id:task/UUID</taskArn>
|
||||
<containerInstanceArn>arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID</containerInstanceArn>
|
||||
<lastStatus>RUNNING</lastStatus>
|
||||
<taskDefinitionArn>arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2</taskDefinitionArn>
|
||||
</task>
|
||||
</StopTaskResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</StopTaskResponse>
|
||||
`
|
||||
|
||||
var SubmitContainerStateChangeResponse = `
|
||||
<SubmitContainerStateChangeResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<SubmitContainerStateChangeResult>
|
||||
<acknowledgment>ACK</acknowledgment>
|
||||
</SubmitContainerStateChangeResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</SubmitContainerStateChangeResponse>
|
||||
`
|
||||
|
||||
var SubmitTaskStateChangeResponse = `
|
||||
<SubmitTaskStateChangeResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
|
||||
<SubmitTaskStateChangeResult>
|
||||
<acknowledgment>ACK</acknowledgment>
|
||||
</SubmitTaskStateChangeResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d798a29-f083-11e1-bdfb-cb223EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</SubmitTaskStateChangeResponse>
|
||||
`
|
||||
435
vendor/github.com/goamz/goamz/elb/elb.go
сгенерированный
поставляемый
435
vendor/github.com/goamz/goamz/elb/elb.go
сгенерированный
поставляемый
@@ -1,435 +0,0 @@
|
||||
// This package provides types and functions to interact Elastic Load Balancing service
|
||||
package elb
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
)
|
||||
|
||||
type ELB struct {
|
||||
aws.Auth
|
||||
aws.Region
|
||||
}
|
||||
|
||||
func New(auth aws.Auth, region aws.Region) *ELB {
|
||||
return &ELB{auth, region}
|
||||
}
|
||||
|
||||
// The CreateLoadBalancer type encapsulates options for the respective request in AWS.
|
||||
// The creation of a Load Balancer may differ inside EC2 and VPC.
|
||||
//
|
||||
// See http://goo.gl/4QFKi for more details.
|
||||
type CreateLoadBalancer struct {
|
||||
Name string
|
||||
AvailabilityZones []string
|
||||
Listeners []Listener
|
||||
Scheme string
|
||||
SecurityGroups []string
|
||||
Subnets []string
|
||||
}
|
||||
|
||||
// Listener to configure in Load Balancer.
|
||||
//
|
||||
// See http://goo.gl/NJQCj for more details.
|
||||
type Listener struct {
|
||||
InstancePort int
|
||||
InstanceProtocol string
|
||||
LoadBalancerPort int
|
||||
Protocol string
|
||||
SSLCertificateId string
|
||||
}
|
||||
|
||||
// Response to a CreateLoadBalance request.
|
||||
//
|
||||
// See http://goo.gl/4QFKi for more details.
|
||||
type CreateLoadBalancerResp struct {
|
||||
DNSName string `xml:"CreateLoadBalancerResult>DNSName"`
|
||||
}
|
||||
|
||||
type SimpleResp struct {
|
||||
RequestId string `xml:"ResponseMetadata>RequestId"`
|
||||
}
|
||||
|
||||
// Creates a Load Balancer in Amazon.
|
||||
//
|
||||
// See http://goo.gl/4QFKi for more details.
|
||||
func (elb *ELB) CreateLoadBalancer(options *CreateLoadBalancer) (resp *CreateLoadBalancerResp, err error) {
|
||||
params := makeCreateParams(options)
|
||||
resp = new(CreateLoadBalancerResp)
|
||||
if err := elb.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Deletes a Load Balancer.
|
||||
//
|
||||
// See http://goo.gl/sDmPp for more details.
|
||||
func (elb *ELB) DeleteLoadBalancer(name string) (resp *SimpleResp, err error) {
|
||||
params := map[string]string{
|
||||
"Action": "DeleteLoadBalancer",
|
||||
"LoadBalancerName": name,
|
||||
}
|
||||
resp = new(SimpleResp)
|
||||
if err := elb.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type RegisterInstancesResp struct {
|
||||
InstanceIds []string `xml:"RegisterInstancesWithLoadBalancerResult>Instances>member>InstanceId"`
|
||||
}
|
||||
|
||||
// Register N instances with a given Load Balancer.
|
||||
//
|
||||
// See http://goo.gl/x9hru for more details.
|
||||
func (elb *ELB) RegisterInstancesWithLoadBalancer(instanceIds []string, lbName string) (resp *RegisterInstancesResp, err error) {
|
||||
// TODO: change params order and use ..., e.g (lbName string, instanceIds ...string)
|
||||
params := map[string]string{
|
||||
"Action": "RegisterInstancesWithLoadBalancer",
|
||||
"LoadBalancerName": lbName,
|
||||
}
|
||||
for i, instanceId := range instanceIds {
|
||||
key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
|
||||
params[key] = instanceId
|
||||
}
|
||||
resp = new(RegisterInstancesResp)
|
||||
if err := elb.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Deregister N instances from a given Load Balancer.
|
||||
//
|
||||
// See http://goo.gl/Hgo4U for more details.
|
||||
func (elb *ELB) DeregisterInstancesFromLoadBalancer(instanceIds []string, lbName string) (resp *SimpleResp, err error) {
|
||||
// TODO: change params order and use ..., e.g (lbName string, instanceIds ...string)
|
||||
params := map[string]string{
|
||||
"Action": "DeregisterInstancesFromLoadBalancer",
|
||||
"LoadBalancerName": lbName,
|
||||
}
|
||||
for i, instanceId := range instanceIds {
|
||||
key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
|
||||
params[key] = instanceId
|
||||
}
|
||||
resp = new(SimpleResp)
|
||||
if err := elb.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type DescribeLoadBalancerResp struct {
|
||||
LoadBalancerDescriptions []LoadBalancerDescription `xml:"DescribeLoadBalancersResult>LoadBalancerDescriptions>member"`
|
||||
}
|
||||
|
||||
type LoadBalancerDescription struct {
|
||||
AvailabilityZones []string `xml:"AvailabilityZones>member"`
|
||||
BackendServerDescriptions []BackendServerDescriptions `xml:"BackendServerDescriptions>member"`
|
||||
CanonicalHostedZoneName string `xml:"CanonicalHostedZoneName"`
|
||||
CanonicalHostedZoneNameId string `xml:"CanonicalHostedZoneNameID"`
|
||||
CreatedTime time.Time `xml:"CreatedTime"`
|
||||
DNSName string `xml:"DNSName"`
|
||||
HealthCheck HealthCheck `xml:"HealthCheck"`
|
||||
Instances []Instance `xml:"Instances>member"`
|
||||
ListenerDescriptions []ListenerDescription `xml:"ListenerDescriptions>member"`
|
||||
LoadBalancerName string `xml:"LoadBalancerName"`
|
||||
Policies Policies `xml:"Policies"`
|
||||
Scheme string `xml:"Scheme"`
|
||||
SecurityGroups []string `xml:"SecurityGroups>member"` //vpc only
|
||||
SourceSecurityGroup SourceSecurityGroup `xml:"SourceSecurityGroup"`
|
||||
Subnets []string `xml:"Subnets>member"`
|
||||
VPCId string `xml:"VPCId"`
|
||||
}
|
||||
|
||||
// Describe Load Balancers.
|
||||
// It can be used to describe all Load Balancers or specific ones.
|
||||
//
|
||||
// See http://goo.gl/wofJA for more details.
|
||||
func (elb *ELB) DescribeLoadBalancers(names ...string) (*DescribeLoadBalancerResp, error) {
|
||||
params := map[string]string{"Action": "DescribeLoadBalancers"}
|
||||
for i, name := range names {
|
||||
index := fmt.Sprintf("LoadBalancerNames.member.%d", i+1)
|
||||
params[index] = name
|
||||
}
|
||||
resp := new(DescribeLoadBalancerResp)
|
||||
if err := elb.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type BackendServerDescriptions struct {
|
||||
InstancePort int `xml:"InstancePort"`
|
||||
PolicyNames []string `xml:"PolicyNames>member"`
|
||||
}
|
||||
|
||||
type HealthCheck struct {
|
||||
HealthyThreshold int `xml:"HealthyThreshold"`
|
||||
Interval int `xml:"Interval"`
|
||||
Target string `xml:"Target"`
|
||||
Timeout int `xml:"Timeout"`
|
||||
UnhealthyThreshold int `xml:"UnhealthyThreshold"`
|
||||
}
|
||||
|
||||
type Instance struct {
|
||||
InstanceId string `xml:"InstanceId"`
|
||||
}
|
||||
|
||||
type ListenerDescription struct {
|
||||
Listener Listener `xml:"Listener"`
|
||||
PolicyNames []string `xml:"PolicyNames>member"`
|
||||
}
|
||||
|
||||
type Policies struct {
|
||||
AppCookieStickinessPolicies []AppCookieStickinessPolicies `xml:"AppCookieStickinessPolicies>member"`
|
||||
LBCookieStickinessPolicies []LBCookieStickinessPolicies `xml:"LBCookieStickinessPolicies>member"`
|
||||
OtherPolicies []string `xml:"OtherPolicies>member"`
|
||||
}
|
||||
|
||||
// see http://goo.gl/clXGV for more information.
|
||||
type AppCookieStickinessPolicies struct {
|
||||
CookieName string `xml:"CookieName"`
|
||||
PolicyName string `xml:"PolicyName"`
|
||||
}
|
||||
|
||||
type LBCookieStickinessPolicies struct {
|
||||
CookieExpirationPeriod int `xml:"CookieExpirationPeriod"`
|
||||
PolicyName string `xml:"PolicyName"`
|
||||
}
|
||||
|
||||
type SourceSecurityGroup struct {
|
||||
GroupName string `xml:"GroupName"`
|
||||
OwnerAlias string `xml:"OwnerAlias"`
|
||||
}
|
||||
|
||||
// Represents a XML response for DescribeInstanceHealth action
|
||||
//
|
||||
// See http://goo.gl/ovIB1 for more information.
|
||||
type DescribeInstanceHealthResp struct {
|
||||
InstanceStates []InstanceState `xml:"DescribeInstanceHealthResult>InstanceStates>member"`
|
||||
}
|
||||
|
||||
// See http://goo.gl/dzWfP for more information.
|
||||
type InstanceState struct {
|
||||
Description string `xml:"Description"`
|
||||
InstanceId string `xml:"InstanceId"`
|
||||
ReasonCode string `xml:"ReasonCode"`
|
||||
State string `xml:"State"`
|
||||
}
|
||||
|
||||
// Describe instance health.
|
||||
//
|
||||
// See http://goo.gl/ovIB1 for more information.
|
||||
func (elb *ELB) DescribeInstanceHealth(lbName string, instanceIds ...string) (*DescribeInstanceHealthResp, error) {
|
||||
params := map[string]string{
|
||||
"Action": "DescribeInstanceHealth",
|
||||
"LoadBalancerName": lbName,
|
||||
}
|
||||
for i, iId := range instanceIds {
|
||||
key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1)
|
||||
params[key] = iId
|
||||
}
|
||||
resp := new(DescribeInstanceHealthResp)
|
||||
if err := elb.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type HealthCheckResp struct {
|
||||
HealthCheck *HealthCheck `xml:"ConfigureHealthCheckResult>HealthCheck"`
|
||||
}
|
||||
|
||||
// Configure health check for a LB
|
||||
//
|
||||
// See http://goo.gl/2HE6a for more information
|
||||
func (elb *ELB) ConfigureHealthCheck(lbName string, healthCheck *HealthCheck) (*HealthCheckResp, error) {
|
||||
params := map[string]string{
|
||||
"Action": "ConfigureHealthCheck",
|
||||
"LoadBalancerName": lbName,
|
||||
"HealthCheck.HealthyThreshold": strconv.Itoa(healthCheck.HealthyThreshold),
|
||||
"HealthCheck.Interval": strconv.Itoa(healthCheck.Interval),
|
||||
"HealthCheck.Target": healthCheck.Target,
|
||||
"HealthCheck.Timeout": strconv.Itoa(healthCheck.Timeout),
|
||||
"HealthCheck.UnhealthyThreshold": strconv.Itoa(healthCheck.UnhealthyThreshold),
|
||||
}
|
||||
resp := new(HealthCheckResp)
|
||||
if err := elb.query(params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Add tags to the named ELB
|
||||
//
|
||||
// Note that AWS only accepts one ELB name at a time (even though it is sent as a list)
|
||||
//
|
||||
// See http://goo.gl/6JW4Wf for the rest of the details
|
||||
func (elb *ELB) AddTags(elbName string, tags map[string]string) (*SimpleResp, error) {
|
||||
var sortedKeys []string
|
||||
params := make(map[string]string)
|
||||
response := &SimpleResp{}
|
||||
|
||||
for tagKey := range tags {
|
||||
sortedKeys = append(sortedKeys, tagKey)
|
||||
}
|
||||
|
||||
sort.Strings(sortedKeys)
|
||||
|
||||
for _, key := range sortedKeys {
|
||||
number := len(tags)
|
||||
params[fmt.Sprintf("Tags.member.%d.Key", number)] = key
|
||||
params[fmt.Sprintf("Tags.member.%d.Value", number)] = tags[key]
|
||||
delete(tags, key)
|
||||
}
|
||||
|
||||
params["Action"] = "AddTags"
|
||||
params["LoadBalancerNames.member.1"] = elbName
|
||||
|
||||
if err := elb.query(params, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Remove tags from the named ELB
|
||||
//
|
||||
// Note that AWS only accepts one ELB name at a time (even though it is sent as a list)
|
||||
//
|
||||
// see http://goo.gl/ochFqo for more details
|
||||
|
||||
func (elb *ELB) RemoveTags(elbName string, tagKeys []string) (*SimpleResp, error) {
|
||||
response := &SimpleResp{}
|
||||
params := make(map[string]string)
|
||||
|
||||
params["Action"] = "RemoveTags"
|
||||
params["LoadBalancerNames.member.1"] = elbName
|
||||
|
||||
for i, tagKey := range tagKeys {
|
||||
params[fmt.Sprintf("Tags.member.%d.Key", i+1)] = tagKey
|
||||
}
|
||||
|
||||
if err := elb.query(params, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (elb *ELB) query(params map[string]string, resp interface{}) error {
|
||||
params["Version"] = "2012-06-01"
|
||||
params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339)
|
||||
data := strings.NewReader(multimap(params).Encode())
|
||||
hreq, err := http.NewRequest("GET", elb.Region.ELBEndpoint+"/", data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hreq.URL.RawQuery = multimap(params).Encode()
|
||||
token := elb.Auth.Token()
|
||||
if token != "" {
|
||||
hreq.Header.Set("X-Amz-Security-Token", token)
|
||||
}
|
||||
|
||||
signer := aws.NewV4Signer(elb.Auth, "elasticloadbalancing", elb.Region)
|
||||
signer.Sign(hreq)
|
||||
|
||||
r, err := http.DefaultClient.Do(hreq)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if r.StatusCode != 200 {
|
||||
return buildError(r)
|
||||
}
|
||||
return xml.NewDecoder(r.Body).Decode(resp)
|
||||
}
|
||||
|
||||
// Error encapsulates an error returned by ELB.
|
||||
type Error struct {
|
||||
// HTTP status code
|
||||
StatusCode int
|
||||
// AWS error code
|
||||
Code string
|
||||
// The human-oriented error message
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
if err.Code == "" {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s (%s)", err.Message, err.Code)
|
||||
}
|
||||
|
||||
type xmlErrors struct {
|
||||
Errors []Error `xml:"Error"`
|
||||
}
|
||||
|
||||
func buildError(r *http.Response) error {
|
||||
var (
|
||||
err Error
|
||||
errors xmlErrors
|
||||
)
|
||||
xml.NewDecoder(r.Body).Decode(&errors)
|
||||
if len(errors.Errors) > 0 {
|
||||
err = errors.Errors[0]
|
||||
}
|
||||
err.StatusCode = r.StatusCode
|
||||
if err.Message == "" {
|
||||
err.Message = r.Status
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
func multimap(p map[string]string) url.Values {
|
||||
q := make(url.Values, len(p))
|
||||
for k, v := range p {
|
||||
q[k] = []string{v}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func makeCreateParams(createLB *CreateLoadBalancer) map[string]string {
|
||||
params := make(map[string]string)
|
||||
params["LoadBalancerName"] = createLB.Name
|
||||
params["Action"] = "CreateLoadBalancer"
|
||||
if createLB.Scheme != "" {
|
||||
params["Scheme"] = createLB.Scheme
|
||||
}
|
||||
for i, s := range createLB.SecurityGroups {
|
||||
key := fmt.Sprintf("SecurityGroups.member.%d", i+1)
|
||||
params[key] = s
|
||||
}
|
||||
for i, s := range createLB.Subnets {
|
||||
key := fmt.Sprintf("Subnets.member.%d", i+1)
|
||||
params[key] = s
|
||||
}
|
||||
for i, l := range createLB.Listeners {
|
||||
key := "Listeners.member.%d.%s"
|
||||
index := i + 1
|
||||
params[fmt.Sprintf(key, index, "InstancePort")] = strconv.Itoa(l.InstancePort)
|
||||
params[fmt.Sprintf(key, index, "InstanceProtocol")] = l.InstanceProtocol
|
||||
params[fmt.Sprintf(key, index, "Protocol")] = l.Protocol
|
||||
params[fmt.Sprintf(key, index, "LoadBalancerPort")] = strconv.Itoa(l.LoadBalancerPort)
|
||||
}
|
||||
for i, az := range createLB.AvailabilityZones {
|
||||
key := fmt.Sprintf("AvailabilityZones.member.%d", i+1)
|
||||
params[key] = az
|
||||
}
|
||||
return params
|
||||
}
|
||||
369
vendor/github.com/goamz/goamz/elb/elb_test.go
сгенерированный
поставляемый
369
vendor/github.com/goamz/goamz/elb/elb_test.go
сгенерированный
поставляемый
@@ -1,369 +0,0 @@
|
||||
package elb_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/elb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
HTTPSuite
|
||||
elb *elb.ELB
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
s.HTTPSuite.SetUpSuite(c)
|
||||
auth := aws.Auth{AccessKey: "abc", SecretKey: "123"}
|
||||
s.elb = elb.New(auth, aws.Region{ELBEndpoint: testServer.URL})
|
||||
}
|
||||
|
||||
func (s *S) TestCreateLoadBalancer(c *C) {
|
||||
testServer.PrepareResponse(200, nil, CreateLoadBalancer)
|
||||
createLB := &elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1a", "us-east-1b"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
Protocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
},
|
||||
},
|
||||
}
|
||||
resp, err := s.elb.CreateLoadBalancer(createLB)
|
||||
c.Assert(err, IsNil)
|
||||
defer s.elb.DeleteLoadBalancer(createLB.Name)
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Action"), Equals, "CreateLoadBalancer")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("LoadBalancerName"), Equals, "testlb")
|
||||
c.Assert(values.Get("AvailabilityZones.member.1"), Equals, "us-east-1a")
|
||||
c.Assert(values.Get("AvailabilityZones.member.2"), Equals, "us-east-1b")
|
||||
c.Assert(values.Get("Listeners.member.1.InstancePort"), Equals, "80")
|
||||
c.Assert(values.Get("Listeners.member.1.InstanceProtocol"), Equals, "http")
|
||||
c.Assert(values.Get("Listeners.member.1.Protocol"), Equals, "http")
|
||||
c.Assert(values.Get("Listeners.member.1.LoadBalancerPort"), Equals, "80")
|
||||
c.Assert(resp.DNSName, Equals, "testlb-339187009.us-east-1.elb.amazonaws.com")
|
||||
}
|
||||
|
||||
func (s *S) TestCreateLoadBalancerWithSubnetsAndMoreListeners(c *C) {
|
||||
testServer.PrepareResponse(200, nil, CreateLoadBalancer)
|
||||
createLB := &elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
Protocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
},
|
||||
{
|
||||
InstancePort: 8080,
|
||||
InstanceProtocol: "http",
|
||||
Protocol: "http",
|
||||
LoadBalancerPort: 8080,
|
||||
},
|
||||
},
|
||||
Subnets: []string{"subnetid-1", "subnetid-2"},
|
||||
SecurityGroups: []string{"sg-1", "sg-2"},
|
||||
}
|
||||
_, err := s.elb.CreateLoadBalancer(createLB)
|
||||
c.Assert(err, IsNil)
|
||||
defer s.elb.DeleteLoadBalancer(createLB.Name)
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Listeners.member.1.InstancePort"), Equals, "80")
|
||||
c.Assert(values.Get("Listeners.member.1.LoadBalancerPort"), Equals, "80")
|
||||
c.Assert(values.Get("Listeners.member.2.InstancePort"), Equals, "8080")
|
||||
c.Assert(values.Get("Listeners.member.2.LoadBalancerPort"), Equals, "8080")
|
||||
c.Assert(values.Get("Subnets.member.1"), Equals, "subnetid-1")
|
||||
c.Assert(values.Get("Subnets.member.2"), Equals, "subnetid-2")
|
||||
c.Assert(values.Get("SecurityGroups.member.1"), Equals, "sg-1")
|
||||
c.Assert(values.Get("SecurityGroups.member.2"), Equals, "sg-2")
|
||||
}
|
||||
|
||||
func (s *S) TestCreateLoadBalancerWithWrongParamsCombination(c *C) {
|
||||
testServer.PrepareResponse(400, nil, CreateLoadBalancerBadRequest)
|
||||
createLB := &elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1a", "us-east-1b"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
Protocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
},
|
||||
},
|
||||
Subnets: []string{"subnetid-1", "subnetid2"},
|
||||
}
|
||||
resp, err := s.elb.CreateLoadBalancer(createLB)
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
e, ok := err.(*elb.Error)
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(e.Message, Equals, "Only one of SubnetIds or AvailabilityZones may be specified")
|
||||
c.Assert(e.Code, Equals, "ValidationError")
|
||||
}
|
||||
|
||||
func (s *S) TestDeleteLoadBalancer(c *C) {
|
||||
testServer.PrepareResponse(200, nil, DeleteLoadBalancer)
|
||||
resp, err := s.elb.DeleteLoadBalancer("testlb")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("Action"), Equals, "DeleteLoadBalancer")
|
||||
c.Assert(values.Get("LoadBalancerName"), Equals, "testlb")
|
||||
c.Assert(resp.RequestId, Equals, "8d7223db-49d7-11e2-bba9-35ba56032fe1")
|
||||
}
|
||||
|
||||
func (s *S) TestRegisterInstancesWithLoadBalancer(c *C) {
|
||||
testServer.PrepareResponse(200, nil, RegisterInstancesWithLoadBalancer)
|
||||
resp, err := s.elb.RegisterInstancesWithLoadBalancer([]string{"i-b44db8ca", "i-461ecf38"}, "testlb")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("Action"), Equals, "RegisterInstancesWithLoadBalancer")
|
||||
c.Assert(values.Get("LoadBalancerName"), Equals, "testlb")
|
||||
c.Assert(values.Get("Instances.member.1.InstanceId"), Equals, "i-b44db8ca")
|
||||
c.Assert(values.Get("Instances.member.2.InstanceId"), Equals, "i-461ecf38")
|
||||
c.Assert(resp.InstanceIds, DeepEquals, []string{"i-b44db8ca", "i-461ecf38"})
|
||||
}
|
||||
|
||||
func (s *S) TestRegisterInstancesWithLoadBalancerBadRequest(c *C) {
|
||||
testServer.PrepareResponse(400, nil, RegisterInstancesWithLoadBalancerBadRequest)
|
||||
resp, err := s.elb.RegisterInstancesWithLoadBalancer([]string{"i-b44db8ca", "i-461ecf38"}, "absentLB")
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
e, ok := err.(*elb.Error)
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(e.Message, Equals, "There is no ACTIVE Load Balancer named 'absentLB'")
|
||||
c.Assert(e.Code, Equals, "LoadBalancerNotFound")
|
||||
}
|
||||
|
||||
func (s *S) TestDeregisterInstancesFromLoadBalancer(c *C) {
|
||||
testServer.PrepareResponse(200, nil, DeregisterInstancesFromLoadBalancer)
|
||||
resp, err := s.elb.DeregisterInstancesFromLoadBalancer([]string{"i-b44db8ca", "i-461ecf38"}, "testlb")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("Action"), Equals, "DeregisterInstancesFromLoadBalancer")
|
||||
c.Assert(values.Get("LoadBalancerName"), Equals, "testlb")
|
||||
c.Assert(values.Get("Instances.member.1.InstanceId"), Equals, "i-b44db8ca")
|
||||
c.Assert(values.Get("Instances.member.2.InstanceId"), Equals, "i-461ecf38")
|
||||
c.Assert(resp.RequestId, Equals, "d6490837-49fd-11e2-bba9-35ba56032fe1")
|
||||
}
|
||||
|
||||
func (s *S) TestDeregisterInstancesFromLoadBalancerBadRequest(c *C) {
|
||||
testServer.PrepareResponse(400, nil, DeregisterInstancesFromLoadBalancerBadRequest)
|
||||
resp, err := s.elb.DeregisterInstancesFromLoadBalancer([]string{"i-b44db8ca", "i-461ecf38"}, "testlb")
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
e, ok := err.(*elb.Error)
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(e.Message, Equals, "There is no ACTIVE Load Balancer named 'absentlb'")
|
||||
c.Assert(e.Code, Equals, "LoadBalancerNotFound")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeLoadBalancers(c *C) {
|
||||
testServer.PrepareResponse(200, nil, DescribeLoadBalancers)
|
||||
resp, err := s.elb.DescribeLoadBalancers()
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeLoadBalancers")
|
||||
t, _ := time.Parse(time.RFC3339, "2012-12-27T11:51:52.970Z")
|
||||
expected := &elb.DescribeLoadBalancerResp{
|
||||
[]elb.LoadBalancerDescription{
|
||||
{
|
||||
AvailabilityZones: []string{"us-east-1a"},
|
||||
BackendServerDescriptions: []elb.BackendServerDescriptions(nil),
|
||||
CanonicalHostedZoneName: "testlb-2087227216.us-east-1.elb.amazonaws.com",
|
||||
CanonicalHostedZoneNameId: "Z3DZXE0Q79N41H",
|
||||
CreatedTime: t,
|
||||
DNSName: "testlb-2087227216.us-east-1.elb.amazonaws.com",
|
||||
HealthCheck: elb.HealthCheck{
|
||||
HealthyThreshold: 10,
|
||||
Interval: 30,
|
||||
Target: "TCP:80",
|
||||
Timeout: 5,
|
||||
UnhealthyThreshold: 2,
|
||||
},
|
||||
Instances: []elb.Instance(nil),
|
||||
ListenerDescriptions: []elb.ListenerDescription{
|
||||
{
|
||||
Listener: elb.Listener{
|
||||
Protocol: "HTTP",
|
||||
LoadBalancerPort: 80,
|
||||
InstanceProtocol: "HTTP",
|
||||
InstancePort: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
LoadBalancerName: "testlb",
|
||||
//Policies: elb.Policies(nil),
|
||||
Scheme: "internet-facing",
|
||||
SecurityGroups: []string(nil),
|
||||
SourceSecurityGroup: elb.SourceSecurityGroup{
|
||||
GroupName: "amazon-elb-sg",
|
||||
OwnerAlias: "amazon-elb",
|
||||
},
|
||||
Subnets: []string(nil),
|
||||
},
|
||||
},
|
||||
}
|
||||
c.Assert(resp, DeepEquals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeLoadBalancersByName(c *C) {
|
||||
testServer.PrepareResponse(200, nil, DescribeLoadBalancers)
|
||||
s.elb.DescribeLoadBalancers("somelb")
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeLoadBalancers")
|
||||
c.Assert(values.Get("LoadBalancerNames.member.1"), Equals, "somelb")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeLoadBalancersBadRequest(c *C) {
|
||||
testServer.PrepareResponse(400, nil, DescribeLoadBalancersBadRequest)
|
||||
resp, err := s.elb.DescribeLoadBalancers()
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err, ErrorMatches, `^Cannot find Load Balancer absentlb \(LoadBalancerNotFound\)$`)
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeInstanceHealth(c *C) {
|
||||
testServer.PrepareResponse(200, nil, DescribeInstanceHealth)
|
||||
resp, err := s.elb.DescribeInstanceHealth("testlb", "i-b44db8ca")
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("Action"), Equals, "DescribeInstanceHealth")
|
||||
c.Assert(values.Get("LoadBalancerName"), Equals, "testlb")
|
||||
c.Assert(values.Get("Instances.member.1.InstanceId"), Equals, "i-b44db8ca")
|
||||
c.Assert(len(resp.InstanceStates) > 0, Equals, true)
|
||||
c.Assert(resp.InstanceStates[0].Description, Equals, "Instance registration is still in progress.")
|
||||
c.Assert(resp.InstanceStates[0].InstanceId, Equals, "i-b44db8ca")
|
||||
c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService")
|
||||
c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "ELB")
|
||||
}
|
||||
|
||||
func (s *S) TestDescribeInstanceHealthBadRequest(c *C) {
|
||||
testServer.PrepareResponse(400, nil, DescribeInstanceHealthBadRequest)
|
||||
resp, err := s.elb.DescribeInstanceHealth("testlb", "i-foooo")
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, ErrorMatches, ".*i-foooo.*(InvalidInstance).*")
|
||||
}
|
||||
|
||||
func (s *S) TestConfigureHealthCheck(c *C) {
|
||||
testServer.PrepareResponse(200, nil, ConfigureHealthCheck)
|
||||
hc := elb.HealthCheck{
|
||||
HealthyThreshold: 10,
|
||||
Interval: 30,
|
||||
Target: "HTTP:80/",
|
||||
Timeout: 5,
|
||||
UnhealthyThreshold: 2,
|
||||
}
|
||||
resp, err := s.elb.ConfigureHealthCheck("testlb", &hc)
|
||||
c.Assert(err, IsNil)
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("Action"), Equals, "ConfigureHealthCheck")
|
||||
c.Assert(values.Get("LoadBalancerName"), Equals, "testlb")
|
||||
c.Assert(values.Get("HealthCheck.HealthyThreshold"), Equals, "10")
|
||||
c.Assert(values.Get("HealthCheck.Interval"), Equals, "30")
|
||||
c.Assert(values.Get("HealthCheck.Target"), Equals, "HTTP:80/")
|
||||
c.Assert(values.Get("HealthCheck.Timeout"), Equals, "5")
|
||||
c.Assert(values.Get("HealthCheck.UnhealthyThreshold"), Equals, "2")
|
||||
c.Assert(resp.HealthCheck.HealthyThreshold, Equals, 10)
|
||||
c.Assert(resp.HealthCheck.Interval, Equals, 30)
|
||||
c.Assert(resp.HealthCheck.Target, Equals, "HTTP:80/")
|
||||
c.Assert(resp.HealthCheck.Timeout, Equals, 5)
|
||||
c.Assert(resp.HealthCheck.UnhealthyThreshold, Equals, 2)
|
||||
}
|
||||
|
||||
func (s *S) TestConfigureHealthCheckBadRequest(c *C) {
|
||||
testServer.PrepareResponse(400, nil, ConfigureHealthCheckBadRequest)
|
||||
hc := elb.HealthCheck{
|
||||
HealthyThreshold: 10,
|
||||
Interval: 30,
|
||||
Target: "HTTP:80/",
|
||||
Timeout: 5,
|
||||
UnhealthyThreshold: 2,
|
||||
}
|
||||
resp, err := s.elb.ConfigureHealthCheck("foolb", &hc)
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err, ErrorMatches, ".*foolb.*(LoadBalancerNotFound).*")
|
||||
}
|
||||
|
||||
func (s *S) TestAddTags(c *C) {
|
||||
testServer.PrepareResponse(200, nil, AddTagsSuccessResponse)
|
||||
tagsToAdd := map[string]string{
|
||||
"my-key": "my-value",
|
||||
"my-super-silly-tag": "its-another-valid-value",
|
||||
}
|
||||
|
||||
resp, err := s.elb.AddTags("my-elb", tagsToAdd)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Action"), Equals, "AddTags")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("LoadBalancerNames.member.1"), Equals, "my-elb")
|
||||
c.Assert(values.Get("Tags.member.1.Key"), Equals, "my-super-silly-tag")
|
||||
c.Assert(values.Get("Tags.member.1.Value"), Equals, "its-another-valid-value")
|
||||
c.Assert(values.Get("Tags.member.2.Key"), Equals, "my-key")
|
||||
c.Assert(values.Get("Tags.member.2.Value"), Equals, "my-value")
|
||||
|
||||
c.Assert(resp.RequestId, Equals, "360e81f7-1100-11e4-b6ed-0f30SOME-SAUCY-EXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestAddBadTags(c *C) {
|
||||
testServer.PrepareResponse(400, nil, TagsBadRequest)
|
||||
tagsToAdd := map[string]string{
|
||||
"my-first-key": "an invalid value",
|
||||
}
|
||||
|
||||
resp, err := s.elb.AddTags("my-bad-elb", tagsToAdd)
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, ErrorMatches, ".*(InvalidParameterValue).*")
|
||||
}
|
||||
|
||||
func (s *S) TestRemoveTags(c *C) {
|
||||
testServer.PrepareResponse(200, nil, RemoveTagsSuccessResponse)
|
||||
tagKeysToRemove := []string{"a-key-one", "a-key-two"}
|
||||
|
||||
resp, err := s.elb.RemoveTags("my-test-elb-1", tagKeysToRemove)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
values := testServer.WaitRequest().URL.Query()
|
||||
c.Assert(values.Get("Version"), Equals, "2012-06-01")
|
||||
c.Assert(values.Get("Action"), Equals, "RemoveTags")
|
||||
c.Assert(values.Get("Timestamp"), Not(Equals), "")
|
||||
c.Assert(values.Get("LoadBalancerNames.member.1"), Equals, "my-test-elb-1")
|
||||
c.Assert([]string{values.Get("Tags.member.1.Key"), values.Get("Tags.member.2.Key")}, DeepEquals, tagKeysToRemove)
|
||||
c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12DIFFEXAMPLE")
|
||||
}
|
||||
|
||||
func (s *S) TestRemoveTagsFailure(c *C) {
|
||||
testServer.PrepareResponse(400, nil, TagsBadRequest)
|
||||
|
||||
resp, err := s.elb.RemoveTags("my-test-elb", []string{"non-existant-tag"})
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, ErrorMatches, ".*(InvalidParameterValue).*")
|
||||
}
|
||||
308
vendor/github.com/goamz/goamz/elb/elbi_test.go
сгенерированный
поставляемый
308
vendor/github.com/goamz/goamz/elb/elbi_test.go
сгенерированный
поставляемый
@@ -1,308 +0,0 @@
|
||||
package elb_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/ec2"
|
||||
"github.com/goamz/goamz/elb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
var amazon = flag.Bool("amazon", false, "Enable tests against amazon server")
|
||||
|
||||
// AmazonServer represents an Amazon AWS server.
|
||||
type AmazonServer struct {
|
||||
auth aws.Auth
|
||||
}
|
||||
|
||||
func (s *AmazonServer) SetUp(c *C) {
|
||||
auth, err := aws.EnvAuth()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
s.auth = auth
|
||||
}
|
||||
|
||||
var _ = Suite(&AmazonClientSuite{})
|
||||
|
||||
// AmazonClientSuite tests the client against a live AWS server.
|
||||
type AmazonClientSuite struct {
|
||||
srv AmazonServer
|
||||
ClientTests
|
||||
}
|
||||
|
||||
// ClientTests defines integration tests designed to test the client.
|
||||
// It is not used as a test suite in itself, but embedded within
|
||||
// another type.
|
||||
type ClientTests struct {
|
||||
elb *elb.ELB
|
||||
ec2 *ec2.EC2
|
||||
}
|
||||
|
||||
func (s *AmazonClientSuite) SetUpSuite(c *C) {
|
||||
if !*amazon {
|
||||
c.Skip("AmazonClientSuite tests not enabled")
|
||||
}
|
||||
s.srv.SetUp(c)
|
||||
s.elb = elb.New(s.srv.auth, aws.USEast)
|
||||
s.ec2 = ec2.New(s.srv.auth, aws.USEast)
|
||||
}
|
||||
|
||||
func (s *ClientTests) TestCreateAndDeleteLoadBalancer(c *C) {
|
||||
createLBReq := elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1a"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
Protocol: "http",
|
||||
},
|
||||
},
|
||||
}
|
||||
resp, err := s.elb.CreateLoadBalancer(&createLBReq)
|
||||
c.Assert(err, IsNil)
|
||||
defer s.elb.DeleteLoadBalancer(createLBReq.Name)
|
||||
c.Assert(resp.DNSName, Not(Equals), "")
|
||||
deleteResp, err := s.elb.DeleteLoadBalancer(createLBReq.Name)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(deleteResp.RequestId, Not(Equals), "")
|
||||
}
|
||||
|
||||
func (s *ClientTests) TestCreateLoadBalancerError(c *C) {
|
||||
createLBReq := elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1a"},
|
||||
Subnets: []string{"subnetid-1"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
Protocol: "http",
|
||||
},
|
||||
},
|
||||
}
|
||||
resp, err := s.elb.CreateLoadBalancer(&createLBReq)
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
e, ok := err.(*elb.Error)
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(e.Message, Matches, "Only one of .* or .* may be specified")
|
||||
c.Assert(e.Code, Equals, "ValidationError")
|
||||
}
|
||||
|
||||
func (s *ClientTests) createInstanceAndLB(c *C) (*elb.CreateLoadBalancer, string) {
|
||||
options := ec2.RunInstancesOptions{
|
||||
ImageId: "ami-ccf405a5",
|
||||
InstanceType: "t1.micro",
|
||||
AvailabilityZone: "us-east-1c",
|
||||
}
|
||||
resp1, err := s.ec2.RunInstances(&options)
|
||||
c.Assert(err, IsNil)
|
||||
instId := resp1.Instances[0].InstanceId
|
||||
createLBReq := elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1c"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
Protocol: "http",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = s.elb.CreateLoadBalancer(&createLBReq)
|
||||
c.Assert(err, IsNil)
|
||||
return &createLBReq, instId
|
||||
}
|
||||
|
||||
// Cost: 0.02 USD
|
||||
func (s *ClientTests) TestCreateRegisterAndDeregisterInstanceWithLoadBalancer(c *C) {
|
||||
createLBReq, instId := s.createInstanceAndLB(c)
|
||||
defer func() {
|
||||
_, err := s.elb.DeleteLoadBalancer(createLBReq.Name)
|
||||
c.Check(err, IsNil)
|
||||
_, err = s.ec2.TerminateInstances([]string{instId})
|
||||
c.Check(err, IsNil)
|
||||
}()
|
||||
resp, err := s.elb.RegisterInstancesWithLoadBalancer([]string{instId}, createLBReq.Name)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.InstanceIds, DeepEquals, []string{instId})
|
||||
resp2, err := s.elb.DeregisterInstancesFromLoadBalancer([]string{instId}, createLBReq.Name)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp2, Not(Equals), "")
|
||||
}
|
||||
|
||||
func (s *ClientTests) TestDescribeLoadBalancers(c *C) {
|
||||
createLBReq := elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1a"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
Protocol: "http",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := s.elb.CreateLoadBalancer(&createLBReq)
|
||||
c.Assert(err, IsNil)
|
||||
defer func() {
|
||||
_, err := s.elb.DeleteLoadBalancer(createLBReq.Name)
|
||||
c.Check(err, IsNil)
|
||||
}()
|
||||
resp, err := s.elb.DescribeLoadBalancers()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(resp.LoadBalancerDescriptions) > 0, Equals, true)
|
||||
c.Assert(resp.LoadBalancerDescriptions[0].AvailabilityZones, DeepEquals, []string{"us-east-1a"})
|
||||
c.Assert(resp.LoadBalancerDescriptions[0].LoadBalancerName, Equals, "testlb")
|
||||
c.Assert(resp.LoadBalancerDescriptions[0].Scheme, Equals, "internet-facing")
|
||||
hc := elb.HealthCheck{
|
||||
HealthyThreshold: 10,
|
||||
Interval: 30,
|
||||
Target: "TCP:80",
|
||||
Timeout: 5,
|
||||
UnhealthyThreshold: 2,
|
||||
}
|
||||
c.Assert(resp.LoadBalancerDescriptions[0].HealthCheck, DeepEquals, hc)
|
||||
ld := []elb.ListenerDescription{
|
||||
{
|
||||
Listener: elb.Listener{
|
||||
Protocol: "HTTP",
|
||||
LoadBalancerPort: 80,
|
||||
InstanceProtocol: "HTTP",
|
||||
InstancePort: 80,
|
||||
},
|
||||
},
|
||||
}
|
||||
c.Assert(resp.LoadBalancerDescriptions[0].ListenerDescriptions, DeepEquals, ld)
|
||||
ssg := elb.SourceSecurityGroup{
|
||||
GroupName: "amazon-elb-sg",
|
||||
OwnerAlias: "amazon-elb",
|
||||
}
|
||||
c.Assert(resp.LoadBalancerDescriptions[0].SourceSecurityGroup, DeepEquals, ssg)
|
||||
}
|
||||
|
||||
func (s *ClientTests) TestDescribeLoadBalancersBadRequest(c *C) {
|
||||
resp, err := s.elb.DescribeLoadBalancers("absentlb")
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, ErrorMatches, ".*(LoadBalancerNotFound).*")
|
||||
}
|
||||
|
||||
func (s *ClientTests) TestDescribeInstanceHealth(c *C) {
|
||||
createLBReq, instId := s.createInstanceAndLB(c)
|
||||
defer func() {
|
||||
_, err := s.elb.DeleteLoadBalancer(createLBReq.Name)
|
||||
c.Check(err, IsNil)
|
||||
_, err = s.ec2.TerminateInstances([]string{instId})
|
||||
c.Check(err, IsNil)
|
||||
}()
|
||||
_, err := s.elb.RegisterInstancesWithLoadBalancer([]string{instId}, createLBReq.Name)
|
||||
c.Assert(err, IsNil)
|
||||
resp, err := s.elb.DescribeInstanceHealth(createLBReq.Name, instId)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(resp.InstanceStates) > 0, Equals, true)
|
||||
c.Assert(resp.InstanceStates[0].Description, Equals, "Instance is in pending state.")
|
||||
c.Assert(resp.InstanceStates[0].InstanceId, Equals, instId)
|
||||
c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService")
|
||||
c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "Instance")
|
||||
}
|
||||
|
||||
func (s *ClientTests) TestDescribeInstanceHealthBadRequest(c *C) {
|
||||
createLBReq := elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1a"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
Protocol: "http",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := s.elb.CreateLoadBalancer(&createLBReq)
|
||||
c.Assert(err, IsNil)
|
||||
defer func() {
|
||||
_, err := s.elb.DeleteLoadBalancer(createLBReq.Name)
|
||||
c.Check(err, IsNil)
|
||||
}()
|
||||
resp, err := s.elb.DescribeInstanceHealth(createLBReq.Name, "i-foo")
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err, ErrorMatches, ".*i-foo.*(InvalidInstance).*")
|
||||
}
|
||||
|
||||
func (s *ClientTests) TestConfigureHealthCheck(c *C) {
|
||||
createLBReq := elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1a"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
Protocol: "http",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := s.elb.CreateLoadBalancer(&createLBReq)
|
||||
c.Assert(err, IsNil)
|
||||
defer func() {
|
||||
_, err := s.elb.DeleteLoadBalancer(createLBReq.Name)
|
||||
c.Check(err, IsNil)
|
||||
}()
|
||||
hc := elb.HealthCheck{
|
||||
HealthyThreshold: 10,
|
||||
Interval: 30,
|
||||
Target: "HTTP:80/",
|
||||
Timeout: 5,
|
||||
UnhealthyThreshold: 2,
|
||||
}
|
||||
resp, err := s.elb.ConfigureHealthCheck(createLBReq.Name, &hc)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.HealthCheck.HealthyThreshold, Equals, 10)
|
||||
c.Assert(resp.HealthCheck.Interval, Equals, 30)
|
||||
c.Assert(resp.HealthCheck.Target, Equals, "HTTP:80/")
|
||||
c.Assert(resp.HealthCheck.Timeout, Equals, 5)
|
||||
c.Assert(resp.HealthCheck.UnhealthyThreshold, Equals, 2)
|
||||
}
|
||||
|
||||
func (s *ClientTests) TestConfigureHealthCheckBadRequest(c *C) {
|
||||
createLBReq := elb.CreateLoadBalancer{
|
||||
Name: "testlb",
|
||||
AvailabilityZones: []string{"us-east-1a"},
|
||||
Listeners: []elb.Listener{
|
||||
{
|
||||
InstancePort: 80,
|
||||
InstanceProtocol: "http",
|
||||
LoadBalancerPort: 80,
|
||||
Protocol: "http",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := s.elb.CreateLoadBalancer(&createLBReq)
|
||||
c.Assert(err, IsNil)
|
||||
defer func() {
|
||||
_, err := s.elb.DeleteLoadBalancer(createLBReq.Name)
|
||||
c.Check(err, IsNil)
|
||||
}()
|
||||
hc := elb.HealthCheck{
|
||||
HealthyThreshold: 10,
|
||||
Interval: 30,
|
||||
Target: "HTTP:80",
|
||||
Timeout: 5,
|
||||
UnhealthyThreshold: 2,
|
||||
}
|
||||
resp, err := s.elb.ConfigureHealthCheck(createLBReq.Name, &hc)
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
expected := "HealthCheck HTTP Target must specify a port followed by a path that begins with a slash. e.g. HTTP:80/ping/this/path (ValidationError)"
|
||||
c.Assert(err.Error(), Equals, expected)
|
||||
}
|
||||
243
vendor/github.com/goamz/goamz/elb/elbt_test.go
сгенерированный
поставляемый
243
vendor/github.com/goamz/goamz/elb/elbt_test.go
сгенерированный
поставляемый
@@ -1,243 +0,0 @@
|
||||
package elb_test
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/elb"
|
||||
"github.com/goamz/goamz/elb/elbtest"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
// LocalServer represents a local elbtest fake server.
|
||||
type LocalServer struct {
|
||||
auth aws.Auth
|
||||
region aws.Region
|
||||
srv *elbtest.Server
|
||||
}
|
||||
|
||||
func (s *LocalServer) SetUp(c *C) {
|
||||
srv, err := elbtest.NewServer()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(srv, NotNil)
|
||||
s.srv = srv
|
||||
s.region = aws.Region{ELBEndpoint: srv.URL()}
|
||||
}
|
||||
|
||||
// LocalServerSuite defines tests that will run
|
||||
// against the local elbtest server. It includes
|
||||
// selected tests from ClientTests;
|
||||
// when the elbtest functionality is sufficient, it should
|
||||
// include all of them, and ClientTests can be simply embedded.
|
||||
type LocalServerSuite struct {
|
||||
srv LocalServer
|
||||
ServerTests
|
||||
clientTests ClientTests
|
||||
}
|
||||
|
||||
// ServerTests defines a set of tests designed to test
|
||||
// the elbtest local fake elb server.
|
||||
// It is not used as a test suite in itself, but embedded within
|
||||
// another type.
|
||||
type ServerTests struct {
|
||||
elb *elb.ELB
|
||||
}
|
||||
|
||||
// AmazonServerSuite runs the elbtest server tests against a live ELB server.
|
||||
// It will only be activated if the -all flag is specified.
|
||||
type AmazonServerSuite struct {
|
||||
srv AmazonServer
|
||||
ServerTests
|
||||
}
|
||||
|
||||
var _ = Suite(&AmazonServerSuite{})
|
||||
|
||||
func (s *AmazonServerSuite) SetUpSuite(c *C) {
|
||||
if !*amazon {
|
||||
c.Skip("AmazonServerSuite tests not enabled")
|
||||
}
|
||||
s.srv.SetUp(c)
|
||||
s.ServerTests.elb = elb.New(s.srv.auth, aws.USEast)
|
||||
}
|
||||
|
||||
var _ = Suite(&LocalServerSuite{})
|
||||
|
||||
func (s *LocalServerSuite) SetUpSuite(c *C) {
|
||||
s.srv.SetUp(c)
|
||||
s.ServerTests.elb = elb.New(s.srv.auth, s.srv.region)
|
||||
s.clientTests.elb = elb.New(s.srv.auth, s.srv.region)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestCreateLoadBalancer(c *C) {
|
||||
s.clientTests.TestCreateAndDeleteLoadBalancer(c)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestCreateLoadBalancerError(c *C) {
|
||||
s.clientTests.TestCreateLoadBalancerError(c)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDescribeLoadBalancer(c *C) {
|
||||
s.clientTests.TestDescribeLoadBalancers(c)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDescribeLoadBalancerListsAddedByNewLoadbalancerFunc(c *C) {
|
||||
srv := s.srv.srv
|
||||
srv.NewLoadBalancer("wierdlb")
|
||||
defer srv.RemoveLoadBalancer("wierdlb")
|
||||
resp, err := s.clientTests.elb.DescribeLoadBalancers()
|
||||
c.Assert(err, IsNil)
|
||||
isPresent := false
|
||||
for _, desc := range resp.LoadBalancerDescriptions {
|
||||
if desc.LoadBalancerName == "wierdlb" {
|
||||
isPresent = true
|
||||
}
|
||||
}
|
||||
c.Assert(isPresent, Equals, true)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDescribeLoadBalancerListsInstancesAddedByRegisterInstancesFunc(c *C) {
|
||||
srv := s.srv.srv
|
||||
lbName := "somelb"
|
||||
srv.NewLoadBalancer(lbName)
|
||||
defer srv.RemoveLoadBalancer(lbName)
|
||||
instId := srv.NewInstance()
|
||||
defer srv.RemoveInstance(instId)
|
||||
srv.RegisterInstance(instId, lbName) // no need to deregister, since we're removing the lb
|
||||
resp, err := s.clientTests.elb.DescribeLoadBalancers()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(resp.LoadBalancerDescriptions) > 0, Equals, true)
|
||||
c.Assert(len(resp.LoadBalancerDescriptions[0].Instances) > 0, Equals, true)
|
||||
c.Assert(resp.LoadBalancerDescriptions[0].Instances, DeepEquals, []elb.Instance{{InstanceId: instId}})
|
||||
srv.DeregisterInstance(instId, lbName)
|
||||
resp, err = s.clientTests.elb.DescribeLoadBalancers()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.LoadBalancerDescriptions[0].Instances, DeepEquals, []elb.Instance(nil))
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDescribeLoadBalancersBadRequest(c *C) {
|
||||
s.clientTests.TestDescribeLoadBalancersBadRequest(c)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestRegisterInstanceWithLoadBalancer(c *C) {
|
||||
srv := s.srv.srv
|
||||
instId := srv.NewInstance()
|
||||
defer srv.RemoveInstance(instId)
|
||||
srv.NewLoadBalancer("testlb")
|
||||
defer srv.RemoveLoadBalancer("testlb")
|
||||
resp, err := s.clientTests.elb.RegisterInstancesWithLoadBalancer([]string{instId}, "testlb")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.InstanceIds, DeepEquals, []string{instId})
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestRegisterInstanceWithLoadBalancerWithAbsentInstance(c *C) {
|
||||
srv := s.srv.srv
|
||||
srv.NewLoadBalancer("testlb")
|
||||
defer srv.RemoveLoadBalancer("testlb")
|
||||
resp, err := s.clientTests.elb.RegisterInstancesWithLoadBalancer([]string{"i-212"}, "testlb")
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err, ErrorMatches, `^InvalidInstance found in \[i-212\]. Invalid id: "i-212" \(InvalidInstance\)$`)
|
||||
c.Assert(resp, IsNil)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestRegisterInstanceWithLoadBalancerWithAbsentLoadBalancer(c *C) {
|
||||
// the verification if the lb exists is done before the instances, so there is no need to create
|
||||
// fixture instances for this test, it'll never get that far
|
||||
resp, err := s.clientTests.elb.RegisterInstancesWithLoadBalancer([]string{"i-212"}, "absentlb")
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err, ErrorMatches, `^There is no ACTIVE Load Balancer named 'absentlb' \(LoadBalancerNotFound\)$`)
|
||||
c.Assert(resp, IsNil)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDeregisterInstanceWithLoadBalancer(c *C) {
|
||||
// there is no need to register the instance first, amazon returns the same response
|
||||
// in both cases (instance registered or not)
|
||||
srv := s.srv.srv
|
||||
instId := srv.NewInstance()
|
||||
defer srv.RemoveInstance(instId)
|
||||
srv.NewLoadBalancer("testlb")
|
||||
defer srv.RemoveLoadBalancer("testlb")
|
||||
resp, err := s.clientTests.elb.DeregisterInstancesFromLoadBalancer([]string{instId}, "testlb")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.RequestId, Not(Equals), "")
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDeregisterInstanceWithLoadBalancerWithAbsentLoadBalancer(c *C) {
|
||||
resp, err := s.clientTests.elb.DeregisterInstancesFromLoadBalancer([]string{"i-212"}, "absentlb")
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err, ErrorMatches, `^There is no ACTIVE Load Balancer named 'absentlb' \(LoadBalancerNotFound\)$`)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDeregisterInstancewithLoadBalancerWithAbsentInstance(c *C) {
|
||||
srv := s.srv.srv
|
||||
srv.NewLoadBalancer("testlb")
|
||||
defer srv.RemoveLoadBalancer("testlb")
|
||||
resp, err := s.clientTests.elb.DeregisterInstancesFromLoadBalancer([]string{"i-212"}, "testlb")
|
||||
c.Assert(resp, IsNil)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(err, ErrorMatches, `^InvalidInstance found in \[i-212\]. Invalid id: "i-212" \(InvalidInstance\)$`)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDescribeInstanceHealth(c *C) {
|
||||
srv := s.srv.srv
|
||||
instId := srv.NewInstance()
|
||||
defer srv.RemoveInstance(instId)
|
||||
srv.NewLoadBalancer("testlb")
|
||||
defer srv.RemoveLoadBalancer("testlb")
|
||||
resp, err := s.clientTests.elb.DescribeInstanceHealth("testlb", instId)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(resp.InstanceStates) > 0, Equals, true)
|
||||
c.Assert(resp.InstanceStates[0].Description, Equals, "Instance is in pending state.")
|
||||
c.Assert(resp.InstanceStates[0].InstanceId, Equals, instId)
|
||||
c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService")
|
||||
c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "Instance")
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDescribeInstanceHealthBadRequest(c *C) {
|
||||
s.clientTests.TestDescribeInstanceHealthBadRequest(c)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDescribeInstanceHealthWithoutSpecifyingInstances(c *C) {
|
||||
srv := s.srv.srv
|
||||
instId := srv.NewInstance()
|
||||
defer srv.RemoveInstance(instId)
|
||||
srv.NewLoadBalancer("testlb")
|
||||
defer srv.RemoveLoadBalancer("testlb")
|
||||
srv.RegisterInstance(instId, "testlb")
|
||||
resp, err := s.clientTests.elb.DescribeInstanceHealth("testlb")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(resp.InstanceStates) > 0, Equals, true)
|
||||
c.Assert(resp.InstanceStates[0].Description, Equals, "Instance is in pending state.")
|
||||
c.Assert(resp.InstanceStates[0].InstanceId, Equals, instId)
|
||||
c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService")
|
||||
c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "Instance")
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestDescribeInstanceHealthChangingIt(c *C) {
|
||||
srv := s.srv.srv
|
||||
instId := srv.NewInstance()
|
||||
defer srv.RemoveInstance(instId)
|
||||
srv.NewLoadBalancer("somelb")
|
||||
defer srv.RemoveLoadBalancer("somelb")
|
||||
srv.RegisterInstance(instId, "somelb")
|
||||
state := elb.InstanceState{
|
||||
Description: "Instance has failed at least the UnhealthyThreshold number of health checks consecutively",
|
||||
InstanceId: instId,
|
||||
State: "OutOfService",
|
||||
ReasonCode: "Instance",
|
||||
}
|
||||
srv.ChangeInstanceState("somelb", state)
|
||||
resp, err := s.clientTests.elb.DescribeInstanceHealth("somelb")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(resp.InstanceStates) > 0, Equals, true)
|
||||
c.Assert(resp.InstanceStates[0].Description, Equals, "Instance has failed at least the UnhealthyThreshold number of health checks consecutively")
|
||||
c.Assert(resp.InstanceStates[0].InstanceId, Equals, instId)
|
||||
c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService")
|
||||
c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "Instance")
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestConfigureHealthCheck(c *C) {
|
||||
s.clientTests.TestConfigureHealthCheck(c)
|
||||
}
|
||||
|
||||
func (s *LocalServerSuite) TestConfigureHealthCheckBadRequest(c *C) {
|
||||
s.clientTests.TestConfigureHealthCheckBadRequest(c)
|
||||
}
|
||||
551
vendor/github.com/goamz/goamz/elb/elbtest/server.go
сгенерированный
поставляемый
551
vendor/github.com/goamz/goamz/elb/elbtest/server.go
сгенерированный
поставляемый
@@ -1,551 +0,0 @@
|
||||
// Package elbtest implements a fake ELB provider with the capability of
|
||||
// inducing errors on any given operation, and retrospectively determining what
|
||||
// operations have been carried out.
|
||||
package elbtest
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/elb"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Server implements an ELB simulator for use in testing.
|
||||
type Server struct {
|
||||
url string
|
||||
listener net.Listener
|
||||
mutex sync.Mutex
|
||||
reqId int
|
||||
lbs map[string]*elb.LoadBalancerDescription
|
||||
lbsReqs map[string]url.Values
|
||||
instances []string
|
||||
instanceStates map[string][]*elb.InstanceState
|
||||
instCount int
|
||||
}
|
||||
|
||||
// Starts and returns a new server
|
||||
func NewServer() (*Server, error) {
|
||||
l, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot listen on localhost: %v", err)
|
||||
}
|
||||
srv := &Server{
|
||||
listener: l,
|
||||
url: "http://" + l.Addr().String(),
|
||||
lbs: make(map[string]*elb.LoadBalancerDescription),
|
||||
instanceStates: make(map[string][]*elb.InstanceState),
|
||||
}
|
||||
go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
srv.serveHTTP(w, req)
|
||||
}))
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// Quit closes down the server.
|
||||
func (srv *Server) Quit() {
|
||||
srv.listener.Close()
|
||||
}
|
||||
|
||||
// URL returns the URL of the server.
|
||||
func (srv *Server) URL() string {
|
||||
return srv.url
|
||||
}
|
||||
|
||||
type xmlErrors struct {
|
||||
XMLName string `xml:"ErrorResponse"`
|
||||
Error elb.Error
|
||||
}
|
||||
|
||||
func (srv *Server) error(w http.ResponseWriter, err *elb.Error) {
|
||||
w.WriteHeader(err.StatusCode)
|
||||
xmlErr := xmlErrors{Error: *err}
|
||||
if e := xml.NewEncoder(w).Encode(xmlErr); e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
req.ParseForm()
|
||||
srv.mutex.Lock()
|
||||
defer srv.mutex.Unlock()
|
||||
f := actions[req.Form.Get("Action")]
|
||||
if f == nil {
|
||||
srv.error(w, &elb.Error{
|
||||
StatusCode: 400,
|
||||
Code: "InvalidParameterValue",
|
||||
Message: "Unrecognized Action",
|
||||
})
|
||||
}
|
||||
reqId := fmt.Sprintf("req%0X", srv.reqId)
|
||||
srv.reqId++
|
||||
if resp, err := f(srv, w, req, reqId); err == nil {
|
||||
if err := xml.NewEncoder(w).Encode(resp); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
switch err.(type) {
|
||||
case *elb.Error:
|
||||
srv.error(w, err.(*elb.Error))
|
||||
default:
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) createLoadBalancer(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
|
||||
composition := map[string]string{
|
||||
"AvailabilityZones.member.1": "Subnets.member.1",
|
||||
}
|
||||
if err := srv.validateComposition(req, composition); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
required := []string{
|
||||
"Listeners.member.1.InstancePort",
|
||||
"Listeners.member.1.InstanceProtocol",
|
||||
"Listeners.member.1.Protocol",
|
||||
"Listeners.member.1.LoadBalancerPort",
|
||||
"LoadBalancerName",
|
||||
}
|
||||
if err := srv.validate(req, required); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := req.FormValue("Path")
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
lbName := req.FormValue("LoadBalancerName")
|
||||
srv.lbs[lbName] = srv.makeLoadBalancerDescription(req.Form)
|
||||
srv.lbs[lbName].DNSName = fmt.Sprintf("%s-some-aws-stuff.us-east-1.elb.amazonaws.com", lbName)
|
||||
return elb.CreateLoadBalancerResp{
|
||||
DNSName: srv.lbs[lbName].DNSName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (srv *Server) deleteLoadBalancer(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
|
||||
if err := srv.validate(req, []string{"LoadBalancerName"}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srv.RemoveLoadBalancer(req.FormValue("LoadBalancerName"))
|
||||
return elb.SimpleResp{RequestId: reqId}, nil
|
||||
}
|
||||
|
||||
func (srv *Server) registerInstancesWithLoadBalancer(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
|
||||
required := []string{"LoadBalancerName", "Instances.member.1.InstanceId"}
|
||||
if err := srv.validate(req, required); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lbName := req.FormValue("LoadBalancerName")
|
||||
if err := srv.lbExists(lbName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instIds := []string{}
|
||||
instances := []elb.Instance{}
|
||||
i := 1
|
||||
instId := req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i))
|
||||
for instId != "" {
|
||||
if err := srv.instanceExists(instId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instIds = append(instIds, instId)
|
||||
instances = append(instances, elb.Instance{InstanceId: instId})
|
||||
i++
|
||||
instId = req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i))
|
||||
}
|
||||
srv.instanceStates[lbName] = append(srv.instanceStates[lbName], srv.makeInstanceState(instId))
|
||||
srv.lbs[lbName].Instances = append(srv.lbs[lbName].Instances, instances...)
|
||||
return elb.RegisterInstancesResp{InstanceIds: instIds}, nil
|
||||
}
|
||||
|
||||
func (srv *Server) deregisterInstancesFromLoadBalancer(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
|
||||
required := []string{"LoadBalancerName"}
|
||||
if err := srv.validate(req, required); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lbName := req.FormValue("LoadBalancerName")
|
||||
if err := srv.lbExists(lbName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i := 1
|
||||
lb := srv.lbs[lbName]
|
||||
instId := req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i))
|
||||
for instId != "" {
|
||||
if err := srv.instanceExists(instId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i++
|
||||
removeInstanceFromLB(lb, instId)
|
||||
instId = req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i))
|
||||
}
|
||||
srv.lbs[lbName] = lb
|
||||
srv.removeInstanceStatesFromLoadBalancer(lbName, instId)
|
||||
return elb.SimpleResp{RequestId: reqId}, nil
|
||||
}
|
||||
|
||||
func (srv *Server) describeLoadBalancers(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
|
||||
i := 1
|
||||
lbName := req.FormValue(fmt.Sprintf("LoadBalancerNames.member.%d", i))
|
||||
for lbName != "" {
|
||||
key := fmt.Sprintf("LoadBalancerNames.member.%d", i)
|
||||
if req.FormValue(key) != "" {
|
||||
if err := srv.lbExists(req.FormValue(key)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
i++
|
||||
lbName = req.FormValue(fmt.Sprintf("LoadBalancerNames.member.%d", i))
|
||||
}
|
||||
lbsDesc := make([]elb.LoadBalancerDescription, len(srv.lbs))
|
||||
i = 0
|
||||
for _, lb := range srv.lbs {
|
||||
lbsDesc[i] = *lb
|
||||
i++
|
||||
}
|
||||
resp := elb.DescribeLoadBalancerResp{
|
||||
LoadBalancerDescriptions: lbsDesc,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// getParameters returns the value all parameters from a request that matches a
|
||||
// prefix.
|
||||
//
|
||||
// For example, for the prefix "Subnets.member.", it will return a slice
|
||||
// containing the value of keys "Subnets.member.1", "Subnets.member.2" ...
|
||||
// "Subnets.member.N". The prefix must include the trailing dot.
|
||||
func (srv *Server) getParameters(prefix string, values url.Values) []string {
|
||||
i, key := 1, ""
|
||||
var k = func(n int) string {
|
||||
return fmt.Sprintf(prefix+"%d", n)
|
||||
}
|
||||
var result []string
|
||||
for i, key = 2, k(i); values.Get(key) != ""; i, key = i+1, k(i) {
|
||||
result = append(result, values.Get(key))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (srv *Server) makeInstanceState(id string) *elb.InstanceState {
|
||||
return &elb.InstanceState{
|
||||
Description: "Instance is in pending state.",
|
||||
InstanceId: id,
|
||||
State: "OutOfService",
|
||||
ReasonCode: "Instance",
|
||||
}
|
||||
}
|
||||
|
||||
func removeInstanceFromLB(lb *elb.LoadBalancerDescription, id string) {
|
||||
index := -1
|
||||
for i, instance := range lb.Instances {
|
||||
if instance.InstanceId == id {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index > -1 {
|
||||
copy(lb.Instances[index:], lb.Instances[index+1:])
|
||||
lb.Instances = lb.Instances[:len(lb.Instances)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) removeInstanceStatesFromLoadBalancer(lb, id string) {
|
||||
for i, state := range srv.instanceStates[lb] {
|
||||
if state.InstanceId == id {
|
||||
a := srv.instanceStates[lb]
|
||||
a[i], a = a[len(a)-1], a[:len(a)-1]
|
||||
srv.instanceStates[lb] = a
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) makeLoadBalancerDescription(value url.Values) *elb.LoadBalancerDescription {
|
||||
lds := []elb.ListenerDescription{}
|
||||
i := 1
|
||||
protocol := value.Get(fmt.Sprintf("Listeners.member.%d.Protocol", i))
|
||||
for protocol != "" {
|
||||
key := fmt.Sprintf("Listeners.member.%d.", i)
|
||||
lInstPort, _ := strconv.Atoi(value.Get(key + "InstancePort"))
|
||||
lLBPort, _ := strconv.Atoi(value.Get(key + "LoadBalancerPort"))
|
||||
lDescription := elb.ListenerDescription{
|
||||
Listener: elb.Listener{
|
||||
Protocol: strings.ToUpper(protocol),
|
||||
InstanceProtocol: strings.ToUpper(value.Get(key + "InstanceProtocol")),
|
||||
LoadBalancerPort: lLBPort,
|
||||
InstancePort: lInstPort,
|
||||
},
|
||||
}
|
||||
i++
|
||||
protocol = value.Get(fmt.Sprintf("Listeners.member.%d.Protocol", i))
|
||||
lds = append(lds, lDescription)
|
||||
}
|
||||
sourceSecGroup := srv.makeSourceSecGroup(value)
|
||||
lbDesc := elb.LoadBalancerDescription{
|
||||
AvailabilityZones: srv.getParameters("AvailabilityZones.member.", value),
|
||||
Subnets: srv.getParameters("Subnets.member.", value),
|
||||
SecurityGroups: srv.getParameters("SecurityGroups.member.", value),
|
||||
HealthCheck: srv.makeHealthCheck(value),
|
||||
ListenerDescriptions: lds,
|
||||
Scheme: value.Get("Scheme"),
|
||||
SourceSecurityGroup: sourceSecGroup,
|
||||
LoadBalancerName: value.Get("LoadBalancerName"),
|
||||
}
|
||||
if lbDesc.Scheme == "" {
|
||||
lbDesc.Scheme = "internet-facing"
|
||||
}
|
||||
return &lbDesc
|
||||
}
|
||||
|
||||
func (srv *Server) makeHealthCheck(value url.Values) elb.HealthCheck {
|
||||
ht := 10
|
||||
timeout := 5
|
||||
ut := 2
|
||||
interval := 30
|
||||
target := "TCP:80"
|
||||
if v := value.Get("HealthCheck.HealthyThreshold"); v != "" {
|
||||
ht, _ = strconv.Atoi(v)
|
||||
}
|
||||
if v := value.Get("HealthCheck.Timeout"); v != "" {
|
||||
timeout, _ = strconv.Atoi(v)
|
||||
}
|
||||
if v := value.Get("HealthCheck.UnhealthyThreshold"); v != "" {
|
||||
ut, _ = strconv.Atoi(v)
|
||||
}
|
||||
if v := value.Get("HealthCheck.Interval"); v != "" {
|
||||
interval, _ = strconv.Atoi(v)
|
||||
}
|
||||
if v := value.Get("HealthCheck.Target"); v != "" {
|
||||
target = v
|
||||
}
|
||||
return elb.HealthCheck{
|
||||
HealthyThreshold: ht,
|
||||
Interval: interval,
|
||||
Target: target,
|
||||
Timeout: timeout,
|
||||
UnhealthyThreshold: ut,
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) makeSourceSecGroup(value url.Values) elb.SourceSecurityGroup {
|
||||
name := "amazon-elb-sg"
|
||||
alias := "amazon-elb"
|
||||
if v := value.Get("SourceSecurityGroup.GroupName"); v != "" {
|
||||
name = v
|
||||
}
|
||||
if v := value.Get("SourceSecurityGroup.OwnerAlias"); v != "" {
|
||||
alias = v
|
||||
}
|
||||
return elb.SourceSecurityGroup{
|
||||
GroupName: name,
|
||||
OwnerAlias: alias,
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) describeInstanceHealth(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
|
||||
if err := srv.lbExists(req.FormValue("LoadBalancerName")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := elb.DescribeInstanceHealthResp{
|
||||
InstanceStates: []elb.InstanceState{},
|
||||
}
|
||||
for _, state := range srv.instanceStates[req.FormValue("LoadBalancerName")] {
|
||||
resp.InstanceStates = append(resp.InstanceStates, *state)
|
||||
}
|
||||
i := 1
|
||||
instanceId := req.FormValue("Instances.member.1.InstanceId")
|
||||
for instanceId != "" {
|
||||
if err := srv.instanceExists(instanceId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
is := elb.InstanceState{
|
||||
Description: "Instance is in pending state.",
|
||||
InstanceId: instanceId,
|
||||
State: "OutOfService",
|
||||
ReasonCode: "Instance",
|
||||
}
|
||||
resp.InstanceStates = append(resp.InstanceStates, is)
|
||||
i++
|
||||
instanceId = req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (srv *Server) configureHealthCheck(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
|
||||
required := []string{
|
||||
"LoadBalancerName",
|
||||
"HealthCheck.HealthyThreshold",
|
||||
"HealthCheck.Interval",
|
||||
"HealthCheck.Target",
|
||||
"HealthCheck.Timeout",
|
||||
"HealthCheck.UnhealthyThreshold",
|
||||
}
|
||||
if err := srv.validate(req, required); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target := req.FormValue("HealthCheck.Target")
|
||||
r, err := regexp.Compile(`[\w]+:[\d]+\/+`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if m := r.FindStringSubmatch(target); m == nil {
|
||||
return nil, &elb.Error{
|
||||
StatusCode: 400,
|
||||
Code: "ValidationError",
|
||||
Message: "HealthCheck HTTP Target must specify a port followed by a path that begins with a slash. e.g. HTTP:80/ping/this/path",
|
||||
}
|
||||
}
|
||||
ht, _ := strconv.Atoi(req.FormValue("HealthCheck.HealthyThreshold"))
|
||||
interval, _ := strconv.Atoi(req.FormValue("HealthCheck.Interval"))
|
||||
timeout, _ := strconv.Atoi(req.FormValue("HealthCheck.Timeout"))
|
||||
ut, _ := strconv.Atoi(req.FormValue("HealthCheck.UnhealthyThreshold"))
|
||||
return elb.HealthCheckResp{
|
||||
HealthCheck: &elb.HealthCheck{
|
||||
HealthyThreshold: ht,
|
||||
Interval: interval,
|
||||
Target: target,
|
||||
Timeout: timeout,
|
||||
UnhealthyThreshold: ut,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (srv *Server) instanceExists(id string) error {
|
||||
for _, instId := range srv.instances {
|
||||
if instId == id {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return &elb.Error{
|
||||
StatusCode: 400,
|
||||
Code: "InvalidInstance",
|
||||
Message: fmt.Sprintf("InvalidInstance found in [%s]. Invalid id: \"%s\"", id, id),
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) lbExists(name string) error {
|
||||
if _, ok := srv.lbs[name]; !ok {
|
||||
return &elb.Error{
|
||||
StatusCode: 400,
|
||||
Code: "LoadBalancerNotFound",
|
||||
Message: fmt.Sprintf("There is no ACTIVE Load Balancer named '%s'", name),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) validate(req *http.Request, required []string) error {
|
||||
for _, field := range required {
|
||||
if req.FormValue(field) == "" {
|
||||
return &elb.Error{
|
||||
StatusCode: 400,
|
||||
Code: "ValidationError",
|
||||
Message: fmt.Sprintf("%s is required.", field),
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validates the composition of the fields.
|
||||
//
|
||||
// Some fields cannot be together in the same request, such as AvailabilityZones and Subnets.
|
||||
// A sample map with the above requirement would be
|
||||
// c := map[string]string{
|
||||
// "AvailabilityZones.member.1": "Subnets.member.1",
|
||||
// }
|
||||
//
|
||||
// The server also requires that at least one of those fields are specified.
|
||||
func (srv *Server) validateComposition(req *http.Request, composition map[string]string) error {
|
||||
for k, v := range composition {
|
||||
if req.FormValue(k) != "" && req.FormValue(v) != "" {
|
||||
return &elb.Error{
|
||||
StatusCode: 400,
|
||||
Code: "ValidationError",
|
||||
Message: fmt.Sprintf("Only one of %s or %s may be specified", k, v),
|
||||
}
|
||||
}
|
||||
if req.FormValue(k) == "" && req.FormValue(v) == "" {
|
||||
return &elb.Error{
|
||||
StatusCode: 400,
|
||||
Code: "ValidationError",
|
||||
Message: fmt.Sprintf("Either %s or %s must be specified", k, v),
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates a fake instance in the server
|
||||
func (srv *Server) NewInstance() string {
|
||||
srv.instCount++
|
||||
instId := fmt.Sprintf("i-%d", srv.instCount)
|
||||
srv.instances = append(srv.instances, instId)
|
||||
return instId
|
||||
}
|
||||
|
||||
// Removes a fake instance from the server
|
||||
//
|
||||
// If no instance is found it does nothing
|
||||
func (srv *Server) RemoveInstance(instId string) {
|
||||
for i, id := range srv.instances {
|
||||
if id == instId {
|
||||
srv.instances[i], srv.instances = srv.instances[len(srv.instances)-1], srv.instances[:len(srv.instances)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a fake load balancer in the fake server
|
||||
func (srv *Server) NewLoadBalancer(name string) {
|
||||
srv.lbs[name] = &elb.LoadBalancerDescription{
|
||||
LoadBalancerName: name,
|
||||
DNSName: fmt.Sprintf("%s-some-aws-stuff.sa-east-1.amazonaws.com", name),
|
||||
}
|
||||
}
|
||||
|
||||
// Removes a fake load balancer from the fake server
|
||||
func (srv *Server) RemoveLoadBalancer(name string) {
|
||||
delete(srv.lbs, name)
|
||||
}
|
||||
|
||||
// Register a fake instance with a fake Load Balancer
|
||||
//
|
||||
// If the Load Balancer does not exists it does nothing
|
||||
func (srv *Server) RegisterInstance(instId, lbName string) {
|
||||
lb, ok := srv.lbs[lbName]
|
||||
if !ok {
|
||||
fmt.Println("lb not found :/")
|
||||
return
|
||||
}
|
||||
lb.Instances = append(lb.Instances, elb.Instance{InstanceId: instId})
|
||||
srv.instanceStates[lbName] = append(srv.instanceStates[lbName], srv.makeInstanceState(instId))
|
||||
}
|
||||
|
||||
func (srv *Server) DeregisterInstance(instId, lbName string) {
|
||||
removeInstanceFromLB(srv.lbs[lbName], instId)
|
||||
srv.removeInstanceStatesFromLoadBalancer(lbName, instId)
|
||||
}
|
||||
|
||||
func (srv *Server) ChangeInstanceState(lb string, state elb.InstanceState) {
|
||||
states := srv.instanceStates[lb]
|
||||
for i, s := range states {
|
||||
if s.InstanceId == state.InstanceId {
|
||||
srv.instanceStates[lb][i] = &state
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) (interface{}, error){
|
||||
"CreateLoadBalancer": (*Server).createLoadBalancer,
|
||||
"DeleteLoadBalancer": (*Server).deleteLoadBalancer,
|
||||
"RegisterInstancesWithLoadBalancer": (*Server).registerInstancesWithLoadBalancer,
|
||||
"DeregisterInstancesFromLoadBalancer": (*Server).deregisterInstancesFromLoadBalancer,
|
||||
"DescribeLoadBalancers": (*Server).describeLoadBalancers,
|
||||
"DescribeInstanceHealth": (*Server).describeInstanceHealth,
|
||||
"ConfigureHealthCheck": (*Server).configureHealthCheck,
|
||||
}
|
||||
9
vendor/github.com/goamz/goamz/elb/export_test.go
сгенерированный
поставляемый
9
vendor/github.com/goamz/goamz/elb/export_test.go
сгенерированный
поставляемый
@@ -1,9 +0,0 @@
|
||||
package elb
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
)
|
||||
|
||||
func Sign(auth aws.Auth, method, path string, params map[string]string, host string) {
|
||||
sign(auth, method, path, params, host)
|
||||
}
|
||||
234
vendor/github.com/goamz/goamz/elb/response_test.go
сгенерированный
поставляемый
234
vendor/github.com/goamz/goamz/elb/response_test.go
сгенерированный
поставляемый
@@ -1,234 +0,0 @@
|
||||
package elb_test
|
||||
|
||||
var CreateLoadBalancer = `
|
||||
<CreateLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<CreateLoadBalancerResult>
|
||||
<DNSName>testlb-339187009.us-east-1.elb.amazonaws.com</DNSName>
|
||||
</CreateLoadBalancerResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>0c3a8e29-490e-11e2-8647-e14ad5151f1f</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateLoadBalancerResponse>
|
||||
`
|
||||
|
||||
var CreateLoadBalancerBadRequest = `
|
||||
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>ValidationError</Code>
|
||||
<Message>Only one of SubnetIds or AvailabilityZones may be specified</Message>
|
||||
</Error>
|
||||
<RequestId>159253fc-49dc-11e2-a47d-cde463c91a3c</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
|
||||
var DeleteLoadBalancer = `
|
||||
<DeleteLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<DeleteLoadBalancerResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8d7223db-49d7-11e2-bba9-35ba56032fe1</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteLoadBalancerResponse>
|
||||
`
|
||||
|
||||
var RegisterInstancesWithLoadBalancer = `
|
||||
<RegisterInstancesWithLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<RegisterInstancesWithLoadBalancerResult>
|
||||
<Instances>
|
||||
<member>
|
||||
<InstanceId>i-b44db8ca</InstanceId>
|
||||
</member>
|
||||
<member>
|
||||
<InstanceId>i-461ecf38</InstanceId>
|
||||
</member>
|
||||
</Instances>
|
||||
</RegisterInstancesWithLoadBalancerResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>0fc82478-49e1-11e2-b947-8768f15220aa</RequestId>
|
||||
</ResponseMetadata>
|
||||
</RegisterInstancesWithLoadBalancerResponse>
|
||||
`
|
||||
|
||||
var RegisterInstancesWithLoadBalancerBadRequest = `
|
||||
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>LoadBalancerNotFound</Code>
|
||||
<Message>There is no ACTIVE Load Balancer named 'absentLB'</Message>
|
||||
</Error>
|
||||
<RequestId>19a0bb97-49f7-11e2-90b4-6bb9ec8331bf</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
|
||||
var DeregisterInstancesFromLoadBalancer = `
|
||||
<DeregisterInstancesFromLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<DeregisterInstancesFromLoadBalancerResult>
|
||||
<Instances/>
|
||||
</DeregisterInstancesFromLoadBalancerResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>d6490837-49fd-11e2-bba9-35ba56032fe1</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeregisterInstancesFromLoadBalancerResponse>
|
||||
`
|
||||
|
||||
var DeregisterInstancesFromLoadBalancerBadRequest = `
|
||||
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>LoadBalancerNotFound</Code>
|
||||
<Message>There is no ACTIVE Load Balancer named 'absentlb'</Message>
|
||||
</Error>
|
||||
<RequestId>498e2b4a-4aa1-11e2-8839-d19a879f2eec</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
|
||||
var DescribeLoadBalancers = `
|
||||
<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<DescribeLoadBalancersResult>
|
||||
<LoadBalancerDescriptions>
|
||||
<member>
|
||||
<SecurityGroups/>
|
||||
<CreatedTime>2012-12-27T11:51:52.970Z</CreatedTime>
|
||||
<LoadBalancerName>testlb</LoadBalancerName>
|
||||
<HealthCheck>
|
||||
<Interval>30</Interval>
|
||||
<Target>TCP:80</Target>
|
||||
<HealthyThreshold>10</HealthyThreshold>
|
||||
<Timeout>5</Timeout>
|
||||
<UnhealthyThreshold>2</UnhealthyThreshold>
|
||||
</HealthCheck>
|
||||
<ListenerDescriptions>
|
||||
<member>
|
||||
<PolicyNames/>
|
||||
<Listener>
|
||||
<Protocol>HTTP</Protocol>
|
||||
<LoadBalancerPort>80</LoadBalancerPort>
|
||||
<InstanceProtocol>HTTP</InstanceProtocol>
|
||||
<InstancePort>80</InstancePort>
|
||||
</Listener>
|
||||
</member>
|
||||
</ListenerDescriptions>
|
||||
<Instances/>
|
||||
<Policies>
|
||||
<AppCookieStickinessPolicies/>
|
||||
<OtherPolicies/>
|
||||
<LBCookieStickinessPolicies/>
|
||||
</Policies>
|
||||
<AvailabilityZones>
|
||||
<member>us-east-1a</member>
|
||||
</AvailabilityZones>
|
||||
<CanonicalHostedZoneName>testlb-2087227216.us-east-1.elb.amazonaws.com</CanonicalHostedZoneName>
|
||||
<CanonicalHostedZoneNameID>Z3DZXE0Q79N41H</CanonicalHostedZoneNameID>
|
||||
<Scheme>internet-facing</Scheme>
|
||||
<SourceSecurityGroup>
|
||||
<OwnerAlias>amazon-elb</OwnerAlias>
|
||||
<GroupName>amazon-elb-sg</GroupName>
|
||||
</SourceSecurityGroup>
|
||||
<DNSName>testlb-2087227216.us-east-1.elb.amazonaws.com</DNSName>
|
||||
<BackendServerDescriptions/>
|
||||
<Subnets/>
|
||||
</member>
|
||||
</LoadBalancerDescriptions>
|
||||
</DescribeLoadBalancersResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>e2e81963-5055-11e2-99c7-434205631d9b</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeLoadBalancersResponse>
|
||||
`
|
||||
|
||||
var DescribeLoadBalancersBadRequest = `
|
||||
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>LoadBalancerNotFound</Code>
|
||||
<Message>Cannot find Load Balancer absentlb</Message>
|
||||
</Error>
|
||||
<RequestId>f14f348e-50f7-11e2-9831-f770dd71c209</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
|
||||
var DescribeInstanceHealth = `
|
||||
<DescribeInstanceHealthResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<DescribeInstanceHealthResult>
|
||||
<InstanceStates>
|
||||
<member>
|
||||
<Description>Instance registration is still in progress.</Description>
|
||||
<InstanceId>i-b44db8ca</InstanceId>
|
||||
<State>OutOfService</State>
|
||||
<ReasonCode>ELB</ReasonCode>
|
||||
</member>
|
||||
</InstanceStates>
|
||||
</DescribeInstanceHealthResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>da0d0f9e-5669-11e2-9f81-319facce7423</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeInstanceHealthResponse>
|
||||
`
|
||||
|
||||
var DescribeInstanceHealthBadRequest = `
|
||||
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>InvalidInstance</Code>
|
||||
<Message>Could not find EC2 instance i-foooo.</Message>
|
||||
</Error>
|
||||
<RequestId>352e00d6-566c-11e2-a46d-313272bbb522</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
|
||||
var ConfigureHealthCheck = `
|
||||
<ConfigureHealthCheckResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<ConfigureHealthCheckResult>
|
||||
<HealthCheck>
|
||||
<Interval>30</Interval>
|
||||
<Target>HTTP:80/</Target>
|
||||
<HealthyThreshold>10</HealthyThreshold>
|
||||
<Timeout>5</Timeout>
|
||||
<UnhealthyThreshold>2</UnhealthyThreshold>
|
||||
</HealthCheck>
|
||||
</ConfigureHealthCheckResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>a882d12c-5694-11e2-b647-594652c9487c</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ConfigureHealthCheckResponse>
|
||||
`
|
||||
|
||||
var ConfigureHealthCheckBadRequest = `
|
||||
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>LoadBalancerNotFound</Code>
|
||||
<Message>There is no ACTIVE Load Balancer named 'foolb'</Message>
|
||||
</Error>
|
||||
<RequestId>2d9fe4a5-5697-11e2-9415-e325c02171d7</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
|
||||
var AddTagsSuccessResponse = `
|
||||
<AddTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06- 01/">
|
||||
<AddTagsResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>360e81f7-1100-11e4-b6ed-0f30SOME-SAUCY-EXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</AddTagsResponse>
|
||||
`
|
||||
|
||||
var TagsBadRequest = `
|
||||
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>InvalidParameterValue</Code>
|
||||
<Message>An invalid or out-of-range value was supplied for the input parameter.</Message>
|
||||
</Error>
|
||||
<RequestId>terrible-request-id</RequestId>
|
||||
</ErrorResponse>
|
||||
`
|
||||
|
||||
var RemoveTagsSuccessResponse = `
|
||||
<RemoveTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
|
||||
<RemoveTagsResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>83c88b9d-12b7-11e3-8b82-87b12DIFFEXAMPLE</RequestId>
|
||||
</ResponseMetadata>
|
||||
</RemoveTagsResponse>
|
||||
`
|
||||
35
vendor/github.com/goamz/goamz/elb/sign.go
сгенерированный
поставляемый
35
vendor/github.com/goamz/goamz/elb/sign.go
сгенерированный
поставляемый
@@ -1,35 +0,0 @@
|
||||
package elb
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
func sign(auth aws.Auth, method, path string, params map[string]string, host string) {
|
||||
params["AWSAccessKeyId"] = auth.AccessKey
|
||||
params["SignatureVersion"] = "2"
|
||||
params["SignatureMethod"] = "HmacSHA256"
|
||||
|
||||
var keys, sarray []string
|
||||
for k := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(params[k]))
|
||||
}
|
||||
joined := strings.Join(sarray, "&")
|
||||
payload := method + "\n" + host + "\n" + path + "\n" + joined
|
||||
hash := hmac.New(sha256.New, []byte(auth.SecretKey))
|
||||
hash.Write([]byte(payload))
|
||||
signature := make([]byte, b64.EncodedLen(hash.Size()))
|
||||
b64.Encode(signature, hash.Sum(nil))
|
||||
|
||||
params["Signature"] = string(signature)
|
||||
}
|
||||
66
vendor/github.com/goamz/goamz/elb/sign_test.go
сгенерированный
поставляемый
66
vendor/github.com/goamz/goamz/elb/sign_test.go
сгенерированный
поставляемый
@@ -1,66 +0,0 @@
|
||||
package elb_test
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/elb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
var testAuth = aws.Auth{AccessKey: "user", SecretKey: "secret"}
|
||||
|
||||
func (s *S) TestBasicSignature(c *C) {
|
||||
params := map[string]string{}
|
||||
elb.Sign(testAuth, "GET", "/path", params, "localhost")
|
||||
c.Assert(params["SignatureVersion"], Equals, "2")
|
||||
c.Assert(params["SignatureMethod"], Equals, "HmacSHA256")
|
||||
expected := "6lSe5QyXum0jMVc7cOUz32/52ZnL7N5RyKRk/09yiK4="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestParamSignature(c *C) {
|
||||
params := map[string]string{
|
||||
"param1": "value1",
|
||||
"param2": "value2",
|
||||
"param3": "value3",
|
||||
}
|
||||
elb.Sign(testAuth, "GET", "/path", params, "localhost")
|
||||
expected := "XWOR4+0lmK8bD8CGDGZ4kfuSPbb2JibLJiCl/OPu1oU="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestManyParams(c *C) {
|
||||
params := map[string]string{
|
||||
"param1": "value10",
|
||||
"param2": "value2",
|
||||
"param3": "value3",
|
||||
"param4": "value4",
|
||||
"param5": "value5",
|
||||
"param6": "value6",
|
||||
"param7": "value7",
|
||||
"param8": "value8",
|
||||
"param9": "value9",
|
||||
"param10": "value1",
|
||||
}
|
||||
elb.Sign(testAuth, "GET", "/path", params, "localhost")
|
||||
expected := "di0sjxIvezUgQ1SIL6i+C/H8lL+U0CQ9frLIak8jkVg="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestEscaping(c *C) {
|
||||
params := map[string]string{"Nonce": "+ +"}
|
||||
elb.Sign(testAuth, "GET", "/path", params, "localhost")
|
||||
c.Assert(params["Nonce"], Equals, "+ +")
|
||||
expected := "bqffDELReIqwjg/W0DnsnVUmfLK4wXVLO4/LuG+1VFA="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
|
||||
func (s *S) TestSignatureExample1(c *C) {
|
||||
params := map[string]string{
|
||||
"Timestamp": "2009-02-01T12:53:20+00:00",
|
||||
"Version": "2007-11-07",
|
||||
"Action": "ListDomains",
|
||||
}
|
||||
elb.Sign(aws.Auth{AccessKey: "access", SecretKey: "secret"}, "GET", "/", params, "sdb.amazonaws.com")
|
||||
expected := "okj96/5ucWBSc1uR2zXVfm6mDHtgfNv657rRtt/aunQ="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
119
vendor/github.com/goamz/goamz/elb/suite_test.go
сгенерированный
поставляемый
119
vendor/github.com/goamz/goamz/elb/suite_test.go
сгенерированный
поставляемый
@@ -1,119 +0,0 @@
|
||||
package elb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
type HTTPSuite struct{}
|
||||
|
||||
var testServer = NewTestHTTPServer("http://localhost:4444", 5*time.Second)
|
||||
|
||||
func (s *HTTPSuite) SetUpSuite(c *C) {
|
||||
testServer.Start()
|
||||
}
|
||||
|
||||
func (s *HTTPSuite) TearDownTest(c *C) {
|
||||
testServer.FlushRequests()
|
||||
}
|
||||
|
||||
type TestHTTPServer struct {
|
||||
URL string
|
||||
Timeout time.Duration
|
||||
started bool
|
||||
request chan *http.Request
|
||||
response chan *testResponse
|
||||
pending chan bool
|
||||
}
|
||||
|
||||
type testResponse struct {
|
||||
Status int
|
||||
Headers map[string]string
|
||||
Body string
|
||||
}
|
||||
|
||||
func NewTestHTTPServer(url string, timeout time.Duration) *TestHTTPServer {
|
||||
return &TestHTTPServer{URL: url, Timeout: timeout}
|
||||
}
|
||||
|
||||
func (s *TestHTTPServer) Start() {
|
||||
if s.started {
|
||||
return
|
||||
}
|
||||
s.started = true
|
||||
|
||||
s.request = make(chan *http.Request, 64)
|
||||
s.response = make(chan *testResponse, 64)
|
||||
s.pending = make(chan bool, 64)
|
||||
|
||||
url, _ := url.Parse(s.URL)
|
||||
go http.ListenAndServe(url.Host, s)
|
||||
|
||||
s.PrepareResponse(202, nil, "Nothing.")
|
||||
for {
|
||||
// Wait for it to be up.
|
||||
resp, err := http.Get(s.URL)
|
||||
if err == nil && resp.StatusCode == 202 {
|
||||
break
|
||||
}
|
||||
time.Sleep(1e8)
|
||||
}
|
||||
s.WaitRequest() // Consume dummy request.
|
||||
}
|
||||
|
||||
// FlushRequests discards requests which were not yet consumed by WaitRequest.
|
||||
func (s *TestHTTPServer) FlushRequests() {
|
||||
for {
|
||||
select {
|
||||
case <-s.request:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TestHTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
s.request <- req
|
||||
var resp *testResponse
|
||||
select {
|
||||
case resp = <-s.response:
|
||||
case <-time.After(s.Timeout):
|
||||
fmt.Fprintf(os.Stderr, "ERROR: Timeout waiting for test to provide response\n")
|
||||
resp = &testResponse{500, nil, ""}
|
||||
}
|
||||
if resp.Headers != nil {
|
||||
h := w.Header()
|
||||
for k, v := range resp.Headers {
|
||||
h.Set(k, v)
|
||||
}
|
||||
}
|
||||
if resp.Status != 0 {
|
||||
w.WriteHeader(resp.Status)
|
||||
}
|
||||
w.Write([]byte(resp.Body))
|
||||
}
|
||||
|
||||
func (s *TestHTTPServer) WaitRequest() *http.Request {
|
||||
select {
|
||||
case req := <-s.request:
|
||||
req.ParseForm()
|
||||
return req
|
||||
case <-time.After(s.Timeout):
|
||||
panic("Timeout waiting for goamz request")
|
||||
}
|
||||
panic("unreached")
|
||||
}
|
||||
|
||||
func (s *TestHTTPServer) PrepareResponse(status int, headers map[string]string, body string) {
|
||||
s.response <- &testResponse{status, headers, body}
|
||||
}
|
||||
66
vendor/github.com/goamz/goamz/exp/mturk/example_test.go
сгенерированный
поставляемый
66
vendor/github.com/goamz/goamz/exp/mturk/example_test.go
сгенерированный
поставляемый
@@ -1,66 +0,0 @@
|
||||
package mturk_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/exp/mturk"
|
||||
)
|
||||
|
||||
var turk *mturk.MTurk
|
||||
|
||||
func ExampleNew() {
|
||||
// These are your AWS tokens. Note that Turk do not support IAM.
|
||||
// So you'll have to use your main profile's tokens.
|
||||
var auth = aws.Auth{AccessKey: "<ACCESS_KEY>", SecretKey: "<SECRET_KEY>"}
|
||||
turk = mturk.New(auth, true) // true to use sandbox mode
|
||||
}
|
||||
|
||||
func Examplemturk_CreateHIT_withExternalQuestion() {
|
||||
question := mturk.ExternalQuestion{
|
||||
ExternalURL: "http://www.amazon.com",
|
||||
FrameHeight: 200,
|
||||
}
|
||||
reward := mturk.Price{
|
||||
Amount: "0.01",
|
||||
CurrencyCode: "USD",
|
||||
}
|
||||
|
||||
hit, err := turk.CreateHIT("title", "description", question, reward, 30, 30, "key1,key2", 3, nil, "annotation")
|
||||
|
||||
if err == nil {
|
||||
fmt.Println(hit)
|
||||
}
|
||||
}
|
||||
|
||||
func Examplemturk_CreateHIT_withHTMLQuestion() {
|
||||
question := mturk.HTMLQuestion{
|
||||
HTMLContent: mturk.HTMLContent{`<![CDATA[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
|
||||
<script type='text/javascript' src='https://s3.amazonaws.com/mturk-public/externalHIT_v1.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<form name='mturk_form' method='post' id='mturk_form' action='https://www.mturk.com/mturk/externalSubmit'>
|
||||
<input type='hidden' value='' name='assignmentId' id='assignmentId'/>
|
||||
<h1>What's up?</h1>
|
||||
<p><textarea name='comment' cols='80' rows='3'></textarea></p>
|
||||
<p><input type='submit' id='submitButton' value='Submit' /></p></form>
|
||||
<script language='Javascript'>turkSetAssignmentID();</script>
|
||||
</body>
|
||||
</html>
|
||||
]]>`},
|
||||
FrameHeight: 200,
|
||||
}
|
||||
reward := mturk.Price{
|
||||
Amount: "0.01",
|
||||
CurrencyCode: "USD",
|
||||
}
|
||||
|
||||
hit, err := turk.CreateHIT("title", "description", question, reward, 30, 30, "key1,key2", 3, nil, "")
|
||||
|
||||
if err == nil {
|
||||
fmt.Println(hit)
|
||||
}
|
||||
}
|
||||
9
vendor/github.com/goamz/goamz/exp/mturk/export_test.go
сгенерированный
поставляемый
9
vendor/github.com/goamz/goamz/exp/mturk/export_test.go
сгенерированный
поставляемый
@@ -1,9 +0,0 @@
|
||||
package mturk
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
)
|
||||
|
||||
func Sign(auth aws.Auth, service, method, timestamp string, params map[string]string) {
|
||||
sign(auth, service, method, timestamp, params)
|
||||
}
|
||||
480
vendor/github.com/goamz/goamz/exp/mturk/mturk.go
сгенерированный
поставляемый
480
vendor/github.com/goamz/goamz/exp/mturk/mturk.go
сгенерированный
поставляемый
@@ -1,480 +0,0 @@
|
||||
//
|
||||
// goamz - Go packages to interact with the Amazon Web Services.
|
||||
//
|
||||
// https://wiki.ubuntu.com/goamz
|
||||
//
|
||||
// Copyright (c) 2011 Canonical Ltd.
|
||||
//
|
||||
// Written by Graham Miller <graham.miller@gmail.com>
|
||||
|
||||
// This package is in an experimental state, and does not currently
|
||||
// follow conventions and style of the rest of goamz or common
|
||||
// Go conventions. It must be polished before it's considered a
|
||||
// first-class package in goamz.
|
||||
package mturk
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"net/http"
|
||||
//"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MTurk struct {
|
||||
aws.Auth
|
||||
URL *url.URL
|
||||
}
|
||||
|
||||
func New(auth aws.Auth, sandbox bool) *MTurk {
|
||||
mt := &MTurk{Auth: auth}
|
||||
var err error
|
||||
if sandbox {
|
||||
mt.URL, err = url.Parse("https://mechanicalturk.sandbox.amazonaws.com/")
|
||||
} else {
|
||||
mt.URL, err = url.Parse("https://mechanicalturk.amazonaws.com/")
|
||||
}
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return mt
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Request dispatching logic.
|
||||
|
||||
// Error encapsulates an error returned by MTurk.
|
||||
type Error struct {
|
||||
StatusCode int // HTTP status code (200, 403, ...)
|
||||
Code string // EC2 error code ("UnsupportedOperation", ...)
|
||||
Message string // The human-oriented error message
|
||||
RequestId string
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
// The request stanza included in several response types, for example
|
||||
// in a "CreateHITResponse". http://goo.gl/qGeKf
|
||||
type xmlRequest struct {
|
||||
RequestId string
|
||||
IsValid string
|
||||
Errors []Error `xml:"Errors>Error"`
|
||||
}
|
||||
|
||||
/*
|
||||
A Price represents an amount of money in a given currency.
|
||||
|
||||
Reference:
|
||||
http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_PriceDataStructureArticle.html
|
||||
*/
|
||||
type Price struct {
|
||||
// The amount of money, as a number. The amount is in the currency specified
|
||||
// by the CurrencyCode. For example, if CurrencyCode is USD, the amount will
|
||||
// be in United States dollars (e.g. 12.75 is $12.75 US).
|
||||
Amount string
|
||||
|
||||
// A code that represents the country and units of the currency. Its value is
|
||||
// Type an ISO 4217 currency code, such as USD for United States dollars.
|
||||
//
|
||||
// Constraints: Currently only USD is supported.
|
||||
CurrencyCode string
|
||||
|
||||
// A textual representation of the price, using symbols and formatting
|
||||
// appropriate for the currency. Symbols are represented using the Unicode
|
||||
// character set. You do not need to specify FormattedPrice in a request.
|
||||
// It is only provided by the service in responses, as a convenience to
|
||||
// your application.
|
||||
FormattedPrice string
|
||||
}
|
||||
|
||||
/*
|
||||
Really just a country string.
|
||||
|
||||
Reference:
|
||||
|
||||
- http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_LocaleDataStructureArticle.html
|
||||
- http://www.iso.org/iso/country_codes/country_codes
|
||||
*/
|
||||
type Locale string
|
||||
|
||||
/*
|
||||
A QualificationRequirement describes a Qualification a Worker must
|
||||
have before the Worker is allowed to accept a HIT. A requirement may optionally
|
||||
state that a Worker must have the Qualification to preview the HIT.
|
||||
|
||||
Reference:
|
||||
|
||||
http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_QualificationRequirementDataStructureArticle.html
|
||||
*/
|
||||
type QualificationRequirement struct {
|
||||
// The ID of the Qualification type for the requirement.
|
||||
// See http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_QualificationRequirementDataStructureArticle.html#ApiReference_QualificationType-IDs
|
||||
QualificationTypeId string
|
||||
|
||||
// The kind of comparison to make against a Qualification's value.
|
||||
// Two values can be compared to see if one value is "LessThan",
|
||||
// "LessThanOrEqualTo", "GreaterThan", "GreaterThanOrEqualTo", "EqualTo", or
|
||||
// "NotEqualTo" the other. A Qualification requirement can also test if a
|
||||
// Qualification "Exists" in the user's profile, regardless of its value.
|
||||
Comparator string
|
||||
|
||||
// The integer value to compare against the Qualification's value.
|
||||
IntegerValue int
|
||||
|
||||
// The locale value to compare against the Qualification's value, if the
|
||||
// Qualification being compared is the locale Qualification.
|
||||
LocaleValue Locale
|
||||
|
||||
// If true, the question data for the HIT will not be shown when a Worker
|
||||
// whose Qualifications do not meet this requirement tries to preview the HIT.
|
||||
// That is, a Worker's Qualifications must meet all of the requirements for
|
||||
// which RequiredToPreview is true in order to preview the HIT.
|
||||
//
|
||||
// If a Worker meets all of the requirements where RequiredToPreview is true
|
||||
// (or if there are no such requirements), but does not meet all of the
|
||||
// requirements for the HIT, the Worker will be allowed to preview the HIT's
|
||||
// question data, but will not be allowed to accept and complete the HIT.
|
||||
RequiredToPreview bool
|
||||
}
|
||||
|
||||
// Data structure holding the contents of an "external"
|
||||
// question. http://goo.gl/NP8Aa
|
||||
type ExternalQuestion struct {
|
||||
XMLName xml.Name `xml:"http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd ExternalQuestion"`
|
||||
ExternalURL string
|
||||
FrameHeight int
|
||||
}
|
||||
|
||||
// Holds the html content of the HTMLQuestion.
|
||||
type HTMLContent struct {
|
||||
Content string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
// Data structure holding the contents of an "html"
|
||||
// question. http://goo.gl/hQn5An
|
||||
type HTMLQuestion struct {
|
||||
XMLName xml.Name `xml:"http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2011-11-11/HTMLQuestion.xsd HTMLQuestion"`
|
||||
HTMLContent HTMLContent
|
||||
|
||||
FrameHeight int
|
||||
}
|
||||
|
||||
// The data structure representing a "human interface task" (HIT)
|
||||
// Currently only supports "external" questions, because Go
|
||||
// structs don't support union types. http://goo.gl/NP8Aa
|
||||
// This type is returned, for example, from SearchHITs
|
||||
// http://goo.gl/PskcX
|
||||
type HIT struct {
|
||||
Request xmlRequest
|
||||
|
||||
HITId string
|
||||
HITTypeId string
|
||||
CreationTime string
|
||||
Title string
|
||||
Description string
|
||||
Keywords string
|
||||
HITStatus string
|
||||
Reward Price
|
||||
LifetimeInSeconds uint
|
||||
AssignmentDurationInSeconds uint
|
||||
MaxAssignments uint
|
||||
AutoApprovalDelayInSeconds uint
|
||||
QualificationRequirement QualificationRequirement
|
||||
Question interface{}
|
||||
RequesterAnnotation string
|
||||
NumberofSimilarHITs uint
|
||||
HITReviewStatus string
|
||||
NumberOfAssignmentsPending uint
|
||||
NumberOfAssignmentsAvailable uint
|
||||
NumberOfAssignmentsCompleted uint
|
||||
}
|
||||
|
||||
// The main data structure returned by SearchHITs
|
||||
// http://goo.gl/PskcX
|
||||
type SearchHITsResult struct {
|
||||
NumResults uint
|
||||
PageNumber uint
|
||||
TotalNumResults uint
|
||||
HITs []HIT `xml:"HIT"`
|
||||
}
|
||||
|
||||
// The wrapper data structure returned by SearchHITs
|
||||
// http://goo.gl/PskcX
|
||||
type SearchHITsResponse struct {
|
||||
RequestId string `xml:"OperationRequest>RequestId"`
|
||||
SearchHITsResult SearchHITsResult
|
||||
}
|
||||
|
||||
// The wrapper data structure returned by CreateHIT
|
||||
// http://goo.gl/PskcX
|
||||
type CreateHITResponse struct {
|
||||
RequestId string `xml:"OperationRequest>RequestId"`
|
||||
HIT HIT
|
||||
}
|
||||
|
||||
type Assignment struct {
|
||||
AssignmentId string
|
||||
WorkerId string
|
||||
HITId string
|
||||
AssignmentStatus string
|
||||
AutoApprovalTime string
|
||||
AcceptTime string
|
||||
SubmitTime string
|
||||
ApprovalTime string
|
||||
Answer string
|
||||
}
|
||||
|
||||
func (a Assignment) Answers() (answers map[string]string) {
|
||||
answers = make(map[string]string)
|
||||
|
||||
decoder := xml.NewDecoder(strings.NewReader(a.Answer))
|
||||
|
||||
for {
|
||||
token, _ := decoder.Token()
|
||||
if token == nil {
|
||||
break
|
||||
}
|
||||
switch startElement := token.(type) {
|
||||
case xml.StartElement:
|
||||
if startElement.Name.Local == "Answer" {
|
||||
var answer struct {
|
||||
QuestionIdentifier string
|
||||
FreeText string
|
||||
}
|
||||
|
||||
decoder.DecodeElement(&answer, &startElement)
|
||||
answers[answer.QuestionIdentifier] = answer.FreeText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type GetAssignmentsForHITResponse struct {
|
||||
RequestId string `xml:"OperationRequest>RequestId"`
|
||||
GetAssignmentsForHITResult struct {
|
||||
Request xmlRequest
|
||||
NumResults uint
|
||||
TotalNumResults uint
|
||||
PageNumber uint
|
||||
Assignment Assignment
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CreateHIT corresponds to the "CreateHIT" operation of the Mechanical Turk
|
||||
API. Currently only supports "external" questions (see "HIT" struct above).
|
||||
|
||||
Here are the detailed description for the parameters:
|
||||
|
||||
title Required. A title should be short and descriptive about the
|
||||
kind of task the HIT contains. On the Amazon Mechanical Turk
|
||||
web site, the HIT title appears in search results, and
|
||||
everywhere the HIT is mentioned.
|
||||
description Required. A description includes detailed information about the
|
||||
kind of task the HIT contains. On the Amazon Mechanical Turk
|
||||
web site, the HIT description appears in the expanded view of
|
||||
search results, and in the HIT and assignment screens. A good
|
||||
description gives the user enough information to evaluate the
|
||||
HIT before accepting it.
|
||||
question Required. The data the person completing the HIT uses to produce
|
||||
the results. Consstraints: Must be a QuestionForm data structure,
|
||||
an ExternalQuestion data structure, or an HTMLQuestion data
|
||||
structure. The XML question data must not be larger than 64
|
||||
kilobytes (65,535 bytes) in size, including whitespace.
|
||||
reward Required. The amount of money the Requester will pay a Worker
|
||||
for successfully completing the HIT.
|
||||
assignmentDurationInSeconds Required. The amount of time, in seconds, that
|
||||
a Worker has to complete the HIT after accepting
|
||||
it. If a Worker does not complete the assignment
|
||||
within the specified duration, the assignment is
|
||||
considered abandoned. If the HIT is still
|
||||
active (that is, its lifetime has not elapsed),
|
||||
the assignment becomes available for other users
|
||||
to find and accept. Valid Values: any integer
|
||||
between 30 (30 seconds) and 31536000 (365 days).
|
||||
lifetimeInSeconds Required. An amount of time, in seconds, after which the
|
||||
HIT is no longer available for users to accept. After
|
||||
the lifetime of the HIT elapses, the HIT no longer
|
||||
appears in HIT searches, even if not all of the
|
||||
assignments for the HIT have been accepted. Valid Values:
|
||||
any integer between 30 (30 seconds) and 31536000 (365 days).
|
||||
keywords One or more words or phrases that describe the HIT,
|
||||
separated by commas. These words are used in searches to
|
||||
find HITs. Constraints: cannot be more than 1,000
|
||||
characters.
|
||||
maxAssignments The number of times the HIT can be accepted and completed
|
||||
before the HIT becomes unavailable. Valid Values: any
|
||||
integer between 1 and 1000000000 (1 billion). Default: 1
|
||||
qualificationRequirement A condition that a Worker's Qualifications must
|
||||
meet before the Worker is allowed to accept and
|
||||
complete the HIT. Constraints: no more than 10
|
||||
QualificationRequirement for each HIT.
|
||||
requesterAnnotation An arbitrary data field. The RequesterAnnotation
|
||||
parameter lets your application attach arbitrary data to
|
||||
the HIT for tracking purposes. For example, the
|
||||
RequesterAnnotation parameter could be an identifier
|
||||
internal to the Requester's application that corresponds
|
||||
with the HIT. Constraints: must not be longer than 255
|
||||
characters in length.
|
||||
|
||||
Reference:
|
||||
http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_CreateHITOperation.html
|
||||
*/
|
||||
func (mt *MTurk) CreateHIT(
|
||||
title, description string,
|
||||
question interface{},
|
||||
reward Price,
|
||||
assignmentDurationInSeconds,
|
||||
lifetimeInSeconds uint,
|
||||
keywords string,
|
||||
maxAssignments uint,
|
||||
qualificationRequirement *QualificationRequirement,
|
||||
requesterAnnotation string) (h *HIT, err error) {
|
||||
|
||||
params := make(map[string]string)
|
||||
params["Title"] = title
|
||||
params["Description"] = description
|
||||
params["Question"], err = xmlEncode(&question)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params["Reward.1.Amount"] = reward.Amount
|
||||
params["Reward.1.CurrencyCode"] = reward.CurrencyCode
|
||||
params["AssignmentDurationInSeconds"] = strconv.FormatUint(uint64(assignmentDurationInSeconds), 10)
|
||||
|
||||
params["LifetimeInSeconds"] = strconv.FormatUint(uint64(lifetimeInSeconds), 10)
|
||||
if keywords != "" {
|
||||
params["Keywords"] = keywords
|
||||
}
|
||||
if maxAssignments != 0 {
|
||||
params["MaxAssignments"] = strconv.FormatUint(uint64(maxAssignments), 10)
|
||||
}
|
||||
if qualificationRequirement != nil {
|
||||
params["QualificationRequirement"], err = xmlEncode(qualificationRequirement)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if requesterAnnotation != "" {
|
||||
params["RequesterAnnotation"] = requesterAnnotation
|
||||
}
|
||||
|
||||
var response CreateHITResponse
|
||||
err = mt.query(params, "CreateHIT", &response)
|
||||
if err == nil {
|
||||
h = &response.HIT
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Corresponds to the "CreateHIT" operation of the Mechanical Turk
|
||||
// API, using an existing "hit type". http://goo.gl/cDBRc Currently only
|
||||
// supports "external" questions (see "HIT" struct above). If
|
||||
// "maxAssignments" or "requesterAnnotation" are the zero value for
|
||||
// their types, they will not be included in the request.
|
||||
func (mt *MTurk) CreateHITOfType(hitTypeId string, q ExternalQuestion, lifetimeInSeconds uint, maxAssignments uint, requesterAnnotation string) (h *HIT, err error) {
|
||||
params := make(map[string]string)
|
||||
params["HITTypeId"] = hitTypeId
|
||||
params["Question"], err = xmlEncode(&q)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params["LifetimeInSeconds"] = strconv.FormatUint(uint64(lifetimeInSeconds), 10)
|
||||
if maxAssignments != 0 {
|
||||
params["MaxAssignments"] = strconv.FormatUint(uint64(maxAssignments), 10)
|
||||
}
|
||||
if requesterAnnotation != "" {
|
||||
params["RequesterAnnotation"] = requesterAnnotation
|
||||
}
|
||||
|
||||
var response CreateHITResponse
|
||||
err = mt.query(params, "CreateHIT", &response)
|
||||
if err == nil {
|
||||
h = &response.HIT
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get the Assignments for a HIT.
|
||||
func (mt *MTurk) GetAssignmentsForHIT(hitId string) (r *Assignment, err error) {
|
||||
params := make(map[string]string)
|
||||
params["HITId"] = hitId
|
||||
var response GetAssignmentsForHITResponse
|
||||
err = mt.query(params, "GetAssignmentsForHIT", &response)
|
||||
if err == nil {
|
||||
r = &response.GetAssignmentsForHITResult.Assignment
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Corresponds to "SearchHITs" operation of Mechanical Turk. http://goo.gl/PskcX
|
||||
// Currenlty supports none of the optional parameters.
|
||||
func (mt *MTurk) SearchHITs() (s *SearchHITsResult, err error) {
|
||||
params := make(map[string]string)
|
||||
var response SearchHITsResponse
|
||||
err = mt.query(params, "SearchHITs", &response)
|
||||
if err == nil {
|
||||
s = &response.SearchHITsResult
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Adds common parameters to the "params" map, signs the request,
|
||||
// adds the signature to the "params" map and sends the request
|
||||
// to the server. It then unmarshals the response in to the "resp"
|
||||
// parameter using xml.Unmarshal()
|
||||
func (mt *MTurk) query(params map[string]string, operation string, resp interface{}) error {
|
||||
service := "AWSMechanicalTurkRequester"
|
||||
timestamp := time.Now().UTC().Format("2006-01-02T15:04:05Z")
|
||||
|
||||
params["AWSAccessKeyId"] = mt.Auth.AccessKey
|
||||
params["Service"] = service
|
||||
params["Timestamp"] = timestamp
|
||||
params["Operation"] = operation
|
||||
|
||||
// make a copy
|
||||
url := *mt.URL
|
||||
|
||||
sign(mt.Auth, service, operation, timestamp, params)
|
||||
url.RawQuery = multimap(params).Encode()
|
||||
r, err := http.Get(url.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//dump, _ := httputil.DumpResponse(r, true)
|
||||
//println("DUMP:\n", string(dump))
|
||||
if r.StatusCode != 200 {
|
||||
return errors.New(fmt.Sprintf("%d: unexpected status code", r.StatusCode))
|
||||
}
|
||||
dec := xml.NewDecoder(r.Body)
|
||||
err = dec.Decode(resp)
|
||||
r.Body.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func multimap(p map[string]string) url.Values {
|
||||
q := make(url.Values, len(p))
|
||||
for k, v := range p {
|
||||
q[k] = []string{v}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func xmlEncode(i interface{}) (s string, err error) {
|
||||
var buf []byte
|
||||
buf, err = xml.Marshal(i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = string(buf)
|
||||
return
|
||||
}
|
||||
170
vendor/github.com/goamz/goamz/exp/mturk/mturk_test.go
сгенерированный
поставляемый
170
vendor/github.com/goamz/goamz/exp/mturk/mturk_test.go
сгенерированный
поставляемый
@@ -1,170 +0,0 @@
|
||||
package mturk_test
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/exp/mturk"
|
||||
"github.com/goamz/goamz/testutil"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
type S struct {
|
||||
mturk *mturk.MTurk
|
||||
}
|
||||
|
||||
var testServer = testutil.NewHTTPServer()
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
testServer.Start()
|
||||
auth := aws.Auth{AccessKey: "abc", SecretKey: "123"}
|
||||
u, err := url.Parse(testServer.URL)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
s.mturk = &mturk.MTurk{
|
||||
Auth: auth,
|
||||
URL: u,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
testServer.Flush()
|
||||
}
|
||||
|
||||
func (s *S) TestCreateHITExternalQuestion(c *C) {
|
||||
testServer.Response(200, nil, BasicHitResponse)
|
||||
|
||||
question := mturk.ExternalQuestion{
|
||||
ExternalURL: "http://www.amazon.com",
|
||||
FrameHeight: 200,
|
||||
}
|
||||
reward := mturk.Price{
|
||||
Amount: "0.01",
|
||||
CurrencyCode: "USD",
|
||||
}
|
||||
hit, err := s.mturk.CreateHIT("title", "description", question, reward, 1, 2, "key1,key2", 3, nil, "annotation")
|
||||
|
||||
testServer.WaitRequest()
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(hit, NotNil)
|
||||
|
||||
c.Assert(hit.HITId, Equals, "28J4IXKO2L927XKJTHO34OCDNASCDW")
|
||||
c.Assert(hit.HITTypeId, Equals, "2XZ7D1X3V0FKQVW7LU51S7PKKGFKDF")
|
||||
}
|
||||
|
||||
func (s *S) TestCreateHITHTMLQuestion(c *C) {
|
||||
testServer.Response(200, nil, BasicHitResponse)
|
||||
|
||||
question := mturk.HTMLQuestion{
|
||||
HTMLContent: mturk.HTMLContent{`<![CDATA[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
|
||||
<script type='text/javascript' src='https://s3.amazonaws.com/mturk-public/externalHIT_v1.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<form name='mturk_form' method='post' id='mturk_form' action='https://www.mturk.com/mturk/externalSubmit'>
|
||||
<input type='hidden' value='' name='assignmentId' id='assignmentId'/>
|
||||
<h1>What's up?</h1>
|
||||
<p><textarea name='comment' cols='80' rows='3'></textarea></p>
|
||||
<p><input type='submit' id='submitButton' value='Submit' /></p></form>
|
||||
<script language='Javascript'>turkSetAssignmentID();</script>
|
||||
</body>
|
||||
</html>
|
||||
]]>`},
|
||||
FrameHeight: 200,
|
||||
}
|
||||
reward := mturk.Price{
|
||||
Amount: "0.01",
|
||||
CurrencyCode: "USD",
|
||||
}
|
||||
hit, err := s.mturk.CreateHIT("title", "description", question, reward, 1, 2, "key1,key2", 3, nil, "annotation")
|
||||
|
||||
testServer.WaitRequest()
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(hit, NotNil)
|
||||
|
||||
c.Assert(hit.HITId, Equals, "28J4IXKO2L927XKJTHO34OCDNASCDW")
|
||||
c.Assert(hit.HITTypeId, Equals, "2XZ7D1X3V0FKQVW7LU51S7PKKGFKDF")
|
||||
}
|
||||
|
||||
func (s *S) TestSearchHITs(c *C) {
|
||||
testServer.Response(200, nil, SearchHITResponse)
|
||||
|
||||
hitResult, err := s.mturk.SearchHITs()
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(hitResult, NotNil)
|
||||
|
||||
c.Assert(hitResult.NumResults, Equals, uint(1))
|
||||
c.Assert(hitResult.PageNumber, Equals, uint(1))
|
||||
c.Assert(hitResult.TotalNumResults, Equals, uint(1))
|
||||
|
||||
c.Assert(len(hitResult.HITs), Equals, 1)
|
||||
c.Assert(hitResult.HITs[0].HITId, Equals, "2BU26DG67D1XTE823B3OQ2JF2XWF83")
|
||||
c.Assert(hitResult.HITs[0].HITTypeId, Equals, "22OWJ5OPB0YV6IGL5727KP9U38P5XR")
|
||||
c.Assert(hitResult.HITs[0].CreationTime, Equals, "2011-12-28T19:56:20Z")
|
||||
c.Assert(hitResult.HITs[0].Title, Equals, "test hit")
|
||||
c.Assert(hitResult.HITs[0].Description, Equals, "please disregard, testing only")
|
||||
c.Assert(hitResult.HITs[0].HITStatus, Equals, "Reviewable")
|
||||
c.Assert(hitResult.HITs[0].MaxAssignments, Equals, uint(1))
|
||||
c.Assert(hitResult.HITs[0].Reward.Amount, Equals, "0.01")
|
||||
c.Assert(hitResult.HITs[0].Reward.CurrencyCode, Equals, "USD")
|
||||
c.Assert(hitResult.HITs[0].AutoApprovalDelayInSeconds, Equals, uint(2592000))
|
||||
c.Assert(hitResult.HITs[0].AssignmentDurationInSeconds, Equals, uint(30))
|
||||
c.Assert(hitResult.HITs[0].NumberOfAssignmentsPending, Equals, uint(0))
|
||||
c.Assert(hitResult.HITs[0].NumberOfAssignmentsAvailable, Equals, uint(1))
|
||||
c.Assert(hitResult.HITs[0].NumberOfAssignmentsCompleted, Equals, uint(0))
|
||||
}
|
||||
|
||||
func (s *S) TestGetAssignmentsForHIT_NoAnswer(c *C) {
|
||||
testServer.Response(200, nil, GetAssignmentsForHITNoAnswerResponse)
|
||||
|
||||
assignment, err := s.mturk.GetAssignmentsForHIT("emptyassignment")
|
||||
|
||||
testServer.WaitRequest()
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(assignment, NotNil)
|
||||
|
||||
c.Assert(assignment.HITId, Equals, "")
|
||||
}
|
||||
|
||||
func (s *S) TestGetAssignmentsForHIT_Answer(c *C) {
|
||||
testServer.Response(200, nil, GetAssignmentsForHITAnswerResponse)
|
||||
|
||||
assignment, err := s.mturk.GetAssignmentsForHIT("emptyassignment")
|
||||
|
||||
testServer.WaitRequest()
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(assignment, NotNil)
|
||||
|
||||
c.Assert(assignment.AssignmentId, Equals, "2QKNTL0XULRGFAQWUWDD05FP94V2O3")
|
||||
c.Assert(assignment.WorkerId, Equals, "A1ZUQ2YDM61713")
|
||||
c.Assert(assignment.HITId, Equals, "2W36VCPWZ9RN5DX1MBJ7VN3D6WEPAM")
|
||||
c.Assert(assignment.AssignmentStatus, Equals, "Submitted")
|
||||
c.Assert(assignment.AutoApprovalTime, Equals, "2014-02-26T09:39:48Z")
|
||||
c.Assert(assignment.AcceptTime, Equals, "2014-01-27T09:39:38Z")
|
||||
c.Assert(assignment.SubmitTime, Equals, "2014-01-27T09:39:48Z")
|
||||
c.Assert(assignment.ApprovalTime, Equals, "")
|
||||
|
||||
answers := assignment.Answers()
|
||||
c.Assert(len(answers), Equals, 4)
|
||||
c.Assert(answers["tags"], Equals, "asd")
|
||||
c.Assert(answers["text_in_image"], Equals, "asd")
|
||||
c.Assert(answers["is_pattern"], Equals, "yes")
|
||||
c.Assert(answers["is_map"], Equals, "yes")
|
||||
}
|
||||
36
vendor/github.com/goamz/goamz/exp/mturk/responses_test.go
сгенерированный
поставляемый
36
vendor/github.com/goamz/goamz/exp/mturk/responses_test.go
сгенерированный
поставляемый
@@ -1,36 +0,0 @@
|
||||
package mturk_test
|
||||
|
||||
var BasicHitResponse = `<?xml version="1.0"?>
|
||||
<CreateHITResponse><OperationRequest><RequestId>643b794b-66b6-4427-bb8a-4d3df5c9a20e</RequestId></OperationRequest><HIT><Request><IsValid>True</IsValid></Request><HITId>28J4IXKO2L927XKJTHO34OCDNASCDW</HITId><HITTypeId>2XZ7D1X3V0FKQVW7LU51S7PKKGFKDF</HITTypeId></HIT></CreateHITResponse>
|
||||
`
|
||||
|
||||
var SearchHITResponse = `<?xml version="1.0"?>
|
||||
<SearchHITsResponse><OperationRequest><RequestId>38862d9c-f015-4177-a2d3-924110a9d6f2</RequestId></OperationRequest><SearchHITsResult><Request><IsValid>True</IsValid></Request><NumResults>1</NumResults><TotalNumResults>1</TotalNumResults><PageNumber>1</PageNumber><HIT><HITId>2BU26DG67D1XTE823B3OQ2JF2XWF83</HITId><HITTypeId>22OWJ5OPB0YV6IGL5727KP9U38P5XR</HITTypeId><CreationTime>2011-12-28T19:56:20Z</CreationTime><Title>test hit</Title><Description>please disregard, testing only</Description><HITStatus>Reviewable</HITStatus><MaxAssignments>1</MaxAssignments><Reward><Amount>0.01</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$0.01</FormattedPrice></Reward><AutoApprovalDelayInSeconds>2592000</AutoApprovalDelayInSeconds><Expiration>2011-12-28T19:56:50Z</Expiration><AssignmentDurationInSeconds>30</AssignmentDurationInSeconds><NumberOfAssignmentsPending>0</NumberOfAssignmentsPending><NumberOfAssignmentsAvailable>1</NumberOfAssignmentsAvailable><NumberOfAssignmentsCompleted>0</NumberOfAssignmentsCompleted></HIT></SearchHITsResult></SearchHITsResponse>
|
||||
`
|
||||
|
||||
var GetAssignmentsForHITNoAnswerResponse = `<?xml version="1.0"?>
|
||||
<GetAssignmentsForHITResponse><OperationRequest><RequestId>536934be-a35b-4e4e-9822-72fbf36d5862</RequestId></OperationRequest><GetAssignmentsForHITResult><Request><IsValid>True</IsValid></Request><NumResults>0</NumResults><TotalNumResults>0</TotalNumResults><PageNumber>1</PageNumber></GetAssignmentsForHITResult></GetAssignmentsForHITResponse>
|
||||
`
|
||||
|
||||
var GetAssignmentsForHITAnswerResponse = `<?xml version="1.0"?>
|
||||
<GetAssignmentsForHITResponse><OperationRequest><RequestId>2f113bdf-2e3e-4c5a-a396-3ed01384ecb9</RequestId></OperationRequest><GetAssignmentsForHITResult><Request><IsValid>True</IsValid></Request><NumResults>1</NumResults><TotalNumResults>1</TotalNumResults><PageNumber>1</PageNumber><Assignment><AssignmentId>2QKNTL0XULRGFAQWUWDD05FP94V2O3</AssignmentId><WorkerId>A1ZUQ2YDM61713</WorkerId><HITId>2W36VCPWZ9RN5DX1MBJ7VN3D6WEPAM</HITId><AssignmentStatus>Submitted</AssignmentStatus><AutoApprovalTime>2014-02-26T09:39:48Z</AutoApprovalTime><AcceptTime>2014-01-27T09:39:38Z</AcceptTime><SubmitTime>2014-01-27T09:39:48Z</SubmitTime><Answer><?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<QuestionFormAnswers xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionFormAnswers.xsd">
|
||||
<Answer>
|
||||
<QuestionIdentifier>tags</QuestionIdentifier>
|
||||
<FreeText>asd</FreeText>
|
||||
</Answer>
|
||||
<Answer>
|
||||
<QuestionIdentifier>text_in_image</QuestionIdentifier>
|
||||
<FreeText>asd</FreeText>
|
||||
</Answer>
|
||||
<Answer>
|
||||
<QuestionIdentifier>is_pattern</QuestionIdentifier>
|
||||
<FreeText>yes</FreeText>
|
||||
</Answer>
|
||||
<Answer>
|
||||
<QuestionIdentifier>is_map</QuestionIdentifier>
|
||||
<FreeText>yes</FreeText>
|
||||
</Answer>
|
||||
</QuestionFormAnswers>
|
||||
</Answer></Assignment></GetAssignmentsForHITResult></GetAssignmentsForHITResponse>
|
||||
`
|
||||
22
vendor/github.com/goamz/goamz/exp/mturk/sign.go
сгенерированный
поставляемый
22
vendor/github.com/goamz/goamz/exp/mturk/sign.go
сгенерированный
поставляемый
@@ -1,22 +0,0 @@
|
||||
package mturk
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"github.com/goamz/goamz/aws"
|
||||
)
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Mechanical Turk signing (http://goo.gl/wrzfn)
|
||||
func sign(auth aws.Auth, service, method, timestamp string, params map[string]string) {
|
||||
payload := service + method + timestamp
|
||||
hash := hmac.New(sha1.New, []byte(auth.SecretKey))
|
||||
hash.Write([]byte(payload))
|
||||
signature := make([]byte, b64.EncodedLen(hash.Size()))
|
||||
b64.Encode(signature, hash.Sum(nil))
|
||||
|
||||
params["Signature"] = string(signature)
|
||||
}
|
||||
19
vendor/github.com/goamz/goamz/exp/mturk/sign_test.go
сгенерированный
поставляемый
19
vendor/github.com/goamz/goamz/exp/mturk/sign_test.go
сгенерированный
поставляемый
@@ -1,19 +0,0 @@
|
||||
package mturk_test
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/exp/mturk"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
// Mechanical Turk REST authentication docs: http://goo.gl/wrzfn
|
||||
|
||||
var testAuth = aws.Auth{AccessKey: "user", SecretKey: "secret"}
|
||||
|
||||
// == fIJy9wCApBNL2R4J2WjJGtIBFX4=
|
||||
func (s *S) TestBasicSignature(c *C) {
|
||||
params := map[string]string{}
|
||||
mturk.Sign(testAuth, "AWSMechanicalTurkRequester", "CreateHIT", "2012-02-16T20:30:47Z", params)
|
||||
expected := "b/TnvzrdeD/L/EyzdFrznPXhido="
|
||||
c.Assert(params["Signature"], Equals, expected)
|
||||
}
|
||||
9
vendor/github.com/goamz/goamz/exp/sdb/export_test.go
сгенерированный
поставляемый
9
vendor/github.com/goamz/goamz/exp/sdb/export_test.go
сгенерированный
поставляемый
@@ -1,9 +0,0 @@
|
||||
package sdb
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
)
|
||||
|
||||
func Sign(auth aws.Auth, method, path string, params map[string][]string, headers map[string][]string) {
|
||||
sign(auth, method, path, params, headers)
|
||||
}
|
||||
120
vendor/github.com/goamz/goamz/exp/sdb/responses_test.go
сгенерированный
поставляемый
120
vendor/github.com/goamz/goamz/exp/sdb/responses_test.go
сгенерированный
поставляемый
@@ -1,120 +0,0 @@
|
||||
package sdb_test
|
||||
|
||||
var TestCreateDomainXmlOK = `
|
||||
<?xml version="1.0"?>
|
||||
<CreateDomainResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>63264005-7a5f-e01a-a224-395c63b89f6d</RequestId>
|
||||
<BoxUsage>0.0055590279</BoxUsage>
|
||||
</ResponseMetadata>
|
||||
</CreateDomainResponse>
|
||||
`
|
||||
|
||||
var TestListDomainsXmlOK = `
|
||||
<?xml version="1.0"?>
|
||||
<ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
|
||||
<ListDomainsResult>
|
||||
<DomainName>Account</DomainName>
|
||||
<DomainName>Domain</DomainName>
|
||||
<DomainName>Record</DomainName>
|
||||
</ListDomainsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>15fcaf55-9914-63c2-21f3-951e31193790</RequestId>
|
||||
<BoxUsage>0.0000071759</BoxUsage>
|
||||
</ResponseMetadata>
|
||||
</ListDomainsResponse>
|
||||
`
|
||||
|
||||
var TestListDomainsWithNextTokenXmlOK = `
|
||||
<?xml version="1.0"?>
|
||||
<ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
|
||||
<ListDomainsResult>
|
||||
<DomainName>Domain1-200706011651</DomainName>
|
||||
<DomainName>Domain2-200706011652</DomainName>
|
||||
<NextToken>TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY=</NextToken>
|
||||
</ListDomainsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>eb13162f-1b95-4511-8b12-489b86acfd28</RequestId>
|
||||
<BoxUsage>0.0000219907</BoxUsage>
|
||||
</ResponseMetadata>
|
||||
</ListDomainsResponse>
|
||||
`
|
||||
|
||||
var TestDeleteDomainXmlOK = `
|
||||
<?xml version="1.0"?>
|
||||
<DeleteDomainResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>039e1e25-9a64-2a74-93da-2fda36122a97</RequestId>
|
||||
<BoxUsage>0.0055590278</BoxUsage>
|
||||
</ResponseMetadata>
|
||||
</DeleteDomainResponse>
|
||||
`
|
||||
|
||||
var TestDomainMetadataXmlNoSuchDomain = `
|
||||
<?xml version="1.0"?>
|
||||
<Response xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
|
||||
<Errors>
|
||||
<Error>
|
||||
<Code>NoSuchDomain</Code>
|
||||
<Message>The specified domain does not exist.</Message>
|
||||
<BoxUsage>0.0000071759</BoxUsage>
|
||||
</Error>
|
||||
</Errors>
|
||||
<RequestID>e050cea2-a772-f90e-2cb0-98ebd42c2898</RequestID>
|
||||
</Response>
|
||||
`
|
||||
|
||||
var TestPutAttrsXmlOK = `
|
||||
<?xml version="1.0"?>
|
||||
<PutAttributesResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>490206ce-8292-456c-a00f-61b335eb202b</RequestId>
|
||||
<BoxUsage>0.0000219907</BoxUsage>
|
||||
</ResponseMetadata>
|
||||
</PutAttributesResponse>
|
||||
`
|
||||
|
||||
var TestAttrsXmlOK = `
|
||||
<?xml version="1.0"?>
|
||||
<GetAttributesResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
|
||||
<GetAttributesResult>
|
||||
<Attribute><Name>Color</Name><Value>Blue</Value></Attribute>
|
||||
<Attribute><Name>Size</Name><Value>Med</Value></Attribute>
|
||||
</GetAttributesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId>
|
||||
<BoxUsage>0.0000219942</BoxUsage>
|
||||
</ResponseMetadata>
|
||||
</GetAttributesResponse>
|
||||
`
|
||||
|
||||
var TestSelectXmlOK = `
|
||||
<?xml version="1.0"?>
|
||||
<SelectResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
|
||||
<SelectResult>
|
||||
<Item>
|
||||
<Name>Item_03</Name>
|
||||
<Attribute><Name>Category</Name><Value>Clothes</Value></Attribute>
|
||||
<Attribute><Name>Subcategory</Name><Value>Pants</Value></Attribute>
|
||||
<Attribute><Name>Name</Name><Value>Sweatpants</Value></Attribute>
|
||||
<Attribute><Name>Color</Name><Value>Blue</Value></Attribute>
|
||||
<Attribute><Name>Color</Name><Value>Yellow</Value></Attribute>
|
||||
<Attribute><Name>Color</Name><Value>Pink</Value></Attribute>
|
||||
<Attribute><Name>Size</Name><Value>Large</Value></Attribute>
|
||||
</Item>
|
||||
<Item>
|
||||
<Name>Item_06</Name>
|
||||
<Attribute><Name>Category</Name><Value>Motorcycle Parts</Value></Attribute>
|
||||
<Attribute><Name>Subcategory</Name><Value>Bodywork</Value></Attribute>
|
||||
<Attribute><Name>Name</Name><Value>Fender Eliminator</Value></Attribute>
|
||||
<Attribute><Name>Color</Name><Value>Blue</Value></Attribute>
|
||||
<Attribute><Name>Make</Name><Value>Yamaha</Value></Attribute>
|
||||
<Attribute><Name>Model</Name><Value>R1</Value></Attribute>
|
||||
</Item>
|
||||
</SelectResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId>
|
||||
<BoxUsage>0.0000219907</BoxUsage>
|
||||
</ResponseMetadata>
|
||||
</SelectResponse>
|
||||
`
|
||||
413
vendor/github.com/goamz/goamz/exp/sdb/sdb.go
сгенерированный
поставляемый
413
vendor/github.com/goamz/goamz/exp/sdb/sdb.go
сгенерированный
поставляемый
@@ -1,413 +0,0 @@
|
||||
//
|
||||
// goamz - Go packages to interact with the Amazon Web Services.
|
||||
//
|
||||
// https://wiki.ubuntu.com/goamz
|
||||
//
|
||||
// Copyright (c) 2011 AppsAttic Ltd.
|
||||
//
|
||||
// sdb package written by:
|
||||
//
|
||||
// Andrew Chilton <chilts@appsattic.com>
|
||||
// Brad Rydzewski <brad.rydzewski@gmail.com>
|
||||
|
||||
// This package is in an experimental state, and does not currently
|
||||
// follow conventions and style of the rest of goamz or common
|
||||
// Go conventions. It must be polished before it's considered a
|
||||
// first-class package in goamz.
|
||||
package sdb
|
||||
|
||||
// BUG: SelectResp isn't properly organized. It must change.
|
||||
|
||||
//
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const debug = false
|
||||
|
||||
// The SDB type encapsulates operations with a specific SimpleDB region.
|
||||
type SDB struct {
|
||||
aws.Auth
|
||||
aws.Region
|
||||
private byte // Reserve the right of using private data.
|
||||
}
|
||||
|
||||
// New creates a new SDB.
|
||||
func New(auth aws.Auth, region aws.Region) *SDB {
|
||||
return &SDB{auth, region, 0}
|
||||
}
|
||||
|
||||
// The Domain type represents a collection of items that are described
|
||||
// by name-value attributes.
|
||||
type Domain struct {
|
||||
*SDB
|
||||
Name string
|
||||
}
|
||||
|
||||
// Domain returns a Domain with the given name.
|
||||
func (sdb *SDB) Domain(name string) *Domain {
|
||||
return &Domain{sdb, name}
|
||||
}
|
||||
|
||||
// The Item type represent individual objects that contain one or more
|
||||
// name-value attributes stored within a SDB Domain as rows.
|
||||
type Item struct {
|
||||
*SDB
|
||||
*Domain
|
||||
Name string
|
||||
}
|
||||
|
||||
// Item returns an Item with the given name.
|
||||
func (domain *Domain) Item(name string) *Item {
|
||||
return &Item{domain.SDB, domain, name}
|
||||
}
|
||||
|
||||
// The Attr type represent categories of data that can be assigned to items.
|
||||
type Attr struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Service-level operations.
|
||||
|
||||
// --- ListDomains
|
||||
|
||||
// Response to a ListDomains request.
|
||||
//
|
||||
// See http://goo.gl/3u0Cf for more details.
|
||||
type ListDomainsResp struct {
|
||||
Domains []string `xml:"ListDomainsResult>DomainName"`
|
||||
NextToken string `xml:"ListDomainsResult>NextToken"`
|
||||
ResponseMetadata ResponseMetadata
|
||||
}
|
||||
|
||||
// ListDomains lists all domains in sdb.
|
||||
//
|
||||
// See http://goo.gl/Dsw15 for more details.
|
||||
func (sdb *SDB) ListDomains() (resp *ListDomainsResp, err error) {
|
||||
return sdb.ListDomainsN(0, "")
|
||||
}
|
||||
|
||||
// ListDomainsN lists domains in sdb up to maxDomains.
|
||||
// If nextToken is not empty, domains listed will start at the given token.
|
||||
//
|
||||
// See http://goo.gl/Dsw15 for more details.
|
||||
func (sdb *SDB) ListDomainsN(maxDomains int, nextToken string) (resp *ListDomainsResp, err error) {
|
||||
params := makeParams("ListDomains")
|
||||
if maxDomains != 0 {
|
||||
params["MaxNumberOfDomains"] = []string{strconv.Itoa(maxDomains)}
|
||||
}
|
||||
if nextToken != "" {
|
||||
params["NextToken"] = []string{nextToken}
|
||||
}
|
||||
resp = &ListDomainsResp{}
|
||||
err = sdb.query(nil, nil, params, nil, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// --- SelectExpression
|
||||
|
||||
// Response to a Select request.
|
||||
//
|
||||
// See http://goo.gl/GTsSZ for more details.
|
||||
type SelectResp struct {
|
||||
Items []struct {
|
||||
Name string
|
||||
Attrs []Attr `xml:"Attribute"`
|
||||
} `xml:"SelectResult>Item"`
|
||||
ResponseMetadata ResponseMetadata
|
||||
}
|
||||
|
||||
// Select returns a set of items and attributes that match expr.
|
||||
// Select is similar to the standard SQL SELECT statement.
|
||||
//
|
||||
// See http://goo.gl/GTsSZ for more details.
|
||||
func (sdb *SDB) Select(expr string, consistent bool) (resp *SelectResp, err error) {
|
||||
resp = &SelectResp{}
|
||||
params := makeParams("Select")
|
||||
params["SelectExpression"] = []string{expr}
|
||||
if consistent {
|
||||
params["ConsistentRead"] = []string{"true"}
|
||||
}
|
||||
err = sdb.query(nil, nil, params, nil, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Domain-level operations.
|
||||
|
||||
// --- CreateDomain
|
||||
|
||||
// CreateDomain creates a new domain.
|
||||
//
|
||||
// See http://goo.gl/jDjGH for more details.
|
||||
func (domain *Domain) CreateDomain() (resp *SimpleResp, err error) {
|
||||
params := makeParams("CreateDomain")
|
||||
resp = &SimpleResp{}
|
||||
err = domain.SDB.query(domain, nil, params, nil, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteDomain deletes an existing domain.
|
||||
//
|
||||
// See http://goo.gl/S0dCL for more details.
|
||||
func (domain *Domain) DeleteDomain() (resp *SimpleResp, err error) {
|
||||
params := makeParams("DeleteDomain")
|
||||
resp = &SimpleResp{}
|
||||
err = domain.SDB.query(domain, nil, params, nil, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Item-level operations.
|
||||
|
||||
type PutAttrs struct {
|
||||
attrs []Attr
|
||||
expected []Attr
|
||||
replace map[string]bool
|
||||
missing map[string]bool
|
||||
}
|
||||
|
||||
func (pa *PutAttrs) Add(name, value string) {
|
||||
pa.attrs = append(pa.attrs, Attr{name, value})
|
||||
}
|
||||
|
||||
func (pa *PutAttrs) Replace(name, value string) {
|
||||
pa.Add(name, value)
|
||||
if pa.replace == nil {
|
||||
pa.replace = make(map[string]bool)
|
||||
}
|
||||
pa.replace[name] = true
|
||||
}
|
||||
|
||||
// The PutAttrs request will only succeed if the existing
|
||||
// item in SimpleDB contains a matching name / value pair.
|
||||
func (pa *PutAttrs) IfValue(name, value string) {
|
||||
pa.expected = append(pa.expected, Attr{name, value})
|
||||
}
|
||||
|
||||
// Flag to test the existence of an attribute while performing
|
||||
// conditional updates. X can be any positive integer or 0.
|
||||
//
|
||||
// This should set Expected.N.Name=name and Expected.N.Exists=false
|
||||
func (pa *PutAttrs) IfMissing(name string) {
|
||||
if pa.missing == nil {
|
||||
pa.missing = make(map[string]bool)
|
||||
}
|
||||
pa.missing[name] = true
|
||||
}
|
||||
|
||||
// PutAttrs adds attrs to item.
|
||||
//
|
||||
// See http://goo.gl/yTAV4 for more details.
|
||||
func (item *Item) PutAttrs(attrs *PutAttrs) (resp *SimpleResp, err error) {
|
||||
params := makeParams("PutAttributes")
|
||||
resp = &SimpleResp{}
|
||||
|
||||
// copy these attrs over to the parameters
|
||||
itemNum := 1
|
||||
for _, attr := range attrs.attrs {
|
||||
itemNumStr := strconv.Itoa(itemNum)
|
||||
|
||||
// do the name, value and replace
|
||||
params["Attribute."+itemNumStr+".Name"] = []string{attr.Name}
|
||||
params["Attribute."+itemNumStr+".Value"] = []string{attr.Value}
|
||||
|
||||
if _, ok := attrs.replace[attr.Name]; ok {
|
||||
params["Attribute."+itemNumStr+".Replace"] = []string{"true"}
|
||||
}
|
||||
|
||||
itemNum++
|
||||
}
|
||||
|
||||
//append expected values to params
|
||||
expectedNum := 1
|
||||
for _, attr := range attrs.expected {
|
||||
expectedNumStr := strconv.Itoa(expectedNum)
|
||||
params["Expected."+expectedNumStr+".Name"] = []string{attr.Name}
|
||||
params["Expected."+expectedNumStr+".Value"] = []string{attr.Value}
|
||||
|
||||
if attrs.missing[attr.Name] {
|
||||
params["Expected."+expectedNumStr+".Exists"] = []string{"false"}
|
||||
}
|
||||
expectedNum++
|
||||
}
|
||||
|
||||
err = item.query(params, nil, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Response to an Attrs request.
|
||||
//
|
||||
// See http://goo.gl/45X1M for more details.
|
||||
type AttrsResp struct {
|
||||
Attrs []Attr `xml:"GetAttributesResult>Attribute"`
|
||||
ResponseMetadata ResponseMetadata
|
||||
}
|
||||
|
||||
// Attrs returns one or more of the named attributes, or
|
||||
// all of item's attributes if names is nil.
|
||||
// If consistent is true, previous writes will necessarily
|
||||
// be observed.
|
||||
//
|
||||
// See http://goo.gl/45X1M for more details.
|
||||
func (item *Item) Attrs(names []string, consistent bool) (resp *AttrsResp, err error) {
|
||||
params := makeParams("GetAttributes")
|
||||
params["ItemName"] = []string{item.Name}
|
||||
if consistent {
|
||||
params["ConsistentRead"] = []string{"true"}
|
||||
}
|
||||
|
||||
// Copy these attributes over to the parameters
|
||||
for i, name := range names {
|
||||
params["AttributeName."+strconv.Itoa(i+1)] = []string{name}
|
||||
}
|
||||
|
||||
resp = &AttrsResp{}
|
||||
err = item.query(params, nil, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Generic data structures for all requests/responses.
|
||||
|
||||
// Error encapsulates an error returned by SDB.
|
||||
type Error struct {
|
||||
StatusCode int // HTTP status code (200, 403, ...)
|
||||
StatusMsg string // HTTP status message ("Service Unavailable", "Bad Request", ...)
|
||||
Code string // SimpleDB error code ("InvalidParameterValue", ...)
|
||||
Message string // The human-oriented error message
|
||||
RequestId string // A unique ID for this request
|
||||
BoxUsage float64 // The measure of machine utilization for this request.
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
// SimpleResp represents a response to an SDB request which on success
|
||||
// will return no other information besides ResponseMetadata.
|
||||
type SimpleResp struct {
|
||||
ResponseMetadata ResponseMetadata
|
||||
}
|
||||
|
||||
// ResponseMetadata
|
||||
type ResponseMetadata struct {
|
||||
RequestId string // A unique ID for tracking the request
|
||||
BoxUsage float64 // The measure of machine utilization for this request.
|
||||
}
|
||||
|
||||
func buildError(r *http.Response) error {
|
||||
err := Error{}
|
||||
err.StatusCode = r.StatusCode
|
||||
err.StatusMsg = r.Status
|
||||
xml.NewDecoder(r.Body).Decode(&err)
|
||||
return &err
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Request dispatching logic.
|
||||
|
||||
func (item *Item) query(params url.Values, headers http.Header, resp interface{}) error {
|
||||
return item.Domain.SDB.query(item.Domain, item, params, headers, resp)
|
||||
}
|
||||
|
||||
func (domain *Domain) query(item *Item, params url.Values, headers http.Header, resp interface{}) error {
|
||||
return domain.SDB.query(domain, item, params, headers, resp)
|
||||
}
|
||||
|
||||
func (sdb *SDB) query(domain *Domain, item *Item, params url.Values, headers http.Header, resp interface{}) error {
|
||||
// all SimpleDB operations have path="/"
|
||||
method := "GET"
|
||||
path := "/"
|
||||
|
||||
// if we have been given no headers or params, create them
|
||||
if headers == nil {
|
||||
headers = map[string][]string{}
|
||||
}
|
||||
if params == nil {
|
||||
params = map[string][]string{}
|
||||
}
|
||||
|
||||
// setup some default parameters
|
||||
params["Version"] = []string{"2009-04-15"}
|
||||
params["Timestamp"] = []string{time.Now().UTC().Format(time.RFC3339)}
|
||||
|
||||
// set the DomainName param (every request must have one)
|
||||
if domain != nil {
|
||||
params["DomainName"] = []string{domain.Name}
|
||||
}
|
||||
|
||||
// set the ItemName if we have one
|
||||
if item != nil {
|
||||
params["ItemName"] = []string{item.Name}
|
||||
}
|
||||
|
||||
// check the endpoint URL
|
||||
u, err := url.Parse(sdb.Region.SDBEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
headers["Host"] = []string{u.Host}
|
||||
sign(sdb.Auth, method, path, params, headers)
|
||||
|
||||
u.Path = path
|
||||
if len(params) > 0 {
|
||||
u.RawQuery = params.Encode()
|
||||
}
|
||||
req := http.Request{
|
||||
URL: u,
|
||||
Method: method,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Close: true,
|
||||
Header: headers,
|
||||
}
|
||||
|
||||
if v, ok := headers["Content-Length"]; ok {
|
||||
req.ContentLength, _ = strconv.ParseInt(v[0], 10, 64)
|
||||
delete(headers, "Content-Length")
|
||||
}
|
||||
|
||||
r, err := http.DefaultClient.Do(&req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if debug {
|
||||
dump, _ := httputil.DumpResponse(r, true)
|
||||
log.Printf("response:\n")
|
||||
log.Printf("%v\n}\n", string(dump))
|
||||
}
|
||||
|
||||
// status code is always 200 when successful (since we're always doing a GET)
|
||||
if r.StatusCode != 200 {
|
||||
return buildError(r)
|
||||
}
|
||||
|
||||
// everything was fine, so unmarshal the XML and return what it's err is (if any)
|
||||
err = xml.NewDecoder(r.Body).Decode(resp)
|
||||
return err
|
||||
}
|
||||
|
||||
func makeParams(action string) map[string][]string {
|
||||
params := make(map[string][]string)
|
||||
params["Action"] = []string{action}
|
||||
return params
|
||||
}
|
||||
219
vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go
сгенерированный
поставляемый
219
vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go
сгенерированный
поставляемый
@@ -1,219 +0,0 @@
|
||||
package sdb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/exp/sdb"
|
||||
"github.com/goamz/goamz/testutil"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
type S struct {
|
||||
sdb *sdb.SDB
|
||||
}
|
||||
|
||||
var testServer = testutil.NewHTTPServer()
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
testServer.Start()
|
||||
auth := aws.Auth{AccessKey: "abc", SecretKey: "123"}
|
||||
s.sdb = sdb.New(auth, aws.Region{SDBEndpoint: testServer.URL})
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
testServer.Flush()
|
||||
}
|
||||
|
||||
func (s *S) TestCreateDomainOK(c *C) {
|
||||
testServer.Response(200, nil, TestCreateDomainXmlOK)
|
||||
|
||||
domain := s.sdb.Domain("domain")
|
||||
resp, err := domain.CreateDomain()
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Method, Equals, "GET")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Header["Date"], Not(Equals), "")
|
||||
|
||||
c.Assert(resp.ResponseMetadata.RequestId, Equals, "63264005-7a5f-e01a-a224-395c63b89f6d")
|
||||
c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0055590279)
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *S) TestListDomainsOK(c *C) {
|
||||
testServer.Response(200, nil, TestListDomainsXmlOK)
|
||||
|
||||
resp, err := s.sdb.ListDomains()
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Method, Equals, "GET")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Header["Date"], Not(Equals), "")
|
||||
|
||||
c.Assert(resp.ResponseMetadata.RequestId, Equals, "15fcaf55-9914-63c2-21f3-951e31193790")
|
||||
c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000071759)
|
||||
c.Assert(resp.Domains, DeepEquals, []string{"Account", "Domain", "Record"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *S) TestListDomainsWithNextTokenXmlOK(c *C) {
|
||||
testServer.Response(200, nil, TestListDomainsWithNextTokenXmlOK)
|
||||
|
||||
resp, err := s.sdb.ListDomains()
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Method, Equals, "GET")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Header["Date"], Not(Equals), "")
|
||||
|
||||
c.Assert(resp.ResponseMetadata.RequestId, Equals, "eb13162f-1b95-4511-8b12-489b86acfd28")
|
||||
c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907)
|
||||
c.Assert(resp.Domains, DeepEquals, []string{"Domain1-200706011651", "Domain2-200706011652"})
|
||||
c.Assert(resp.NextToken, Equals, "TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY=")
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *S) TestDeleteDomainOK(c *C) {
|
||||
testServer.Response(200, nil, TestDeleteDomainXmlOK)
|
||||
|
||||
domain := s.sdb.Domain("domain")
|
||||
resp, err := domain.DeleteDomain()
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Method, Equals, "GET")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Header["Date"], Not(Equals), "")
|
||||
|
||||
c.Assert(resp.ResponseMetadata.RequestId, Equals, "039e1e25-9a64-2a74-93da-2fda36122a97")
|
||||
c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0055590278)
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *S) TestPutAttrsOK(c *C) {
|
||||
testServer.Response(200, nil, TestPutAttrsXmlOK)
|
||||
|
||||
domain := s.sdb.Domain("MyDomain")
|
||||
item := domain.Item("Item123")
|
||||
|
||||
putAttrs := new(sdb.PutAttrs)
|
||||
putAttrs.Add("FirstName", "john")
|
||||
putAttrs.Add("LastName", "smith")
|
||||
putAttrs.Replace("MiddleName", "jacob")
|
||||
|
||||
putAttrs.IfValue("FirstName", "john")
|
||||
putAttrs.IfMissing("FirstName")
|
||||
|
||||
resp, err := item.PutAttrs(putAttrs)
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, Equals, "GET")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"PutAttributes"})
|
||||
c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"})
|
||||
c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"})
|
||||
c.Assert(req.Form["Attribute.1.Name"], DeepEquals, []string{"FirstName"})
|
||||
c.Assert(req.Form["Attribute.1.Value"], DeepEquals, []string{"john"})
|
||||
c.Assert(req.Form["Attribute.2.Name"], DeepEquals, []string{"LastName"})
|
||||
c.Assert(req.Form["Attribute.2.Value"], DeepEquals, []string{"smith"})
|
||||
c.Assert(req.Form["Attribute.3.Name"], DeepEquals, []string{"MiddleName"})
|
||||
c.Assert(req.Form["Attribute.3.Value"], DeepEquals, []string{"jacob"})
|
||||
c.Assert(req.Form["Attribute.3.Replace"], DeepEquals, []string{"true"})
|
||||
|
||||
c.Assert(req.Form["Expected.1.Name"], DeepEquals, []string{"FirstName"})
|
||||
c.Assert(req.Form["Expected.1.Value"], DeepEquals, []string{"john"})
|
||||
c.Assert(req.Form["Expected.1.Exists"], DeepEquals, []string{"false"})
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(resp.ResponseMetadata.RequestId, Equals, "490206ce-8292-456c-a00f-61b335eb202b")
|
||||
c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907)
|
||||
|
||||
}
|
||||
|
||||
func (s *S) TestAttrsOK(c *C) {
|
||||
testServer.Response(200, nil, TestAttrsXmlOK)
|
||||
|
||||
domain := s.sdb.Domain("MyDomain")
|
||||
item := domain.Item("Item123")
|
||||
|
||||
resp, err := item.Attrs(nil, true)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Method, Equals, "GET")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Header["Date"], Not(Equals), "")
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"GetAttributes"})
|
||||
c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"})
|
||||
c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"})
|
||||
c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"})
|
||||
|
||||
c.Assert(resp.Attrs[0].Name, Equals, "Color")
|
||||
c.Assert(resp.Attrs[0].Value, Equals, "Blue")
|
||||
c.Assert(resp.Attrs[1].Name, Equals, "Size")
|
||||
c.Assert(resp.Attrs[1].Value, Equals, "Med")
|
||||
c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d")
|
||||
c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219942)
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *S) TestAttrsSelectOK(c *C) {
|
||||
testServer.Response(200, nil, TestAttrsXmlOK)
|
||||
|
||||
domain := s.sdb.Domain("MyDomain")
|
||||
item := domain.Item("Item123")
|
||||
|
||||
resp, err := item.Attrs([]string{"Color", "Size"}, true)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Method, Equals, "GET")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Header["Date"], Not(Equals), "")
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"GetAttributes"})
|
||||
c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"})
|
||||
c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"})
|
||||
c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"})
|
||||
c.Assert(req.Form["AttributeName.1"], DeepEquals, []string{"Color"})
|
||||
c.Assert(req.Form["AttributeName.2"], DeepEquals, []string{"Size"})
|
||||
|
||||
c.Assert(resp.Attrs[0].Name, Equals, "Color")
|
||||
c.Assert(resp.Attrs[0].Value, Equals, "Blue")
|
||||
c.Assert(resp.Attrs[1].Name, Equals, "Size")
|
||||
c.Assert(resp.Attrs[1].Value, Equals, "Med")
|
||||
c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d")
|
||||
c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219942)
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *S) TestSelectOK(c *C) {
|
||||
testServer.Response(200, nil, TestSelectXmlOK)
|
||||
|
||||
resp, err := s.sdb.Select("select Color from MyDomain where Color like 'Blue%'", true)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Method, Equals, "GET")
|
||||
c.Assert(req.URL.Path, Equals, "/")
|
||||
c.Assert(req.Header["Date"], Not(Equals), "")
|
||||
c.Assert(req.Form["Action"], DeepEquals, []string{"Select"})
|
||||
c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"})
|
||||
|
||||
c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d")
|
||||
c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907)
|
||||
c.Assert(len(resp.Items), Equals, 2)
|
||||
c.Assert(resp.Items[0].Name, Equals, "Item_03")
|
||||
c.Assert(resp.Items[1].Name, Equals, "Item_06")
|
||||
c.Assert(resp.Items[0].Attrs[0].Name, Equals, "Category")
|
||||
c.Assert(resp.Items[0].Attrs[0].Value, Equals, "Clothes")
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
54
vendor/github.com/goamz/goamz/exp/sdb/sign.go
сгенерированный
поставляемый
54
vendor/github.com/goamz/goamz/exp/sdb/sign.go
сгенерированный
поставляемый
@@ -1,54 +0,0 @@
|
||||
package sdb
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// SimpleDB signing (http://goo.gl/CaY81)
|
||||
|
||||
func sign(auth aws.Auth, method, path string, params url.Values, headers http.Header) {
|
||||
var host string
|
||||
for k, v := range headers {
|
||||
k = strings.ToLower(k)
|
||||
switch k {
|
||||
case "host":
|
||||
host = v[0]
|
||||
}
|
||||
}
|
||||
|
||||
// set up some defaults used for signing the request
|
||||
params["AWSAccessKeyId"] = []string{auth.AccessKey}
|
||||
params["SignatureVersion"] = []string{"2"}
|
||||
params["SignatureMethod"] = []string{"HmacSHA256"}
|
||||
if auth.Token() != "" {
|
||||
params["SecurityToken"] = []string{auth.Token()}
|
||||
}
|
||||
|
||||
// join up all the incoming params
|
||||
var sarray []string
|
||||
for k, v := range params {
|
||||
sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v[0]))
|
||||
}
|
||||
sort.StringSlice(sarray).Sort()
|
||||
joined := strings.Join(sarray, "&")
|
||||
|
||||
// create the payload, sign it and create the signature
|
||||
payload := strings.Join([]string{method, host, "/", joined}, "\n")
|
||||
hash := hmac.New(sha256.New, []byte(auth.SecretKey))
|
||||
hash.Write([]byte(payload))
|
||||
signature := make([]byte, b64.EncodedLen(hash.Size()))
|
||||
b64.Encode(signature, hash.Sum(nil))
|
||||
|
||||
// add the signature to the outgoing params
|
||||
params["Signature"] = []string{string(signature)}
|
||||
}
|
||||
29
vendor/github.com/goamz/goamz/exp/sdb/sign_test.go
сгенерированный
поставляемый
29
vendor/github.com/goamz/goamz/exp/sdb/sign_test.go
сгенерированный
поставляемый
@@ -1,29 +0,0 @@
|
||||
package sdb_test
|
||||
|
||||
import (
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/exp/sdb"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
// SimpleDB ReST authentication docs: http://goo.gl/CaY81
|
||||
|
||||
var testAuth = aws.Auth{AccessKey: "access-key-id-s8eBOWuU", SecretKey: "secret-access-key-UkQjTLd9"}
|
||||
|
||||
func (s *S) TestSignExampleDomainCreate(c *C) {
|
||||
method := "GET"
|
||||
params := map[string][]string{
|
||||
"Action": {"CreateDomain"},
|
||||
"DomainName": {"MyDomain"},
|
||||
"Timestamp": {"2011-08-20T07:23:57+12:00"},
|
||||
"Version": {"2009-04-15"},
|
||||
}
|
||||
headers := map[string][]string{
|
||||
"Host": {"sdb.amazonaws.com"},
|
||||
}
|
||||
sdb.Sign(testAuth, method, "", params, headers)
|
||||
expected := "ot2JaeeqMRJqgAqW67hkzUlffgxdOz4RykbrECB+tDU="
|
||||
c.Assert(params["Signature"], DeepEquals, []string{expected})
|
||||
}
|
||||
|
||||
// Do a few test methods which takes combinations of params
|
||||
149
vendor/github.com/goamz/goamz/exp/ses/ses.go
сгенерированный
поставляемый
149
vendor/github.com/goamz/goamz/exp/ses/ses.go
сгенерированный
поставляемый
@@ -1,149 +0,0 @@
|
||||
// ses.go
|
||||
package ses
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SES struct {
|
||||
auth aws.Auth
|
||||
region aws.Region
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// Initializes a pointer to an SES struct which can be used
|
||||
// to perform SES API calls.
|
||||
func NewSES(auth aws.Auth, region aws.Region) *SES {
|
||||
ses := SES{auth, region, nil}
|
||||
return &ses
|
||||
}
|
||||
|
||||
// Sends an email to the specifications stored in the Email struct.
|
||||
func (ses *SES) SendEmail(email *Email) error {
|
||||
data := make(url.Values)
|
||||
|
||||
index := 0
|
||||
for i := range email.destination.bccAddresses {
|
||||
if len(email.destination.bccAddresses[i]) > 0 {
|
||||
index += 1
|
||||
key := "Destination.BccAddresses.member." + strconv.Itoa(index)
|
||||
data.Add(key, email.destination.bccAddresses[i])
|
||||
}
|
||||
}
|
||||
|
||||
index = 0
|
||||
for i := range email.destination.ccAddresses {
|
||||
if len(email.destination.ccAddresses[i]) > 0 {
|
||||
index += 1
|
||||
key := "Destination.CcAddresses.member." + strconv.Itoa(index)
|
||||
data.Add(key, email.destination.ccAddresses[i])
|
||||
}
|
||||
}
|
||||
|
||||
index = 0
|
||||
for i := range email.destination.toAddresses {
|
||||
if len(email.destination.toAddresses[i]) > 0 {
|
||||
index += 1
|
||||
key := "Destination.ToAddresses.member." + strconv.Itoa(index)
|
||||
data.Add(key, email.destination.toAddresses[i])
|
||||
}
|
||||
}
|
||||
|
||||
index = 0
|
||||
for i := range email.replyTo {
|
||||
if len(email.replyTo[i]) > 0 {
|
||||
index += 1
|
||||
key := "ReplyToAddresses.member." + strconv.Itoa(index)
|
||||
data.Add(key, email.replyTo[i])
|
||||
}
|
||||
}
|
||||
|
||||
if len(email.message.Subject.Data) > 0 {
|
||||
if len(email.message.Subject.Charset) > 0 {
|
||||
data.Add("Message.Subject.Charset", email.message.Subject.Charset)
|
||||
}
|
||||
data.Add("Message.Subject.Data", email.message.Subject.Data)
|
||||
}
|
||||
|
||||
if len(email.message.Body.Html.Data) > 0 {
|
||||
if len(email.message.Body.Html.Charset) > 0 {
|
||||
data.Add("Message.Body.Html.Charset", email.message.Body.Html.Charset)
|
||||
}
|
||||
data.Add("Message.Body.Html.Data", email.message.Body.Html.Data)
|
||||
}
|
||||
|
||||
if len(email.message.Body.Text.Data) > 0 {
|
||||
if len(email.message.Body.Text.Charset) > 0 {
|
||||
data.Add("Message.Body.Text.Charset", email.message.Body.Text.Charset)
|
||||
}
|
||||
data.Add("Message.Body.Text.Data", email.message.Body.Text.Data)
|
||||
}
|
||||
|
||||
if len(email.returnPath) > 0 {
|
||||
data.Add("ReturnPath", email.returnPath)
|
||||
}
|
||||
|
||||
if len(email.source) > 0 {
|
||||
data.Add("Source", email.source)
|
||||
}
|
||||
|
||||
return ses.doPost("SendEmail", data)
|
||||
}
|
||||
|
||||
// Do an SES POST action.
|
||||
func (ses *SES) doPost(action string, data url.Values) error {
|
||||
req := http.Request{
|
||||
Method: "POST",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Close: true,
|
||||
Header: http.Header{}}
|
||||
|
||||
URL, err := url.Parse(ses.region.SESEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
URL.Path = "/"
|
||||
|
||||
req.URL = URL
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
token := ses.auth.Token()
|
||||
if token != "" {
|
||||
req.Header.Set("X-Amz-Security-Token", token)
|
||||
}
|
||||
sign(ses.auth, "POST", req.Header)
|
||||
|
||||
data.Add("AWSAccessKeyId", ses.auth.AccessKey)
|
||||
data.Add("Action", action)
|
||||
|
||||
body := data.Encode()
|
||||
req.Header.Add("Content-Length", strconv.Itoa(len(body)))
|
||||
req.Body = ioutil.NopCloser(strings.NewReader(body))
|
||||
|
||||
if ses.client == nil {
|
||||
ses.client = &http.Client{}
|
||||
}
|
||||
|
||||
resp, err := ses.client.Do(&req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode > 204 {
|
||||
defer resp.Body.Close()
|
||||
return buildError(resp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildError(r *http.Response) *SESError {
|
||||
rootElem := errorResponse{}
|
||||
xml.NewDecoder(r.Body).Decode(&rootElem)
|
||||
return &rootElem.Error
|
||||
}
|
||||
69
vendor/github.com/goamz/goamz/exp/ses/ses_test.go
сгенерированный
поставляемый
69
vendor/github.com/goamz/goamz/exp/ses/ses_test.go
сгенерированный
поставляемый
@@ -1,69 +0,0 @@
|
||||
// ses_test
|
||||
package ses_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
"github.com/goamz/goamz/exp/ses"
|
||||
"github.com/goamz/goamz/testutil"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
type S struct {
|
||||
ses *ses.SES
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
var testServer = testutil.NewHTTPServer()
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
testServer.Start()
|
||||
auth := aws.Auth{AccessKey: "abc", SecretKey: "123"}
|
||||
s.ses = ses.NewSES(auth, aws.Region{Name: "faux-region-1", S3Endpoint: testServer.URL})
|
||||
}
|
||||
|
||||
func (s *S) TearDownStrategy(c *C) {
|
||||
|
||||
}
|
||||
|
||||
func (s *S) SetUpTest(c *C) {
|
||||
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
testServer.Flush()
|
||||
}
|
||||
|
||||
func (s *S) TestSendEmail(c *C) {
|
||||
testServer.Response(200, nil, "")
|
||||
|
||||
email := ses.NewEmail()
|
||||
email.AddTo("test@test.com")
|
||||
email.AddSource("test@test.com")
|
||||
email.SetSubject("test")
|
||||
email.SetBodyHtml("test")
|
||||
|
||||
s.ses.SendEmail(email)
|
||||
req := testServer.WaitRequest()
|
||||
|
||||
c.Assert(req.Method, Equals, "POST")
|
||||
c.Assert(req.Header["Date"], Not(Equals), "")
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(req.Body)
|
||||
body, _ := url.ParseQuery(buf.String())
|
||||
|
||||
c.Assert(body, Not(IsNil))
|
||||
c.Assert(body["Destination.ToAddresses.member.1"], Equals, "test@test.com")
|
||||
c.Assert(body["Source"], Equals, "test@test.com")
|
||||
c.Assert(body["Message.Subject.Data"], Equals, "test")
|
||||
c.Assert(body["Message.Body.Html.Data"], Equals, "test")
|
||||
}
|
||||
160
vendor/github.com/goamz/goamz/exp/ses/ses_types.go
сгенерированный
поставляемый
160
vendor/github.com/goamz/goamz/exp/ses/ses_types.go
сгенерированный
поставляемый
@@ -1,160 +0,0 @@
|
||||
// ses_types
|
||||
package ses
|
||||
|
||||
// Private internal representation of message body.
|
||||
type Body struct {
|
||||
Html Content
|
||||
Text Content
|
||||
}
|
||||
|
||||
// Content data structure with charset and payload (Data).
|
||||
type Content struct {
|
||||
Charset string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Email data structure. Should be main data structure used
|
||||
// for sending emails using SES.
|
||||
type Email struct {
|
||||
destination *destination
|
||||
message Message
|
||||
replyTo []string
|
||||
returnPath string
|
||||
source string
|
||||
}
|
||||
|
||||
// Private internal representation for email destinations.
|
||||
type destination struct {
|
||||
bccAddresses []string
|
||||
ccAddresses []string
|
||||
toAddresses []string
|
||||
}
|
||||
|
||||
// Message data structure.
|
||||
type Message struct {
|
||||
Body Body
|
||||
Subject Content
|
||||
}
|
||||
|
||||
// SES Error structure.
|
||||
type SESError struct {
|
||||
Type string
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
RequestId string
|
||||
}
|
||||
|
||||
type errorResponse struct {
|
||||
Error SESError
|
||||
}
|
||||
|
||||
func (err *SESError) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
// Returns a pointer to an empty but initialized Email.
|
||||
func NewEmail() *Email {
|
||||
return &Email{
|
||||
destination: newDestination(),
|
||||
replyTo: make([]string, 5)}
|
||||
}
|
||||
|
||||
// Private function to return an initialized destination.
|
||||
func newDestination() *destination {
|
||||
return &destination{
|
||||
bccAddresses: make([]string, 5),
|
||||
ccAddresses: make([]string, 5),
|
||||
toAddresses: make([]string, 5)}
|
||||
}
|
||||
|
||||
// Add a BCC destination to Email.
|
||||
func (em *Email) AddBCC(address string) {
|
||||
em.destination.bccAddresses = append(em.destination.bccAddresses, address)
|
||||
}
|
||||
|
||||
// Add multiple BCC destinations to Email.
|
||||
func (em *Email) AddBCCs(addresses []string) {
|
||||
em.destination.bccAddresses = append(em.destination.bccAddresses, addresses...)
|
||||
}
|
||||
|
||||
// Add a CC destination to Email.
|
||||
func (em *Email) AddCC(address string) {
|
||||
em.destination.ccAddresses = append(em.destination.ccAddresses, address)
|
||||
}
|
||||
|
||||
// Add multiple CC destinations to Email.
|
||||
func (em *Email) AddCCs(addresses []string) {
|
||||
em.destination.ccAddresses = append(em.destination.ccAddresses, addresses...)
|
||||
}
|
||||
|
||||
// Add a To destination to Email.
|
||||
func (em *Email) AddTo(address string) {
|
||||
em.destination.toAddresses = append(em.destination.toAddresses, address)
|
||||
}
|
||||
|
||||
// Add multiple To destinations to Email.
|
||||
func (em *Email) AddTos(addresses []string) {
|
||||
em.destination.toAddresses = append(em.destination.toAddresses, addresses...)
|
||||
}
|
||||
|
||||
// Add a reply-to address to Email.
|
||||
func (em *Email) AddReplyTo(address string) {
|
||||
em.replyTo = append(em.replyTo, address)
|
||||
}
|
||||
|
||||
// Add multiple reply-to addresses to Email.
|
||||
func (em *Email) AddReplyTos(addresses []string) {
|
||||
em.replyTo = append(em.replyTo, addresses...)
|
||||
}
|
||||
|
||||
// Set the return path for Email.
|
||||
func (em *Email) SetReturnPath(path string) {
|
||||
em.returnPath = path
|
||||
}
|
||||
|
||||
// Set the source address for Email.
|
||||
func (em *Email) SetSource(source string) {
|
||||
em.source = source
|
||||
}
|
||||
|
||||
// Set the Email message.
|
||||
func (em *Email) SetMessage(message Message) {
|
||||
em.message = message
|
||||
}
|
||||
|
||||
// Set the subject for the Email message.
|
||||
// Uses ASCII as charset.
|
||||
func (em *Email) SetSubject(subject string) {
|
||||
em.message.Subject = Content{Data: subject}
|
||||
}
|
||||
|
||||
// Set the subject for the Email message.
|
||||
// Uses the charset (or ASCII if none) of Content.
|
||||
func (em *Email) SetSubjectAsContent(subject Content) {
|
||||
em.message.Subject = subject
|
||||
}
|
||||
|
||||
// Sets the HTML body of the Email message.
|
||||
// Uses ASCII as charset.
|
||||
func (em *Email) SetBodyHtml(html string) {
|
||||
em.message.Body.Html = Content{Data: html}
|
||||
}
|
||||
|
||||
// Sets the HTML body of the Email message.
|
||||
// Uses the charset (or ASCII if none) of Content.
|
||||
func (em *Email) SetBodyHtmlAsContent(html Content) {
|
||||
em.message.Body.Html = html
|
||||
}
|
||||
|
||||
// Sets the raw text body of the Email message.
|
||||
// Uses ASCII as charset.
|
||||
func (em *Email) SetBodyRawText(text string) {
|
||||
em.message.Body.Text = Content{Data: text}
|
||||
}
|
||||
|
||||
// Sets the raw test body of the Email message.
|
||||
// Uses the charset (or ASCII if none) of Content.
|
||||
func (em *Email) SetBodyRawTextAsContent(text Content) {
|
||||
em.message.Body.Text = text
|
||||
}
|
||||
26
vendor/github.com/goamz/goamz/exp/ses/sign.go
сгенерированный
поставляемый
26
vendor/github.com/goamz/goamz/exp/ses/sign.go
сгенерированный
поставляемый
@@ -1,26 +0,0 @@
|
||||
// sign
|
||||
package ses
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
AMZ_DATE_STYLE = "Mon, 02 Jan 2006 15:04:05 -0700"
|
||||
)
|
||||
|
||||
// Sign SES request as dictated by Amazon's Version 3 signature method.
|
||||
func sign(auth aws.Auth, method string, headers map[string][]string) {
|
||||
date := time.Now().UTC().Format(AMZ_DATE_STYLE)
|
||||
h := hmac.New(sha256.New, []byte(auth.SecretKey))
|
||||
h.Write([]byte(date))
|
||||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
authHeader := fmt.Sprintf("AWS3-HTTPS AWSAccessKeyId=%s, Algorithm=HmacSHA256, Signature=%s", auth.AccessKey, signature)
|
||||
headers["Date"] = []string{date}
|
||||
headers["X-Amzn-Authorization"] = []string{authHeader}
|
||||
}
|
||||
21
vendor/github.com/goamz/goamz/exp/sns/Makefile
сгенерированный
поставляемый
21
vendor/github.com/goamz/goamz/exp/sns/Makefile
сгенерированный
поставляемый
@@ -1,21 +0,0 @@
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG=launchpad.net/goamz/sns
|
||||
|
||||
GOFILES=\
|
||||
sns.go\
|
||||
sign.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
||||
GOFMT=gofmt
|
||||
BADFMT=$(shell $(GOFMT) -l $(GOFILES) 2> /dev/null)
|
||||
|
||||
gofmt: $(BADFMT)
|
||||
@for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done
|
||||
|
||||
ifneq ($(BADFMT),)
|
||||
ifneq ($(MAKECMDGOALS), gofmt)
|
||||
#$(warning WARNING: make gofmt: $(BADFMT))
|
||||
endif
|
||||
endif
|
||||
1
vendor/github.com/goamz/goamz/exp/sns/README
сгенерированный
поставляемый
1
vendor/github.com/goamz/goamz/exp/sns/README
сгенерированный
поставляемый
@@ -1 +0,0 @@
|
||||
Amazon Simple Notification Service API for Golang.
|
||||
132
vendor/github.com/goamz/goamz/exp/sns/endpoint.go
сгенерированный
поставляемый
132
vendor/github.com/goamz/goamz/exp/sns/endpoint.go
сгенерированный
поставляемый
@@ -1,132 +0,0 @@
|
||||
package sns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type DeleteEndpointResponse struct {
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
type GetEndpointAttributesResponse struct {
|
||||
Attributes []AttributeEntry `xml:"GetEndpointAttributesResult>Attributes>entry"`
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
type PlatformEndpointOpt struct {
|
||||
Attributes []AttributeEntry
|
||||
PlatformApplicationArn string
|
||||
CustomUserData string
|
||||
Token string
|
||||
}
|
||||
|
||||
type CreatePlatformEndpointResponse struct {
|
||||
EndpointArn string `xml:"CreatePlatformEndpointResult>EndpointArn"`
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
type PlatformEndpoints struct {
|
||||
EndpointArn string `xml:"EndpointArn"`
|
||||
Attributes []AttributeEntry `xml:"Attributes>entry"`
|
||||
}
|
||||
|
||||
type ListEndpointsByPlatformApplicationResponse struct {
|
||||
Endpoints []PlatformEndpoints `xml:"ListEndpointsByPlatformApplicationResult>Endpoints>member"`
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
type SetEndpointAttributesOpt struct {
|
||||
Attributes []AttributeEntry
|
||||
EndpointArn string
|
||||
}
|
||||
|
||||
type SetEndpointAttributesResponse struct {
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
// DeleteEndpoint
|
||||
//
|
||||
// See http://goo.gl/9SlUD9 for more details.
|
||||
func (sns *SNS) DeleteEndpoint(endpointArn string) (resp *DeleteEndpointResponse, err error) {
|
||||
resp = &DeleteEndpointResponse{}
|
||||
params := makeParams("DeleteEndpoint")
|
||||
|
||||
params["EndpointArn"] = endpointArn
|
||||
|
||||
err = sns.query(params, resp)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetEndpointAttributes
|
||||
//
|
||||
// See http://goo.gl/c8E5X1 for more details.
|
||||
func (sns *SNS) GetEndpointAttributes(endpointArn string) (resp *GetEndpointAttributesResponse, err error) {
|
||||
resp = &GetEndpointAttributesResponse{}
|
||||
|
||||
params := makeParams("GetEndpointAttributes")
|
||||
|
||||
params["EndpointArn"] = endpointArn
|
||||
|
||||
err = sns.query(params, resp)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CreatePlatformEndpoint
|
||||
//
|
||||
// See http://goo.gl/4tnngi for more details.
|
||||
func (sns *SNS) CreatePlatformEndpoint(options *PlatformEndpointOpt) (resp *CreatePlatformEndpointResponse, err error) {
|
||||
|
||||
resp = &CreatePlatformEndpointResponse{}
|
||||
params := makeParams("CreatePlatformEndpoint")
|
||||
|
||||
params["PlatformApplicationArn"] = options.PlatformApplicationArn
|
||||
params["Token"] = options.Token
|
||||
|
||||
if options.CustomUserData != "" {
|
||||
params["CustomUserData"] = options.CustomUserData
|
||||
}
|
||||
|
||||
err = sns.query(params, resp)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ListEndpointsByPlatformApplication
|
||||
//
|
||||
// See http://goo.gl/L7ioyR for more detail.
|
||||
func (sns *SNS) ListEndpointsByPlatformApplication(platformApplicationArn, nextToken string) (resp *ListEndpointsByPlatformApplicationResponse, err error) {
|
||||
resp = &ListEndpointsByPlatformApplicationResponse{}
|
||||
|
||||
params := makeParams("ListEndpointsByPlatformApplication")
|
||||
|
||||
params["PlatformApplicationArn"] = platformApplicationArn
|
||||
|
||||
if nextToken != "" {
|
||||
params["NextToken"] = nextToken
|
||||
}
|
||||
|
||||
err = sns.query(params, resp)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// SetEndpointAttributes
|
||||
//
|
||||
// See http://goo.gl/GTktCj for more detail.
|
||||
func (sns *SNS) SetEndpointAttributes(options *SetEndpointAttributesOpt) (resp *SetEndpointAttributesResponse, err error) {
|
||||
resp = &SetEndpointAttributesResponse{}
|
||||
params := makeParams("SetEndpointAttributes")
|
||||
|
||||
params["EndpointArn"] = options.EndpointArn
|
||||
|
||||
for i, attr := range options.Attributes {
|
||||
params[fmt.Sprintf("Attributes.entry.%s.key", strconv.Itoa(i+1))] = attr.Key
|
||||
params[fmt.Sprintf("Attributes.entry.%s.value", strconv.Itoa(i+1))] = attr.Value
|
||||
}
|
||||
|
||||
err = sns.query(params, resp)
|
||||
return
|
||||
}
|
||||
51
vendor/github.com/goamz/goamz/exp/sns/permissions.go
сгенерированный
поставляемый
51
vendor/github.com/goamz/goamz/exp/sns/permissions.go
сгенерированный
поставляемый
@@ -1,51 +0,0 @@
|
||||
package sns
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Permission struct {
|
||||
ActionName string
|
||||
AccountId string
|
||||
}
|
||||
|
||||
type AddPermissionResponse struct {
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
// AddPermission
|
||||
//
|
||||
// See http://goo.gl/mbY4a for more details.
|
||||
func (sns *SNS) AddPermission(permissions []Permission, Label, TopicArn string) (resp *AddPermissionResponse, err error) {
|
||||
resp = &AddPermissionResponse{}
|
||||
params := makeParams("AddPermission")
|
||||
|
||||
for i, p := range permissions {
|
||||
params["AWSAccountId.member."+strconv.Itoa(i+1)] = p.AccountId
|
||||
params["ActionName.member."+strconv.Itoa(i+1)] = p.ActionName
|
||||
}
|
||||
|
||||
params["Label"] = Label
|
||||
params["TopicArn"] = TopicArn
|
||||
|
||||
err = sns.query(params, resp)
|
||||
return
|
||||
}
|
||||
|
||||
type RemovePermissionResponse struct {
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
// RemovePermission
|
||||
//
|
||||
// See http://goo.gl/wGl5j for more details.
|
||||
func (sns *SNS) RemovePermission(Label, TopicArn string) (resp *RemovePermissionResponse, err error) {
|
||||
resp = &RemovePermissionResponse{}
|
||||
params := makeParams("RemovePermission")
|
||||
|
||||
params["Label"] = Label
|
||||
params["TopicArn"] = TopicArn
|
||||
|
||||
err = sns.query(params, resp)
|
||||
return
|
||||
}
|
||||
135
vendor/github.com/goamz/goamz/exp/sns/platform.go
сгенерированный
поставляемый
135
vendor/github.com/goamz/goamz/exp/sns/platform.go
сгенерированный
поставляемый
@@ -1,135 +0,0 @@
|
||||
package sns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type CreatePlatformApplicationResponse struct {
|
||||
PlatformApplicationArn string `xml:"CreatePlatformApplicationResult>PlatformApplicationArn"`
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
type PlatformApplicationOpt struct {
|
||||
Attributes []AttributeEntry
|
||||
Name string
|
||||
Platform string
|
||||
}
|
||||
|
||||
type DeletePlatformApplicationResponse struct {
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
type GetPlatformApplicationAttributesResponse struct {
|
||||
Attributes []AttributeEntry `xml:"GetPlatformApplicationAttributesResult>Attributes>entry"`
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
type SetPlatformApplicationAttributesOpt struct {
|
||||
Attributes []AttributeEntry
|
||||
PlatformApplicationArn string
|
||||
}
|
||||
|
||||
type SetPlatformApplicationAttributesResponse struct {
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
type PlatformApplication struct {
|
||||
Attributes []AttributeEntry `xml:"Attributes>entry"`
|
||||
PlatformApplicationArn string
|
||||
}
|
||||
|
||||
type ListPlatformApplicationsResponse struct {
|
||||
NextToken string
|
||||
PlatformApplications []PlatformApplication `xml:"ListPlatformApplicationsResult>PlatformApplications>member"`
|
||||
ResponseMetadata
|
||||
}
|
||||
|
||||
// CreatePlatformApplication
|
||||
//
|
||||
// See http://goo.gl/Mbbl6Z for more details.
|
||||
|
||||
func (sns *SNS) CreatePlatformApplication(options *PlatformApplicationOpt) (resp *CreatePlatformApplicationResponse, err error) {
|
||||
resp = &CreatePlatformApplicationResponse{}
|
||||
params := makeParams("CreatePlatformApplication")
|
||||
|
||||
params["Platform"] = options.Platform
|
||||
params["Name"] = options.Name
|
||||
|
||||
for i, attr := range options.Attributes {
|
||||
params[fmt.Sprintf("Attributes.entry.%s.key", strconv.Itoa(i+1))] = attr.Key
|
||||
params[fmt.Sprintf("Attributes.entry.%s.value", strconv.Itoa(i+1))] = attr.Value
|
||||
}
|
||||
|
||||
err = sns.query(params, resp)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// DeletePlatformApplication
|
||||
//
|
||||
// See http://goo.gl/6GB3DN for more details.
|
||||
func (sns *SNS) DeletePlatformApplication(platformApplicationArn string) (resp *DeletePlatformApplicationResponse, err error) {
|
||||
resp = &DeletePlatformApplicationResponse{}
|
||||
|
||||
params := makeParams("DeletePlatformApplication")
|
||||
|
||||
params["PlatformApplicationArn"] = platformApplicationArn
|
||||
|
||||
err = sns.query(params, resp)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPlatformApplicationAttributes
|
||||
//
|
||||
// See http://goo.gl/GswJ8I for more details.
|
||||
func (sns *SNS) GetPlatformApplicationAttributes(platformApplicationArn, nextToken string) (resp *GetPlatformApplicationAttributesResponse, err error) {
|
||||
resp = &GetPlatformApplicationAttributesResponse{}
|
||||
|
||||
params := makeParams("GetPlatformApplicationAttributes")
|
||||
|
||||
params["PlatformApplicationArn"] = platformApplicationArn
|
||||
|
||||
if nextToken != "" {
|
||||
params["NextToken"] = nextToken
|
||||
}
|
||||
|
||||
err = sns.query(params, resp)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ListPlatformApplications
|
||||
//
|
||||
// See http://goo.gl/vQ3ooV for more detail.
|
||||
func (sns *SNS) ListPlatformApplications(nextToken string) (resp *ListPlatformApplicationsResponse, err error) {
|
||||
resp = &ListPlatformApplicationsResponse{}
|
||||
params := makeParams("ListPlatformApplications")
|
||||
|
||||
if nextToken != "" {
|
||||
params["NextToken"] = nextToken
|
||||
}
|
||||
|
||||
err = sns.query(params, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// SetPlatformApplicationAttributes
|
||||
//
|
||||
// See http://goo.gl/RWnzzb for more detail.
|
||||
func (sns *SNS) SetPlatformApplicationAttributes(options *SetPlatformApplicationAttributesOpt) (resp *SetPlatformApplicationAttributesResponse, err error) {
|
||||
resp = &SetPlatformApplicationAttributesResponse{}
|
||||
params := makeParams("SetPlatformApplicationAttributes")
|
||||
|
||||
params["PlatformApplicationArn"] = options.PlatformApplicationArn
|
||||
|
||||
for i, attr := range options.Attributes {
|
||||
params[fmt.Sprintf("Attributes.entry.%s.key", strconv.Itoa(i+1))] = attr.Key
|
||||
params[fmt.Sprintf("Attributes.entry.%s.value", strconv.Itoa(i+1))] = attr.Value
|
||||
}
|
||||
|
||||
err = sns.query(params, resp)
|
||||
return
|
||||
}
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user