0
0
mirror of https://github.com/thegeeklab/wp-s3-action.git synced 2024-11-14 00:00:39 +00:00

Merge pull request #11 from ericanderson/cloudfront

Cloudfront
This commit is contained in:
Jack Spirou 2016-03-15 13:29:13 -05:00
commit c20b5f1b7a
24 changed files with 5743 additions and 53 deletions

View File

@ -35,7 +35,7 @@ make deps build
"finished_at": 1421029813, "finished_at": 1421029813,
"message": "Update the Readme", "message": "Update the Readme",
"author": "johnsmith", "author": "johnsmith",
"author_email": "john.smith@gmail.com" "author_email": "john.smith@gmail.com",
"event": "push", "event": "push",
"branch": "master", "branch": "master",
"commit": "436b7a6e2abaddfd35740527353e78a227ddcb2c", "commit": "436b7a6e2abaddfd35740527353e78a227ddcb2c",
@ -53,7 +53,8 @@ make deps build
"secret_key": "9c5785d3ece6a9cdefa42eb99b58986f9095ff1c", "secret_key": "9c5785d3ece6a9cdefa42eb99b58986f9095ff1c",
"source": "folder/to/archive", "source": "folder/to/archive",
"target": "/target/location", "target": "/target/location",
"delete": true "delete": true,
"cloudfront_distribution_id": "E16G5UJABCDAGDL"
} }
} }
EOF EOF

31
aws.go
View File

@ -7,20 +7,23 @@ import (
"mime" "mime"
"os" "os"
"path/filepath" "path/filepath"
"time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudFront"
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
"github.com/ryanuber/go-glob" "github.com/ryanuber/go-glob"
) )
type AWS struct { type AWS struct {
client *s3.S3 client *s3.S3
remote []string cfClient *cloudfront.CloudFront
local []string remote []string
vargs PluginArgs local []string
vargs PluginArgs
} }
func NewAWS(vargs PluginArgs) AWS { func NewAWS(vargs PluginArgs) AWS {
@ -29,10 +32,11 @@ func NewAWS(vargs PluginArgs) AWS {
Region: aws.String(vargs.Region), Region: aws.String(vargs.Region),
}) })
c := s3.New(sess) c := s3.New(sess)
cf := cloudfront.New(sess)
r := make([]string, 1, 1) r := make([]string, 1, 1)
l := make([]string, 1, 1) l := make([]string, 1, 1)
return AWS{c, r, l, vargs} return AWS{c, cf, r, l, vargs}
} }
func (a *AWS) Upload(local, remote string) error { func (a *AWS) Upload(local, remote string) error {
@ -270,3 +274,20 @@ func (a *AWS) List(path string) ([]string, error) {
return remote, nil return remote, nil
} }
func (a *AWS) Invalidate(invalidatePath string) error {
debug("Invalidating \"%s\"", invalidatePath)
_, err := a.cfClient.CreateInvalidation(&cloudfront.CreateInvalidationInput{
DistributionId: aws.String(a.vargs.CloudFrontDistribution),
InvalidationBatch: &cloudfront.InvalidationBatch{
CallerReference: aws.String(time.Now().Format(time.RFC3339Nano)),
Paths: &cloudfront.Paths{
Quantity: aws.Int64(1),
Items: []*string{
aws.String(invalidatePath),
},
},
},
})
return err
}

140
main.go
View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -23,25 +24,72 @@ type result struct {
err error err error
} }
type app struct {
vargs *PluginArgs
workspace *drone.Workspace
client AWS
jobs []job
}
var ( var (
buildCommit string buildCommit string
) )
var MissingAwsValuesMessage = "Must set access_key, secret_key, and bucket"
func main() { func main() {
fmt.Printf("Drone S3 Sync Plugin built from %s\n", buildCommit) fmt.Printf("Drone S3 Sync Plugin built from %s\n", buildCommit)
vargs := PluginArgs{} a := newApp()
workspace := drone.Workspace{}
plugin.Param("vargs", &vargs) err := a.loadVargs()
plugin.Param("workspace", &workspace) if err != nil {
if err := plugin.Parse(); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
if len(vargs.Key) == 0 || len(vargs.Secret) == 0 || len(vargs.Bucket) == 0 { err = a.sanitizeInputs()
return if err != nil {
fmt.Println(err)
os.Exit(1)
}
a.createClient()
a.createSyncJobs()
a.createInvalidateJob()
a.runJobs()
fmt.Println("done!")
}
func newApp() *app {
return &app{
vargs: &PluginArgs{},
workspace: &drone.Workspace{},
jobs: make([]job, 1, 1),
}
}
func (a *app) loadVargs() error {
plugin.Param("vargs", a.vargs)
plugin.Param("workspace", a.workspace)
err := plugin.Parse()
return err
}
func (a *app) createClient() {
a.client = NewAWS(*a.vargs)
}
func (a *app) sanitizeInputs() error {
vargs := a.vargs
workspace := a.workspace
if len(a.vargs.Key) == 0 || len(a.vargs.Secret) == 0 || len(a.vargs.Bucket) == 0 {
return errors.New(MissingAwsValuesMessage)
} }
if len(vargs.Region) == 0 { if len(vargs.Region) == 0 {
@ -57,15 +105,19 @@ func main() {
vargs.Target = vargs.Target[1:] vargs.Target = vargs.Target[1:]
} }
client := NewAWS(vargs) return nil
remote, err := client.List(vargs.Target) }
func (a *app) createSyncJobs() {
vargs := a.vargs
remote, err := a.client.List(vargs.Target)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
local := make([]string, 1, 1) local := make([]string, 1, 1)
jobs := make([]job, 1, 1)
err = filepath.Walk(vargs.Source, func(path string, info os.FileInfo, err error) error { err = filepath.Walk(vargs.Source, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() { if err != nil || info.IsDir() {
return err return err
@ -79,7 +131,7 @@ func main() {
} }
} }
local = append(local, localPath) local = append(local, localPath)
jobs = append(jobs, job{ a.jobs = append(a.jobs, job{
local: filepath.Join(vargs.Source, localPath), local: filepath.Join(vargs.Source, localPath),
remote: filepath.Join(vargs.Target, localPath), remote: filepath.Join(vargs.Target, localPath),
action: "upload", action: "upload",
@ -95,44 +147,64 @@ func main() {
for path, location := range vargs.Redirects { for path, location := range vargs.Redirects {
path = strings.TrimPrefix(path, "/") path = strings.TrimPrefix(path, "/")
local = append(local, path) local = append(local, path)
jobs = append(jobs, job{ a.jobs = append(a.jobs, job{
local: path, local: path,
remote: location, remote: location,
action: "redirect", action: "redirect",
}) })
} }
if a.vargs.Delete {
for _, r := range remote {
found := false
for _, l := range local {
if l == r {
found = true
break
}
}
for _, r := range remote { if !found {
found := false a.jobs = append(a.jobs, job{
for _, l := range local { local: "",
if l == r { remote: r,
found = true action: "delete",
break })
} }
} }
if !found {
jobs = append(jobs, job{
local: "",
remote: r,
action: "delete",
})
}
} }
}
func (a *app) createInvalidateJob() {
if len(a.vargs.CloudFrontDistribution) > 0 {
a.jobs = append(a.jobs, job{
local: "",
remote: filepath.Join("/", a.vargs.Target, "*"),
action: "invalidateCloudFront",
})
}
}
func (a *app) runJobs() {
vargs := a.vargs
client := a.client
jobChan := make(chan struct{}, maxConcurrent) jobChan := make(chan struct{}, maxConcurrent)
results := make(chan *result, len(jobs)) results := make(chan *result, len(a.jobs))
var invalidateJob *job
fmt.Printf("Synchronizing with bucket \"%s\"\n", vargs.Bucket) fmt.Printf("Synchronizing with bucket \"%s\"\n", vargs.Bucket)
for _, j := range jobs { for _, j := range a.jobs {
jobChan <- struct{}{} jobChan <- struct{}{}
go func(j job) { go func(j job) {
var err error
if j.action == "upload" { if j.action == "upload" {
err = client.Upload(j.local, j.remote) err = client.Upload(j.local, j.remote)
} else if j.action == "redirect" { } else if j.action == "redirect" {
err = client.Redirect(j.local, j.remote) err = client.Redirect(j.local, j.remote)
} else if j.action == "delete" && vargs.Delete { } else if j.action == "delete" {
err = client.Delete(j.remote) err = client.Delete(j.remote)
} else if j.action == "invalidateCloudFront" {
invalidateJob = &j
// err = client.Invalidate(j.remote)
} else { } else {
err = nil err = nil
} }
@ -141,7 +213,7 @@ func main() {
}(j) }(j)
} }
for _ = range jobs { for _ = range a.jobs {
r := <-results r := <-results
if r.err != nil { if r.err != nil {
fmt.Printf("ERROR: failed to %s %s to %s: %+v\n", r.j.action, r.j.local, r.j.remote, r.err) fmt.Printf("ERROR: failed to %s %s to %s: %+v\n", r.j.action, r.j.local, r.j.remote, r.err)
@ -149,7 +221,13 @@ func main() {
} }
} }
fmt.Println("done!") if invalidateJob != nil {
err := client.Invalidate(invalidateJob.remote)
if err != nil {
fmt.Printf("ERROR: failed to %s %s to %s: %+v\n", invalidateJob.action, invalidateJob.local, invalidateJob.remote, err)
os.Exit(1)
}
}
} }
func debug(format string, args ...interface{}) { func debug(format string, args ...interface{}) {

41
main_test.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func newAppWithAwsVars() *app {
a := newApp()
a.vargs.Key = "AAAA"
a.vargs.Secret = "AAAA"
a.vargs.Bucket = "AAAA"
return a
}
func TestSanitizeInputs(t *testing.T) {
a := newApp()
err := a.sanitizeInputs()
assert.EqualError(t, err, MissingAwsValuesMessage)
a = newAppWithAwsVars()
a.workspace.Path = "foo/bar"
a.vargs.Target = "/remote/foo"
a.sanitizeInputs()
assert.EqualValues(t, "foo/bar", a.vargs.Source, "Source should default to workspace.Path")
assert.EqualValues(t, "us-east-1", a.vargs.Region, "Region should default to 'us-east'")
assert.EqualValues(t, "remote/foo", a.vargs.Target, "Target should have first slash stripped")
a = newAppWithAwsVars()
a.workspace.Path = "foo/bar"
a.vargs.Source = "some/folder"
a.vargs.Target = "remote/foo"
a.sanitizeInputs()
assert.EqualValues(t, "foo/bar/some/folder", a.vargs.Source, "Source should combine workspace.Path and specified Source")
assert.EqualValues(t, "remote/foo", a.vargs.Target, "Target should have first slash stripped")
}

View File

@ -3,17 +3,18 @@ package main
import "encoding/json" import "encoding/json"
type PluginArgs struct { type PluginArgs struct {
Key string `json:"access_key"` Key string `json:"access_key"`
Secret string `json:"secret_key"` Secret string `json:"secret_key"`
Bucket string `json:"bucket"` Bucket string `json:"bucket"`
Region string `json:"region"` Region string `json:"region"`
Source string `json:"source"` Source string `json:"source"`
Target string `json:"target"` Target string `json:"target"`
Delete bool `json:"delete"` Delete bool `json:"delete"`
Access StringMap `json:"acl"` Access StringMap `json:"acl"`
ContentType StringMap `json:"content_type"` ContentType StringMap `json:"content_type"`
Metadata DeepStringMap `json:"metadata"` Metadata DeepStringMap `json:"metadata"`
Redirects map[string]string `json:"redirects"` Redirects map[string]string `json:"redirects"`
CloudFrontDistribution string `json:"cloudfront_distribution_id"`
} }
type DeepStringMap struct { type DeepStringMap struct {

202
vendor/github.com/aws/aws-sdk-go/LICENSE.txt generated vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

3
vendor/github.com/aws/aws-sdk-go/NOTICE.txt generated vendored Normal file
View File

@ -0,0 +1,3 @@
AWS SDK for Go
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright 2014-2015 Stripe, Inc.

View File

@ -0,0 +1,75 @@
package protocol
import (
"crypto/rand"
"fmt"
"reflect"
)
// RandReader is the random reader the protocol package will use to read
// random bytes from. This is exported for testing, and should not be used.
var RandReader = rand.Reader
const idempotencyTokenFillTag = `idempotencyToken`
// CanSetIdempotencyToken returns true if the struct field should be
// automatically populated with a Idempotency token.
//
// Only *string and string type fields that are tagged with idempotencyToken
// which are not already set can be auto filled.
func CanSetIdempotencyToken(v reflect.Value, f reflect.StructField) bool {
switch u := v.Interface().(type) {
// To auto fill an Idempotency token the field must be a string,
// tagged for auto fill, and have a zero value.
case *string:
return u == nil && len(f.Tag.Get(idempotencyTokenFillTag)) != 0
case string:
return len(u) == 0 && len(f.Tag.Get(idempotencyTokenFillTag)) != 0
}
return false
}
// GetIdempotencyToken returns a randomly generated idempotency token.
func GetIdempotencyToken() string {
b := make([]byte, 16)
RandReader.Read(b)
return UUIDVersion4(b)
}
// SetIdempotencyToken will set the value provided with a Idempotency Token.
// Given that the value can be set. Will panic if value is not setable.
func SetIdempotencyToken(v reflect.Value) {
if v.Kind() == reflect.Ptr {
if v.IsNil() && v.CanSet() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
}
v = reflect.Indirect(v)
if !v.CanSet() {
panic(fmt.Sprintf("unable to set idempotnecy token %v", v))
}
b := make([]byte, 16)
_, err := rand.Read(b)
if err != nil {
// TODO handle error
return
}
v.Set(reflect.ValueOf(UUIDVersion4(b)))
}
// UUIDVersion4 returns a Version 4 random UUID from the byte slice provided
func UUIDVersion4(u []byte) string {
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29
// 13th character is "4"
u[6] = (u[6] | 0x40) & 0x4F
// 17th character is "8", "9", "a", or "b"
u[8] = (u[8] | 0x80) & 0xBF
return fmt.Sprintf(`%X-%X-%X-%X-%X`, u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
}

View File

@ -39,6 +39,9 @@ func init() {
} }
} }
// BuildHandler is a named request handler for building rest protocol requests
var BuildHandler = request.NamedHandler{Name: "awssdk.rest.Build", Fn: Build}
// Build builds the REST component of a service request. // Build builds the REST component of a service request.
func Build(r *request.Request) { func Build(r *request.Request) {
if r.ParamsFilled() { if r.ParamsFilled() {

View File

@ -3,6 +3,7 @@ package rest
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"reflect" "reflect"
@ -15,6 +16,12 @@ import (
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
) )
// UnmarshalHandler is a named request handler for unmarshaling rest protocol requests
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.rest.Unmarshal", Fn: Unmarshal}
// UnmarshalMetaHandler is a named request handler for unmarshaling rest protocol request metadata
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.rest.UnmarshalMeta", Fn: UnmarshalMeta}
// Unmarshal unmarshals the REST component of a response in a REST service. // Unmarshal unmarshals the REST component of a response in a REST service.
func Unmarshal(r *request.Request) { func Unmarshal(r *request.Request) {
if r.DataFilled() { if r.DataFilled() {
@ -45,6 +52,7 @@ func unmarshalBody(r *request.Request, v reflect.Value) {
if payload.IsValid() { if payload.IsValid() {
switch payload.Interface().(type) { switch payload.Interface().(type) {
case []byte: case []byte:
defer r.HTTPResponse.Body.Close()
b, err := ioutil.ReadAll(r.HTTPResponse.Body) b, err := ioutil.ReadAll(r.HTTPResponse.Body)
if err != nil { if err != nil {
r.Error = awserr.New("SerializationError", "failed to decode REST response", err) r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
@ -52,6 +60,7 @@ func unmarshalBody(r *request.Request, v reflect.Value) {
payload.Set(reflect.ValueOf(b)) payload.Set(reflect.ValueOf(b))
} }
case *string: case *string:
defer r.HTTPResponse.Body.Close()
b, err := ioutil.ReadAll(r.HTTPResponse.Body) b, err := ioutil.ReadAll(r.HTTPResponse.Body)
if err != nil { if err != nil {
r.Error = awserr.New("SerializationError", "failed to decode REST response", err) r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
@ -66,6 +75,8 @@ func unmarshalBody(r *request.Request, v reflect.Value) {
case "aws.ReadSeekCloser", "io.ReadCloser": case "aws.ReadSeekCloser", "io.ReadCloser":
payload.Set(reflect.ValueOf(r.HTTPResponse.Body)) payload.Set(reflect.ValueOf(r.HTTPResponse.Body))
default: default:
io.Copy(ioutil.Discard, r.HTTPResponse.Body)
defer r.HTTPResponse.Body.Close()
r.Error = awserr.New("SerializationError", r.Error = awserr.New("SerializationError",
"failed to decode REST response", "failed to decode REST response",
fmt.Errorf("unknown payload type %s", payload.Type())) fmt.Errorf("unknown payload type %s", payload.Type()))

View File

@ -16,6 +16,18 @@ import (
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil" "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
) )
// BuildHandler is a named request handler for building restxml protocol requests
var BuildHandler = request.NamedHandler{Name: "awssdk.restxml.Build", Fn: Build}
// UnmarshalHandler is a named request handler for unmarshaling restxml protocol requests
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.restxml.Unmarshal", Fn: Unmarshal}
// UnmarshalMetaHandler is a named request handler for unmarshaling restxml protocol request metadata
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.restxml.UnmarshalMeta", Fn: UnmarshalMeta}
// UnmarshalErrorHandler is a named request handler for unmarshaling restxml protocol request errors
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.restxml.UnmarshalError", Fn: UnmarshalError}
// Build builds a request payload for the REST XML protocol. // Build builds a request payload for the REST XML protocol.
func Build(r *request.Request) { func Build(r *request.Request) {
rest.Build(r) rest.Build(r)

View File

@ -0,0 +1,21 @@
package protocol
import (
"io"
"io/ioutil"
"github.com/aws/aws-sdk-go/aws/request"
)
// UnmarshalDiscardBodyHandler is a named request handler to empty and close a response's body
var UnmarshalDiscardBodyHandler = request.NamedHandler{Name: "awssdk.shared.UnmarshalDiscardBody", Fn: UnmarshalDiscardBody}
// UnmarshalDiscardBody is a request handler to empty a response's body and closing it.
func UnmarshalDiscardBody(r *request.Request) {
if r.HTTPResponse == nil || r.HTTPResponse.Body == nil {
return
}
io.Copy(ioutil.Discard, r.HTTPResponse.Body)
r.HTTPResponse.Body.Close()
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package cloudfront
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/protocol/restxml"
"github.com/aws/aws-sdk-go/private/signer/v4"
)
// CloudFront is a client for CloudFront.
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type CloudFront struct {
*client.Client
}
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// A ServiceName is the name of the service the client will make API calls to.
const ServiceName = "cloudfront"
// New creates a new instance of the CloudFront client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a CloudFront client from just a session.
// svc := cloudfront.New(mySession)
//
// // Create a CloudFront client with additional configuration
// svc := cloudfront.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *CloudFront {
c := p.ClientConfig(ServiceName, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *CloudFront {
svc := &CloudFront{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2016-01-28",
},
handlers,
),
}
// Handlers
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBackNamed(restxml.BuildHandler)
svc.Handlers.Unmarshal.PushBackNamed(restxml.UnmarshalHandler)
svc.Handlers.UnmarshalMeta.PushBackNamed(restxml.UnmarshalMetaHandler)
svc.Handlers.UnmarshalError.PushBackNamed(restxml.UnmarshalErrorHandler)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return svc
}
// newRequest creates a new request for a CloudFront operation and runs any
// custom request initialization.
func (c *CloudFront) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
// Run custom request initialization if present
if initRequest != nil {
initRequest(req)
}
return req
}

View File

@ -0,0 +1,76 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package cloudfront
import (
"github.com/aws/aws-sdk-go/private/waiter"
)
func (c *CloudFront) WaitUntilDistributionDeployed(input *GetDistributionInput) error {
waiterCfg := waiter.Config{
Operation: "GetDistribution",
Delay: 60,
MaxAttempts: 25,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "path",
Argument: "Distribution.Status",
Expected: "Deployed",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *CloudFront) WaitUntilInvalidationCompleted(input *GetInvalidationInput) error {
waiterCfg := waiter.Config{
Operation: "GetInvalidation",
Delay: 20,
MaxAttempts: 30,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "path",
Argument: "Invalidation.Status",
Expected: "Completed",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *CloudFront) WaitUntilStreamingDistributionDeployed(input *GetStreamingDistributionInput) error {
waiterCfg := waiter.Config{
Operation: "GetStreamingDistribution",
Delay: 60,
MaxAttempts: 25,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "path",
Argument: "StreamingDistribution.Status",
Expected: "Deployed",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}

22
vendor/github.com/stretchr/testify/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
Please consider promoting this project if you find it useful.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,387 @@
/*
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
* THIS FILE MUST NOT BE EDITED BY HAND
*/
package assert
import (
http "net/http"
url "net/url"
time "time"
)
// Condition uses a Comparison to assert a complex condition.
func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
return Condition(a.t, comp, msgAndArgs...)
}
// Contains asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'")
// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
return Contains(a.t, s, contains, msgAndArgs...)
}
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// a.Empty(obj)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
return Empty(a.t, object, msgAndArgs...)
}
// Equal asserts that two objects are equal.
//
// a.Equal(123, 123, "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
return Equal(a.t, expected, actual, msgAndArgs...)
}
// EqualError asserts that a function returned an error (i.e. not `nil`)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
// if assert.Error(t, err, "An error was expected") {
// assert.Equal(t, err, expectedError)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
return EqualError(a.t, theError, errString, msgAndArgs...)
}
// EqualValues asserts that two objects are equal or convertable to the same types
// and equal.
//
// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
return EqualValues(a.t, expected, actual, msgAndArgs...)
}
// Error asserts that a function returned an error (i.e. not `nil`).
//
// actualObj, err := SomeFunction()
// if a.Error(err, "An error was expected") {
// assert.Equal(t, err, expectedError)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
return Error(a.t, err, msgAndArgs...)
}
// Exactly asserts that two objects are equal is value and type.
//
// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
return Exactly(a.t, expected, actual, msgAndArgs...)
}
// Fail reports a failure through
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
return Fail(a.t, failureMessage, msgAndArgs...)
}
// FailNow fails test
func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool {
return FailNow(a.t, failureMessage, msgAndArgs...)
}
// False asserts that the specified value is false.
//
// a.False(myBool, "myBool should be false")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
return False(a.t, value, msgAndArgs...)
}
// HTTPBodyContains asserts that a specified handler returns a
// body that contains a string.
//
// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
return HTTPBodyContains(a.t, handler, method, url, values, str)
}
// HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string.
//
// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
return HTTPBodyNotContains(a.t, handler, method, url, values, str)
}
// HTTPError asserts that a specified handler returns an error status code.
//
// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool {
return HTTPError(a.t, handler, method, url, values)
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
//
// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool {
return HTTPRedirect(a.t, handler, method, url, values)
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool {
return HTTPSuccess(a.t, handler, method, url, values)
}
// Implements asserts that an object is implemented by the specified interface.
//
// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject")
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
return Implements(a.t, interfaceObject, object, msgAndArgs...)
}
// InDelta asserts that the two numerals are within delta of each other.
//
// a.InDelta(math.Pi, (22 / 7.0), 0.01)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
return InDelta(a.t, expected, actual, delta, msgAndArgs...)
}
// InDeltaSlice is the same as InDelta, except it compares two slices.
func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
}
// InEpsilon asserts that expected and actual have a relative error less than epsilon
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
}
// InEpsilonSlice is the same as InEpsilon, except it compares two slices.
func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
return InEpsilonSlice(a.t, expected, actual, delta, msgAndArgs...)
}
// IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
return IsType(a.t, expectedType, object, msgAndArgs...)
}
// JSONEq asserts that two JSON strings are equivalent.
//
// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
return JSONEq(a.t, expected, actual, msgAndArgs...)
}
// Len asserts that the specified object has specific length.
// Len also fails if the object has a type that len() not accept.
//
// a.Len(mySlice, 3, "The size of slice is not 3")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
return Len(a.t, object, length, msgAndArgs...)
}
// Nil asserts that the specified object is nil.
//
// a.Nil(err, "err should be nothing")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
return Nil(a.t, object, msgAndArgs...)
}
// NoError asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
// if a.NoError(err) {
// assert.Equal(t, actualObj, expectedObj)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
return NoError(a.t, err, msgAndArgs...)
}
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
return NotContains(a.t, s, contains, msgAndArgs...)
}
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if a.NotEmpty(obj) {
// assert.Equal(t, "two", obj[1])
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
return NotEmpty(a.t, object, msgAndArgs...)
}
// NotEqual asserts that the specified values are NOT equal.
//
// a.NotEqual(obj1, obj2, "two objects shouldn't be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
return NotEqual(a.t, expected, actual, msgAndArgs...)
}
// NotNil asserts that the specified object is not nil.
//
// a.NotNil(err, "err should be something")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
return NotNil(a.t, object, msgAndArgs...)
}
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
// a.NotPanics(func(){
// RemainCalm()
// }, "Calling RemainCalm() should NOT panic")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
return NotPanics(a.t, f, msgAndArgs...)
}
// NotRegexp asserts that a specified regexp does not match a string.
//
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
// a.NotRegexp("^start", "it's not starting")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
return NotRegexp(a.t, rx, str, msgAndArgs...)
}
// NotZero asserts that i is not the zero value for its type and returns the truth.
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
return NotZero(a.t, i, msgAndArgs...)
}
// Panics asserts that the code inside the specified PanicTestFunc panics.
//
// a.Panics(func(){
// GoCrazy()
// }, "Calling GoCrazy() should panic")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
return Panics(a.t, f, msgAndArgs...)
}
// Regexp asserts that a specified regexp matches a string.
//
// a.Regexp(regexp.MustCompile("start"), "it's starting")
// a.Regexp("start...$", "it's not starting")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
return Regexp(a.t, rx, str, msgAndArgs...)
}
// True asserts that the specified value is true.
//
// a.True(myBool, "myBool should be true")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
return True(a.t, value, msgAndArgs...)
}
// WithinDuration asserts that the two times are within duration delta of each other.
//
// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
return WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
}
// Zero asserts that i is the zero value for its type and returns the truth.
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
return Zero(a.t, i, msgAndArgs...)
}

View File

@ -0,0 +1,4 @@
{{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool {
return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
}

1004
vendor/github.com/stretchr/testify/assert/assertions.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

45
vendor/github.com/stretchr/testify/assert/doc.go generated vendored Normal file
View File

@ -0,0 +1,45 @@
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
//
// Example Usage
//
// The following is a complete example using assert in a standard test function:
// import (
// "testing"
// "github.com/stretchr/testify/assert"
// )
//
// func TestSomething(t *testing.T) {
//
// var a string = "Hello"
// var b string = "Hello"
//
// assert.Equal(t, a, b, "The two words should be the same.")
//
// }
//
// if you assert many times, use the format below:
//
// import (
// "testing"
// "github.com/stretchr/testify/assert"
// )
//
// func TestSomething(t *testing.T) {
// assert := assert.New(t)
//
// var a string = "Hello"
// var b string = "Hello"
//
// assert.Equal(a, b, "The two words should be the same.")
// }
//
// Assertions
//
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
// testing framework. This allows the assertion funcs to write the failings and other details to
// the correct place.
//
// Every assertion function also takes an optional string message as the final argument,
// allowing custom error messages to be appended to the message the assertion method outputs.
package assert

10
vendor/github.com/stretchr/testify/assert/errors.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
package assert
import (
"errors"
)
// AnError is an error instance useful for testing. If the code does not care
// about error specifics, and only needs to return the error for example, this
// error should be used to make the test code more readable.
var AnError = errors.New("assert.AnError general error for testing")

View File

@ -0,0 +1,16 @@
package assert
// Assertions provides assertion methods around the
// TestingT interface.
type Assertions struct {
t TestingT
}
// New makes a new Assertions object for the specified TestingT.
func New(t TestingT) *Assertions {
return &Assertions{
t: t,
}
}
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl

View File

@ -0,0 +1,106 @@
package assert
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
)
// httpCode is a helper that returns HTTP code of the response. It returns -1
// if building a new request fails.
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int {
w := httptest.NewRecorder()
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil {
return -1
}
handler(w, req)
return w.Code
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
return code >= http.StatusOK && code <= http.StatusPartialContent
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
//
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
}
// HTTPError asserts that a specified handler returns an error status code.
//
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
return code >= http.StatusBadRequest
}
// HTTPBody is a helper that returns HTTP body of the response. It returns
// empty string if building a new request fails.
func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
w := httptest.NewRecorder()
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil {
return ""
}
handler(w, req)
return w.Body.String()
}
// HTTPBodyContains asserts that a specified handler returns a
// body that contains a string.
//
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str))
if !contains {
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
}
return contains
}
// HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string.
//
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str))
if contains {
Fail(t, "Expected response body for %s to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)
}
return !contains
}

23
vendor/vendor.json vendored
View File

@ -67,6 +67,11 @@
"revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56", "revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56",
"revisionTime": "2016-01-21T13:38:05-08:00" "revisionTime": "2016-01-21T13:38:05-08:00"
}, },
{
"path": "github.com/aws/aws-sdk-go/private/protocol",
"revision": "996f601d8198d4fdc49bb5d93c73f27a6bdfe900",
"revisionTime": "2016-03-09T12:56:27-08:00"
},
{ {
"path": "github.com/aws/aws-sdk-go/private/protocol/query", "path": "github.com/aws/aws-sdk-go/private/protocol/query",
"revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56", "revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56",
@ -79,13 +84,13 @@
}, },
{ {
"path": "github.com/aws/aws-sdk-go/private/protocol/rest", "path": "github.com/aws/aws-sdk-go/private/protocol/rest",
"revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56", "revision": "996f601d8198d4fdc49bb5d93c73f27a6bdfe900",
"revisionTime": "2016-01-21T13:38:05-08:00" "revisionTime": "2016-03-09T12:56:27-08:00"
}, },
{ {
"path": "github.com/aws/aws-sdk-go/private/protocol/restxml", "path": "github.com/aws/aws-sdk-go/private/protocol/restxml",
"revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56", "revision": "996f601d8198d4fdc49bb5d93c73f27a6bdfe900",
"revisionTime": "2016-01-21T13:38:05-08:00" "revisionTime": "2016-03-09T12:56:27-08:00"
}, },
{ {
"path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", "path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
@ -102,6 +107,11 @@
"revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56", "revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56",
"revisionTime": "2016-01-21T13:38:05-08:00" "revisionTime": "2016-01-21T13:38:05-08:00"
}, },
{
"path": "github.com/aws/aws-sdk-go/service/cloudFront",
"revision": "996f601d8198d4fdc49bb5d93c73f27a6bdfe900",
"revisionTime": "2016-03-09T12:56:27-08:00"
},
{ {
"path": "github.com/aws/aws-sdk-go/service/s3", "path": "github.com/aws/aws-sdk-go/service/s3",
"revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56", "revision": "f22f230de2ce5d5bd15c415fb72d30a77931ac56",
@ -124,6 +134,11 @@
"revision": "0067a9abd927e50aed5190662702f81231413ae0", "revision": "0067a9abd927e50aed5190662702f81231413ae0",
"revisionTime": "2014-06-17T10:15:57-07:00" "revisionTime": "2014-06-17T10:15:57-07:00"
}, },
{
"path": "github.com/stretchr/testify/assert",
"revision": "6fe211e493929a8aac0469b93f28b1d0688a9a3a",
"revisionTime": "2016-03-05T16:54:46Z"
},
{ {
"path": "golang.org/x/net/context", "path": "golang.org/x/net/context",
"revision": "6ccd6698c634f5d835c40c1c31848729e0cecda1", "revision": "6ccd6698c634f5d835c40c1c31848729e0cecda1",