Merge pull request #1575 from hashicorp/dockerize-pg-secret-tests

Dockerize Postgres secret backend acceptance tests
This commit is contained in:
Jeff Mitchell 2016-06-30 20:48:26 +02:00 committed by GitHub
commit 742b4c72bc
25 changed files with 2218 additions and 60 deletions

View File

@ -6,14 +6,71 @@ import (
"log"
"os"
"reflect"
"sync"
"testing"
"time"
"github.com/hashicorp/vault/logical"
logicaltest "github.com/hashicorp/vault/logical/testing"
"github.com/lib/pq"
"github.com/mitchellh/mapstructure"
"github.com/ory-am/dockertest"
)
var (
testImagePull sync.Once
)
func prepareTestContainer(t *testing.T, s logical.Storage, b logical.Backend) (cid dockertest.ContainerID, retURL string) {
if os.Getenv("PG_URL") != "" {
return "", os.Getenv("PG_URL")
}
// Without this the checks for whether the container has started seem to
// never actually pass. There's really no reason to expose the test
// containers, so don't.
dockertest.BindDockerToLocalhost = "yep"
testImagePull.Do(func() {
dockertest.Pull("postgres")
})
cid, connErr := dockertest.ConnectToPostgreSQL(60, 500*time.Millisecond, func(connURL string) bool {
// This will cause a validation to run
resp, err := b.HandleRequest(&logical.Request{
Storage: s,
Operation: logical.UpdateOperation,
Path: "config/connection",
Data: map[string]interface{}{
"connection_url": connURL,
},
})
if err != nil || (resp != nil && resp.IsError()) {
// It's likely not up and running yet, so return false and try again
return false
}
if resp == nil {
t.Fatal("expected warning")
}
retURL = connURL
return true
})
if connErr != nil {
t.Fatalf("could not connect to database: %v", connErr)
}
return
}
func cleanupTestContainer(t *testing.T, cid dockertest.ContainerID) {
err := cid.KillRemove()
if err != nil {
t.Fatal(err)
}
}
func TestBackend_config_connection(t *testing.T) {
var resp *logical.Response
var err error
@ -55,43 +112,51 @@ func TestBackend_config_connection(t *testing.T) {
}
func TestBackend_basic(t *testing.T) {
b, _ := Factory(logical.TestBackendConfig())
d1 := map[string]interface{}{
"connection_url": os.Getenv("PG_URL"),
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(config)
if err != nil {
t.Fatal(err)
}
d2 := map[string]interface{}{
"value": os.Getenv("PG_URL"),
cid, connURL := prepareTestContainer(t, config.StorageView, b)
if cid != "" {
defer cleanupTestContainer(t, cid)
}
connData := map[string]interface{}{
"connection_url": connURL,
}
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
PreCheck: func() { testAccPreCheck(t) },
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepConfig(t, d1, false),
testAccStepConfig(t, connData, false),
testAccStepRole(t),
testAccStepReadCreds(t, b, "web"),
testAccStepConfig(t, d2, false),
testAccStepRole(t),
testAccStepReadCreds(t, b, "web"),
testAccStepReadCreds(t, b, "web", connURL),
},
})
}
func TestBackend_roleCrud(t *testing.T) {
b, _ := Factory(logical.TestBackendConfig())
d := map[string]interface{}{
"connection_url": os.Getenv("PG_URL"),
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(config)
if err != nil {
t.Fatal(err)
}
cid, connURL := prepareTestContainer(t, config.StorageView, b)
if cid != "" {
defer cleanupTestContainer(t, cid)
}
connData := map[string]interface{}{
"connection_url": connURL,
}
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
PreCheck: func() { testAccPreCheck(t) },
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepConfig(t, d, false),
testAccStepConfig(t, connData, false),
testAccStepRole(t),
testAccStepReadRole(t, "web", testRole),
testAccStepDeleteRole(t, "web"),
@ -100,39 +165,6 @@ func TestBackend_roleCrud(t *testing.T) {
})
}
func TestBackend_configConnection(t *testing.T) {
b, _ := Factory(logical.TestBackendConfig())
d1 := map[string]interface{}{
"value": os.Getenv("PG_URL"),
}
d2 := map[string]interface{}{
"connection_url": os.Getenv("PG_URL"),
}
d3 := map[string]interface{}{
"value": os.Getenv("PG_URL"),
"connection_url": os.Getenv("PG_URL"),
}
d4 := map[string]interface{}{}
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
PreCheck: func() { testAccPreCheck(t) },
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepConfig(t, d1, false),
testAccStepConfig(t, d2, false),
testAccStepConfig(t, d3, false),
testAccStepConfig(t, d4, true),
},
})
}
func testAccPreCheck(t *testing.T) {
if v := os.Getenv("PG_URL"); v == "" {
t.Fatal("PG_URL must be set for acceptance tests")
}
}
func testAccStepConfig(t *testing.T, d map[string]interface{}, expectError bool) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
@ -154,8 +186,8 @@ func testAccStepConfig(t *testing.T, d map[string]interface{}, expectError bool)
return fmt.Errorf("expected error, but write succeeded.")
}
return nil
} else if resp != nil {
return fmt.Errorf("response should be nil")
} else if resp != nil && resp.IsError() {
return fmt.Errorf("got an error response: %v", resp.Error())
}
return nil
},
@ -179,7 +211,7 @@ func testAccStepDeleteRole(t *testing.T, n string) logicaltest.TestStep {
}
}
func testAccStepReadCreds(t *testing.T, b logical.Backend, name string) logicaltest.TestStep {
func testAccStepReadCreds(t *testing.T, b logical.Backend, name string, connURL string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.ReadOperation,
Path: "creds/" + name,
@ -193,7 +225,7 @@ func testAccStepReadCreds(t *testing.T, b logical.Backend, name string) logicalt
}
log.Printf("[WARN] Generated credentials: %v", d)
conn, err := pq.ParseURL(os.Getenv("PG_URL"))
conn, err := pq.ParseURL(connURL)
if err != nil {
t.Fatal(err)
}

View File

@ -18,16 +18,19 @@ func pathConfigConnection(b *backend) *framework.Path {
Type: framework.TypeString,
Description: "DB connection string",
},
"value": &framework.FieldSchema{
Type: framework.TypeString,
Description: `DB connection string. Use 'connection_url' instead.
This will be deprecated.`,
},
"verify_connection": &framework.FieldSchema{
Type: framework.TypeBool,
Default: true,
Description: `If set, connection_url is verified by actually connecting to the database`,
},
"max_open_connections": &framework.FieldSchema{
Type: framework.TypeInt,
Description: `Maximum number of open connections to the database;
@ -35,7 +38,6 @@ a zero uses the default value of two and a
negative value means unlimited`,
},
// Implementation note:
"max_idle_connections": &framework.FieldSchema{
Type: framework.TypeInt,
Description: `Maximum number of idle connections to the database;

View File

@ -97,6 +97,18 @@ func (b *backend) secretCredsRevoke(
return nil, err
}
// Check if the role exists
var exists bool
query := fmt.Sprintf("SELECT exists (SELECT rolname FROM pg_roles WHERE rolname='%s');", username)
err = db.QueryRow(query).Scan(&exists)
if err != nil && err != sql.ErrNoRows {
return nil, err
}
if exists == false {
return nil, nil
}
// Query for permissions; we need to revoke permissions before we can drop
// the role
// This isn't done in a transaction because even if we fail along the way,

View File

@ -1,6 +1,7 @@
package logical
import (
"errors"
"fmt"
"reflect"
"time"
@ -154,6 +155,19 @@ func (r *Response) IsError() bool {
return r != nil && len(r.Data) == 1 && r.Data["error"] != nil
}
func (r *Response) Error() error {
if !r.IsError() {
return nil
}
switch r.Data["error"].(type) {
case string:
return errors.New(r.Data["error"].(string))
case error:
return r.Data["error"].(error)
}
return nil
}
// HelpResponse is used to format a help response
func HelpResponse(text string, seeAlso []string) *Response {
return &Response{

202
vendor/camlistore.org/COPYING 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.

275
vendor/camlistore.org/pkg/netutil/ident.go generated vendored Normal file
View File

@ -0,0 +1,275 @@
/*
Copyright 2011 Google Inc.
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.
*/
// Package netutil identifies the system userid responsible for
// localhost TCP connections.
package netutil // import "camlistore.org/pkg/netutil"
import (
"bufio"
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"os"
"os/exec"
"os/user"
"regexp"
"runtime"
"strconv"
"strings"
)
var (
ErrNotFound = errors.New("netutil: connection not found")
ErrUnsupportedOS = errors.New("netutil: not implemented on this operating system")
)
// ConnUserid returns the uid that owns the given localhost connection.
// The returned error is ErrNotFound if the connection wasn't found.
func ConnUserid(conn net.Conn) (uid int, err error) {
return AddrPairUserid(conn.LocalAddr(), conn.RemoteAddr())
}
// HostPortToIP parses a host:port to a TCPAddr without resolving names.
// If given a context IP, it will resolve localhost to match the context's IP family.
func HostPortToIP(hostport string, ctx *net.TCPAddr) (hostaddr *net.TCPAddr, err error) {
host, port, err := net.SplitHostPort(hostport)
if err != nil {
return nil, err
}
iport, err := strconv.Atoi(port)
if err != nil || iport < 0 || iport > 0xFFFF {
return nil, fmt.Errorf("invalid port %d", iport)
}
var addr net.IP
if ctx != nil && host == "localhost" {
if ctx.IP.To4() != nil {
addr = net.IPv4(127, 0, 0, 1)
} else {
addr = net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
}
} else if addr = net.ParseIP(host); addr == nil {
return nil, fmt.Errorf("could not parse IP %s", host)
}
return &net.TCPAddr{IP: addr, Port: iport}, nil
}
// AddrPairUserid returns the local userid who owns the TCP connection
// given by the local and remote ip:port (lipport and ripport,
// respectively). Returns ErrNotFound for the error if the TCP connection
// isn't found.
func AddrPairUserid(local, remote net.Addr) (uid int, err error) {
lAddr, lOk := local.(*net.TCPAddr)
rAddr, rOk := remote.(*net.TCPAddr)
if !(lOk && rOk) {
return -1, fmt.Errorf("netutil: Could not convert Addr to TCPAddr.")
}
localv4 := (lAddr.IP.To4() != nil)
remotev4 := (rAddr.IP.To4() != nil)
if localv4 != remotev4 {
return -1, fmt.Errorf("netutil: address pairs of different families; localv4=%v, remotev4=%v",
localv4, remotev4)
}
switch runtime.GOOS {
case "darwin":
return uidFromLsof(lAddr.IP, lAddr.Port, rAddr.IP, rAddr.Port)
case "freebsd":
return uidFromSockstat(lAddr.IP, lAddr.Port, rAddr.IP, rAddr.Port)
case "linux":
file := "/proc/net/tcp"
if !localv4 {
file = "/proc/net/tcp6"
}
f, err := os.Open(file)
if err != nil {
return -1, fmt.Errorf("Error opening %s: %v", file, err)
}
defer f.Close()
return uidFromProcReader(lAddr.IP, lAddr.Port, rAddr.IP, rAddr.Port, f)
}
return 0, ErrUnsupportedOS
}
func toLinuxIPv4Order(b []byte) []byte {
binary.BigEndian.PutUint32(b, binary.LittleEndian.Uint32(b))
return b
}
func toLinuxIPv6Order(b []byte) []byte {
for i := 0; i < 16; i += 4 {
sb := b[i : i+4]
binary.BigEndian.PutUint32(sb, binary.LittleEndian.Uint32(sb))
}
return b
}
type maybeBrackets net.IP
func (p maybeBrackets) String() string {
s := net.IP(p).String()
if strings.Contains(s, ":") {
return "[" + s + "]"
}
return s
}
// Changed by tests.
var uidFromUsername = uidFromUsernameFn
func uidFromUsernameFn(username string) (uid int, err error) {
if uid := os.Getuid(); uid != 0 && username == os.Getenv("USER") {
return uid, nil
}
u, err := user.Lookup(username)
if err == nil {
uid, err := strconv.Atoi(u.Uid)
return uid, err
}
return 0, err
}
func uidFromLsof(lip net.IP, lport int, rip net.IP, rport int) (uid int, err error) {
seek := fmt.Sprintf("%s:%d->%s:%d", maybeBrackets(lip), lport, maybeBrackets(rip), rport)
seekb := []byte(seek)
if _, err = exec.LookPath("lsof"); err != nil {
return
}
cmd := exec.Command("lsof",
"-b", // avoid system calls that could block
"-w", // and don't warn about cases where -b fails
"-n", // don't resolve network names
"-P", // don't resolve network ports,
// TODO(bradfitz): pass down the uid we care about, then do: ?
//"-a", // AND the following together:
// "-u", strconv.Itoa(uid) // just this uid
"-itcp") // we only care about TCP connections
stdout, err := cmd.StdoutPipe()
if err != nil {
return
}
defer cmd.Wait()
defer stdout.Close()
err = cmd.Start()
if err != nil {
return
}
defer cmd.Process.Kill()
br := bufio.NewReader(stdout)
for {
line, err := br.ReadSlice('\n')
if err == io.EOF {
break
}
if err != nil {
return -1, err
}
if !bytes.Contains(line, seekb) {
continue
}
// SystemUIS 276 bradfitz 15u IPv4 0xffffff801a7c74e0 0t0 TCP 127.0.0.1:56718->127.0.0.1:5204 (ESTABLISHED)
f := bytes.Fields(line)
if len(f) < 8 {
continue
}
username := string(f[2])
return uidFromUsername(username)
}
return -1, ErrNotFound
}
func uidFromSockstat(lip net.IP, lport int, rip net.IP, rport int) (int, error) {
cmd := exec.Command("sockstat", "-Ptcp")
stdout, err := cmd.StdoutPipe()
if err != nil {
return -1, err
}
defer cmd.Wait()
defer stdout.Close()
err = cmd.Start()
if err != nil {
return -1, err
}
defer cmd.Process.Kill()
return uidFromSockstatReader(lip, lport, rip, rport, stdout)
}
func uidFromSockstatReader(lip net.IP, lport int, rip net.IP, rport int, r io.Reader) (int, error) {
pat, err := regexp.Compile(fmt.Sprintf(`^([^ ]+).*%s:%d *%s:%d$`,
lip.String(), lport, rip.String(), rport))
if err != nil {
return -1, err
}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
l := scanner.Text()
m := pat.FindStringSubmatch(l)
if len(m) == 2 {
return uidFromUsername(m[1])
}
}
if err := scanner.Err(); err != nil {
return -1, err
}
return -1, ErrNotFound
}
func uidFromProcReader(lip net.IP, lport int, rip net.IP, rport int, r io.Reader) (uid int, err error) {
buf := bufio.NewReader(r)
localHex := ""
remoteHex := ""
ipv4 := lip.To4() != nil
if ipv4 {
// In the kernel, the port is run through ntohs(), and
// the inet_request_socket in
// include/net/inet_socket.h says the "loc_addr" and
// "rmt_addr" fields are __be32, but get_openreq4's
// printf of them is raw, without byte order
// converstion.
localHex = fmt.Sprintf("%08X:%04X", toLinuxIPv4Order([]byte(lip.To4())), lport)
remoteHex = fmt.Sprintf("%08X:%04X", toLinuxIPv4Order([]byte(rip.To4())), rport)
} else {
localHex = fmt.Sprintf("%032X:%04X", toLinuxIPv6Order([]byte(lip.To16())), lport)
remoteHex = fmt.Sprintf("%032X:%04X", toLinuxIPv6Order([]byte(rip.To16())), rport)
}
for {
line, err := buf.ReadString('\n')
if err != nil {
return -1, ErrNotFound
}
parts := strings.Fields(strings.TrimSpace(line))
if len(parts) < 8 {
continue
}
// log.Printf("parts[1] = %q; localHex = %q", parts[1], localHex)
if parts[1] == localHex && parts[2] == remoteHex {
uid, err = strconv.Atoi(parts[7])
return uid, err
}
}
panic("unreachable")
}

141
vendor/camlistore.org/pkg/netutil/netutil.go generated vendored Normal file
View File

@ -0,0 +1,141 @@
/*
Copyright 2014 The Camlistore Authors
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.
*/
package netutil
import (
"errors"
"fmt"
"net"
"net/url"
"strings"
"time"
)
// AwaitReachable tries to make a TCP connection to addr regularly.
// It returns an error if it's unable to make a connection before maxWait.
func AwaitReachable(addr string, maxWait time.Duration) error {
done := time.Now().Add(maxWait)
for time.Now().Before(done) {
c, err := net.Dial("tcp", addr)
if err == nil {
c.Close()
return nil
}
time.Sleep(100 * time.Millisecond)
}
return fmt.Errorf("%v unreachable for %v", addr, maxWait)
}
// HostPort takes a urlStr string URL, and returns a host:port string suitable
// to passing to net.Dial, with the port set as the scheme's default port if
// absent.
func HostPort(urlStr string) (string, error) {
// TODO: rename this function to URLHostPort instead, like
// ListenHostPort below.
u, err := url.Parse(urlStr)
if err != nil {
return "", fmt.Errorf("could not parse %q as a url: %v", urlStr, err)
}
if u.Scheme == "" {
return "", fmt.Errorf("url %q has no scheme", urlStr)
}
hostPort := u.Host
if hostPort == "" || strings.HasPrefix(hostPort, ":") {
return "", fmt.Errorf("url %q has no host", urlStr)
}
idx := strings.Index(hostPort, "]")
if idx == -1 {
idx = 0
}
if !strings.Contains(hostPort[idx:], ":") {
if u.Scheme == "https" {
hostPort += ":443"
} else {
hostPort += ":80"
}
}
return hostPort, nil
}
// ListenHostPort maps a listen address into a host:port string.
// If the host part in listenAddr is empty or 0.0.0.0, localhost
// is used instead.
func ListenHostPort(listenAddr string) (string, error) {
hp := listenAddr
if strings.HasPrefix(hp, ":") {
hp = "localhost" + hp
} else if strings.HasPrefix(hp, "0.0.0.0:") {
hp = "localhost:" + hp[len("0.0.0.0:"):]
}
if _, _, err := net.SplitHostPort(hp); err != nil {
return "", err
}
return hp, nil
}
// ListenOnLocalRandomPort returns a TCP listener on a random
// localhost port.
func ListenOnLocalRandomPort() (net.Listener, error) {
ip, err := Localhost()
if err != nil {
return nil, err
}
return net.ListenTCP("tcp", &net.TCPAddr{IP: ip, Port: 0})
}
// Localhost returns the first address found when
// doing a lookup of "localhost". If not successful,
// it looks for an ip on the loopback interfaces.
func Localhost() (net.IP, error) {
if ip := localhostLookup(); ip != nil {
return ip, nil
}
if ip := loopbackIP(); ip != nil {
return ip, nil
}
return nil, errors.New("No loopback ip found.")
}
// localhostLookup looks for a loopback IP by resolving localhost.
func localhostLookup() net.IP {
if ips, err := net.LookupIP("localhost"); err == nil && len(ips) > 0 {
return ips[0]
}
return nil
}
// loopbackIP returns the first loopback IP address sniffing network
// interfaces or nil if none is found.
func loopbackIP() net.IP {
interfaces, err := net.Interfaces()
if err != nil {
return nil
}
for _, inf := range interfaces {
const flagUpLoopback = net.FlagUp | net.FlagLoopback
if inf.Flags&flagUpLoopback == flagUpLoopback {
addrs, _ := inf.Addrs()
for _, addr := range addrs {
ip, _, err := net.ParseCIDR(addr.String())
if err == nil && ip.IsLoopback() {
return ip
}
}
}
}
return nil
}

202
vendor/github.com/ory-am/dockertest/LICENSE 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 2014 The Camlistore Authors
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.

258
vendor/github.com/ory-am/dockertest/README.md generated vendored Normal file
View File

@ -0,0 +1,258 @@
# [ory.am](https://ory.am)/dockertest
[![Build Status](https://travis-ci.org/ory-am/dockertest.svg)](https://travis-ci.org/ory-am/dockertest?branch=master)
[![Coverage Status](https://coveralls.io/repos/ory-am/dockertest/badge.svg?branch=master&service=github)](https://coveralls.io/github/ory-am/dockertest?branch=master)
Use Docker to run your Go language integration tests against third party services on **Microsoft Windows, Mac OSX and Linux**!
Dockertest uses [docker-machine](https://docs.docker.com/machine/) (aka [Docker Toolbox](https://www.docker.com/toolbox)) to spin up images on Windows and Mac OSX as well.
Dockertest is based on [docker.go](https://github.com/camlistore/camlistore/blob/master/pkg/test/dockertest/docker.go)
from [camlistore](https://github.com/camlistore/camlistore).
This fork detects automatically, if [Docker Toolbox](https://www.docker.com/toolbox)
is installed. If it is, Docker integration on Windows and Mac OSX can be used without any additional work.
To avoid port collisions when using docker-machine, Dockertest chooses a random port to bind the requested image.
Dockertest ships with support for these backends:
* PostgreSQL
* MySQL
* MongoDB
* NSQ
* Redis
* Elastic Search
* RethinkDB
* RabbitMQ
* Mockserver
* ActiveMQ
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**
- [Why should I use Dockertest?](#why-should-i-use-dockertest)
- [Installing and using Dockertest](#installing-and-using-dockertest)
- [Start a container](#start-a-container)
- [Write awesome tests](#write-awesome-tests)
- [Setting up Travis-CI](#setting-up-travis-ci)
- [Troubleshoot & FAQ](#troubleshoot-&-faq)
- [I want to use a specific image version](#i-want-to-use-a-specific-image-version)
- [My build is broken!](#my-build-is-broken)
- [Out of disk space](#out-of-disk-space)
- [Removing old containers](#removing-old-containers)
- [Customized database] (#Customized-database)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Why should I use Dockertest?
When developing applications, it is often necessary to use services that talk to a database system.
Unit Testing these services can be cumbersome because mocking database/DBAL is strenuous. Making slight changes to the
schema implies rewriting at least some, if not all of the mocks. The same goes for API changes in the DBAL.
To avoid this, it is smarter to test these specific services against a real database that is destroyed after testing.
Docker is the perfect system for running unit tests as you can spin up containers in a few seconds and kill them when
the test completes. The Dockertest library provides easy to use commands for spinning up Docker containers and using
them for your tests.
## Installing and using Dockertest
Using Dockertest is straightforward and simple. Check the [releases tab](https://github.com/ory-am/dockertest/releases)
for available releases.
To install dockertest, run
```
go get gopkg.in/ory-am/dockertest.vX
```
where `X` is your desired version. For example:
```
go get gopkg.in/ory-am/dockertest.v2
```
**Note:**
When using the Docker Toolbox (Windows / OSX), make sure that the VM is started by running `docker-machine start default`.
### Start a container
```go
package main
import (
"gopkg.in/ory-am/dockertest.v2"
"gopkg.in/mgo.v2"
"time"
)
func main() {
var db *mgo.Session
c, err := dockertest.ConnectToMongoDB(15, time.Millisecond*500, func(url string) bool {
// This callback function checks if the image's process is responsive.
// Sometimes, docker images are booted but the process (in this case MongoDB) is still doing maintenance
// before being fully responsive which might cause issues like "TCP Connection reset by peer".
var err error
db, err = mgo.Dial(url)
if err != nil {
return false
}
// Sometimes, dialing the database is not enough because the port is already open but the process is not responsive.
// Most database conenctors implement a ping function which can be used to test if the process is responsive.
// Alternatively, you could execute a query to see if an error occurs or not.
return db.Ping() == nil
})
if err != nil {
log.Fatalf("Could not connect to database: %s", err)
}
// Close db connection and kill the container when we leave this function body.
defer db.Close()
defer c.KillRemove()
// The image is now responsive.
}
```
You can start PostgreSQL and MySQL in a similar fashion.
It is also possible to start a custom container (in this example, a RabbitMQ container):
```go
c, ip, port, err := dockertest.SetupCustomContainer("rabbitmq", 5672, 10*time.Second)
if err != nil {
log.Fatalf("Could not setup container: %s", err
}
defer c.KillRemove()
err = dockertest.ConnectToCustomContainer(fmt.Sprintf("%v:%v", ip, port), 15, time.Millisecond*500, func(url string) bool {
amqp, err := amqp.Dial(fmt.Sprintf("amqp://%v", url))
if err != nil {
return false
}
defer amqp.Close()
return true
})
...
```
## Write awesome tests
It is a good idea to start up the container only once when running tests.
```go
import (
"fmt"
"testing"
"log"
"os"
"database/sql"
_ "github.com/lib/pq"
"gopkg.in/ory-am/dockertest.v2"
)
var db *sql.DB
func TestMain(m *testing.M) {
c, err := dockertest.ConnectToPostgreSQL(15, time.Second, func(url string) bool {
// Check if postgres is responsive...
var err error
db, err = sql.Open("postgres", url)
if err != nil {
return false
}
return db.Ping() == nil
})
if err != nil {
log.Fatalf("Could not connect to database: %s", err)
}
// Execute tasks like setting up schemata.
// Run tests
result := m.Run()
// Close database connection.
db.Close()
// Clean up image.
c.KillRemove()
// Exit tests.
os.Exit(result)
}
func TestFunction(t *testing.T) {
// db.Exec(...
}
```
### Setting up Travis-CI
You can run the Docker integration on Travis easily:
```yml
# Sudo is required for docker
sudo: required
# Enable docker
services:
- docker
# In Travis, we need to bind to 127.0.0.1 in order to get a working connection. This environment variable
# tells dockertest to do that.
env:
- DOCKERTEST_BIND_LOCALHOST=true
```
## Troubleshoot & FAQ
### I need to use a specific container version for XYZ
You can specify a container version by setting environment variables or globals. For more information, check [vars.go](vars.go).
### My build is broken!
With v2, we removed all `Open*` methods to reduce duplicate code, unnecessary dependencies and make maintenance easier.
If you relied on these, run `go get gopkg.in/ory-am/dockertest.v1` and replace
`import "github.com/ory-am/dockertest"` with `import "gopkg.in/ory-am/dockertest.v1"`.
### Out of disk space
Try cleaning up the images with [docker-cleanup-volumes](https://github.com/chadoe/docker-cleanup-volumes).
### Removing old containers
Sometimes container clean up fails. Check out
[this stackoverflow question](http://stackoverflow.com/questions/21398087/how-to-delete-dockers-images) on how to fix this.
### Customized database
I am using postgres (or mysql) driver, how do I use customized database instead of default one?
You can alleviate this helper function to do that, see testcase or example below:
```go
func TestMain(m *testing.M) {
if c, err := dockertest.ConnectToPostgreSQL(15, time.Second, func(url string) bool {
customizedDB := "cherry" // here I am connecting cherry database
newURL, err := SetUpPostgreDatabase(customizedDB, url)
// or use SetUpMysqlDatabase for mysql driver
if err != nil {
log.Fatal(err)
}
db, err := sql.Open("postgres", newURL)
if err != nil {
return false
}
return db.Ping() == nil
}); err != nil {
log.Fatal(err)
}
```
*Thanks to our sponsors: Ory GmbH & Imarum GmbH*

43
vendor/github.com/ory-am/dockertest/activemq.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package dockertest
import (
"errors"
"fmt"
"log"
"time"
)
// SetupActiveMQContainer sets up a real ActiveMQ instance for testing purposes,
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupActiveMQContainer() (c ContainerID, ip string, port int, err error) {
port = RandomPort()
forward := fmt.Sprintf("%d:%d", port, 61613)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(ActiveMQImageName, port, 10*time.Second, func() (string, error) {
res, err := run("--name", GenerateContainerID(), "-d", "-P", "-p", forward, ActiveMQImageName)
return res, err
})
return
}
// ConnectToActiveMQ starts a ActiveMQ image and passes the amqp url to the connector callback.
// The url will match the ip:port pattern (e.g. 123.123.123.123:4241)
func ConnectToActiveMQ(tries int, delay time.Duration, connector func(url string) bool) (c ContainerID, err error) {
c, ip, port, err := SetupActiveMQContainer()
if err != nil {
return c, fmt.Errorf("Could not set up ActiveMQ container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
url := fmt.Sprintf("%s:%d", ip, port)
if connector(url) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up ActiveMQ container.")
}

65
vendor/github.com/ory-am/dockertest/container.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
package dockertest
import (
"camlistore.org/pkg/netutil"
"fmt"
"os/exec"
"strings"
"time"
)
// ContainerID represents a container and offers methods like Kill or IP.
type ContainerID string
// IP retrieves the container's IP address.
func (c ContainerID) IP() (string, error) {
return IP(string(c))
}
// Kill runs "docker kill" on the container.
func (c ContainerID) Kill() error {
return KillContainer(string(c))
}
// Remove runs "docker rm" on the container
func (c ContainerID) Remove() error {
if Debug || c == "nil" {
return nil
}
return runDockerCommand("docker", "rm", "-v", string(c)).Run()
}
// KillRemove calls Kill on the container, and then Remove if there was
// no error.
func (c ContainerID) KillRemove() error {
if err := c.Kill(); err != nil {
return err
}
return c.Remove()
}
// lookup retrieves the ip address of the container, and tries to reach
// before timeout the tcp address at this ip and given port.
func (c ContainerID) lookup(ports []int, timeout time.Duration) (ip string, err error) {
if DockerMachineAvailable {
var out []byte
out, err = exec.Command("docker-machine", "ip", DockerMachineName).Output()
ip = strings.TrimSpace(string(out))
} else if BindDockerToLocalhost != "" {
ip = "127.0.0.1"
} else {
ip, err = c.IP()
}
if err != nil {
err = fmt.Errorf("error getting IP: %v", err)
return
}
for _, port := range ports {
addr := fmt.Sprintf("%s:%d", ip, port)
err = netutil.AwaitReachable(addr, timeout)
if err != nil {
return
}
}
return
}

39
vendor/github.com/ory-am/dockertest/custom.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
package dockertest
import (
"errors"
"fmt"
"log"
"time"
)
// SetupCustomContainer sets up a real an instance of the given image for testing purposes,
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupCustomContainer(imageName string, exposedPort int, timeOut time.Duration, extraDockerArgs ...string) (c ContainerID, ip string, localPort int, err error) {
localPort = RandomPort()
forward := fmt.Sprintf("%d:%d", localPort, exposedPort)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(imageName, localPort, timeOut, func() (string, error) {
args := make([]string, 0, len(extraDockerArgs)+7)
args = append(args, "--name", GenerateContainerID(), "-d", "-P", "-p", forward)
args = append(args, extraDockerArgs...)
args = append(args, imageName)
return run(args...)
})
return
}
// ConnectToCustomContainer attempts to connect to a custom container until successful or the maximum number of tries is reached.
func ConnectToCustomContainer(url string, tries int, delay time.Duration, connector func(url string) bool) error {
for try := 0; try <= tries; try++ {
time.Sleep(delay)
if connector(url) {
return nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return errors.New("Could not set up custom container.")
}

5
vendor/github.com/ory-am/dockertest/doc.go generated vendored Normal file
View File

@ -0,0 +1,5 @@
// Package dockertest contains helper functions for setting up and tearing down docker containers to aid in testing.
// dockertest supports spinning up MySQL, PostgreSQL and MongoDB out of the box.
//
// Dockertest provides two environment variables
package dockertest

260
vendor/github.com/ory-am/dockertest/docker.go generated vendored Normal file
View File

@ -0,0 +1,260 @@
package dockertest
/*
Copyright 2014 The Camlistore Authors
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.
*/
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"math/rand"
"os/exec"
"regexp"
"strings"
"time"
// Import postgres driver
_ "github.com/lib/pq"
"github.com/pborman/uuid"
)
/// runLongTest checks all the conditions for running a docker container
// based on image.
func runLongTest(image string) error {
DockerMachineAvailable = false
if haveDockerMachine() {
DockerMachineAvailable = true
if !startDockerMachine() {
log.Printf(`Starting docker machine "%s" failed.
This could be because the image is already running or because the image does not exist.
Tests will fail if the image does not exist.`, DockerMachineName)
}
} else if !haveDocker() {
return errors.New("Neither 'docker' nor 'docker-machine' available on this system.")
}
if ok, err := HaveImage(image); !ok || err != nil {
if err != nil {
return fmt.Errorf("Error checking for docker image %s: %v", image, err)
}
log.Printf("Pulling docker image %s ...", image)
if err := Pull(image); err != nil {
return fmt.Errorf("Error pulling %s: %v", image, err)
}
}
return nil
}
func runDockerCommand(command string, args ...string) *exec.Cmd {
if DockerMachineAvailable {
command = "/usr/local/bin/" + strings.Join(append([]string{command}, args...), " ")
cmd := exec.Command("docker-machine", "ssh", DockerMachineName, command)
return cmd
}
return exec.Command(command, args...)
}
// haveDockerMachine returns whether the "docker" command was found.
func haveDockerMachine() bool {
_, err := exec.LookPath("docker-machine")
return err == nil
}
// startDockerMachine starts the docker machine and returns false if the command failed to execute
func startDockerMachine() bool {
_, err := exec.Command("docker-machine", "start", DockerMachineName).Output()
return err == nil
}
// haveDocker returns whether the "docker" command was found.
func haveDocker() bool {
_, err := exec.LookPath("docker")
return err == nil
}
type dockerImage struct {
repo string
tag string
}
type dockerImageList []dockerImage
func (l dockerImageList) contains(repo string, tag string) bool {
if tag == "" {
tag = "latest"
}
for _, image := range l {
if image.repo == repo && image.tag == tag {
return true
}
}
return false
}
func parseDockerImagesOutput(data []byte) (images dockerImageList) {
lines := strings.Split(string(data), "\n")
if len(lines) < 2 {
return
}
// skip first line with columns names
images = make(dockerImageList, 0, len(lines)-1)
for _, line := range lines[1:] {
cols := strings.Fields(line)
if len(cols) < 2 {
continue
}
image := dockerImage{
repo: cols[0],
tag: cols[1],
}
images = append(images, image)
}
return
}
func parseImageName(name string) (repo string, tag string) {
if fields := strings.SplitN(name, ":", 2); len(fields) == 2 {
repo, tag = fields[0], fields[1]
} else {
repo = name
}
return
}
// HaveImage reports if docker have image 'name'.
func HaveImage(name string) (bool, error) {
out, err := runDockerCommand("docker", "images", "--no-trunc").Output()
if err != nil {
return false, err
}
repo, tag := parseImageName(name)
images := parseDockerImagesOutput(out)
return images.contains(repo, tag), nil
}
func run(args ...string) (containerID string, err error) {
var stdout, stderr bytes.Buffer
validID := regexp.MustCompile(`^([a-zA-Z0-9]+)$`)
cmd := runDockerCommand("docker", append([]string{"run"}, args...)...)
cmd.Stdout, cmd.Stderr = &stdout, &stderr
if err = cmd.Run(); err != nil {
err = fmt.Errorf("Error running docker\nStdOut: %s\nStdErr: %s\nError: %v\n\n", stdout.String(), stderr.String(), err)
return
}
containerID = strings.TrimSpace(string(stdout.String()))
if !validID.MatchString(containerID) {
return "", fmt.Errorf("Error running docker: %s", containerID)
}
if containerID == "" {
return "", errors.New("Unexpected empty output from `docker run`")
}
return containerID, nil
}
// KillContainer runs docker kill on a container.
func KillContainer(container string) error {
if container != "" {
return runDockerCommand("docker", "kill", container).Run()
}
return nil
}
// Pull retrieves the docker image with 'docker pull'.
func Pull(image string) error {
out, err := runDockerCommand("docker", "pull", image).CombinedOutput()
if err != nil {
err = fmt.Errorf("%v: %s", err, out)
}
return err
}
// IP returns the IP address of the container.
func IP(containerID string) (string, error) {
out, err := runDockerCommand("docker", "inspect", containerID).Output()
if err != nil {
return "", err
}
type networkSettings struct {
IPAddress string
}
type container struct {
NetworkSettings networkSettings
}
var c []container
if err := json.NewDecoder(bytes.NewReader(out)).Decode(&c); err != nil {
return "", err
}
if len(c) == 0 {
return "", errors.New("no output from docker inspect")
}
if ip := c[0].NetworkSettings.IPAddress; ip != "" {
return ip, nil
}
return "", errors.New("could not find an IP. Not running?")
}
// SetupMultiportContainer sets up a container, using the start function to run the given image.
// It also looks up the IP address of the container, and tests this address with the given
// ports and timeout. It returns the container ID and its IP address, or makes the test
// fail on error.
func SetupMultiportContainer(image string, ports []int, timeout time.Duration, start func() (string, error)) (c ContainerID, ip string, err error) {
err = runLongTest(image)
if err != nil {
return "", "", err
}
containerID, err := start()
if err != nil {
return "", "", err
}
c = ContainerID(containerID)
ip, err = c.lookup(ports, timeout)
if err != nil {
c.KillRemove()
return "", "", err
}
return c, ip, nil
}
// SetupContainer sets up a container, using the start function to run the given image.
// It also looks up the IP address of the container, and tests this address with the given
// port and timeout. It returns the container ID and its IP address, or makes the test
// fail on error.
func SetupContainer(image string, port int, timeout time.Duration, start func() (string, error)) (c ContainerID, ip string, err error) {
return SetupMultiportContainer(image, []int{port}, timeout, start)
}
// RandomPort returns a random non-priviledged port.
func RandomPort() int {
min := 1025
max := 65534
return min + rand.Intn(max-min)
}
// GenerateContainerID generated a random container id.
func GenerateContainerID() string {
return ContainerPrefix + uuid.New()
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}

42
vendor/github.com/ory-am/dockertest/elasticsearch.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
package dockertest
import (
"errors"
"fmt"
"log"
"time"
)
// SetupElasticSearchContainer sets up a real ElasticSearch instance for testing purposes
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupElasticSearchContainer() (c ContainerID, ip string, port int, err error) {
port = RandomPort()
forward := fmt.Sprintf("%d:%d", port, 9200)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(ElasticSearchImageName, port, 15*time.Second, func() (string, error) {
return run("--name", GenerateContainerID(), "-d", "-P", "-p", forward, ElasticSearchImageName)
})
return
}
// ConnectToElasticSearch starts an ElasticSearch image and passes the database url to the connector callback function.
// The url will match the ip:port pattern (e.g. 123.123.123.123:4241)
func ConnectToElasticSearch(tries int, delay time.Duration, connector func(url string) bool) (c ContainerID, err error) {
c, ip, port, err := SetupElasticSearchContainer()
if err != nil {
return c, fmt.Errorf("Could not set up ElasticSearch container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
url := fmt.Sprintf("%s:%d", ip, port)
if connector(url) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up ElasticSearch container.")
}

66
vendor/github.com/ory-am/dockertest/mockserver.go generated vendored Normal file
View File

@ -0,0 +1,66 @@
package dockertest
import (
"fmt"
"time"
"log"
"github.com/go-errors/errors"
)
// SetupMockserverContainer sets up a real Mockserver instance for testing purposes
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupMockserverContainer() (c ContainerID, ip string, mockPort, proxyPort int, err error) {
mockPort = RandomPort()
proxyPort = RandomPort()
mockForward := fmt.Sprintf("%d:%d", mockPort, 1080)
proxyForward := fmt.Sprintf("%d:%d", proxyPort, 1090)
if BindDockerToLocalhost != "" {
mockForward = "127.0.0.1:" + mockForward
proxyForward = "127.0.0.1:" + proxyForward
}
c, ip, err = SetupMultiportContainer(RabbitMQImageName, []int{ mockPort, proxyPort}, 10*time.Second, func() (string, error) {
res, err := run("--name", GenerateContainerID(), "-d", "-P", "-p", mockForward, "-p", proxyForward, MockserverImageName)
return res, err
})
return
}
// ConnectToMockserver starts a Mockserver image and passes the mock and proxy urls to the connector callback functions.
// The urls will match the http://ip:port pattern (e.g. http://123.123.123.123:4241)
func ConnectToMockserver(tries int, delay time.Duration, mockConnector func(url string) bool, proxyConnector func(url string) bool) (c ContainerID, err error) {
c, ip, mockPort, proxyPort, err := SetupMockserverContainer()
if err != nil {
return c, fmt.Errorf("Could not set up Mockserver container: %v", err)
}
var mockOk, proxyOk bool
for try := 0; try <= tries; try++ {
time.Sleep(delay)
if !mockOk {
if mockConnector(fmt.Sprintf("http://%s:%d", ip, mockPort)) {
mockOk = true
} else {
log.Printf("Try %d failed for mock. Retrying.", try)
}
}
if !proxyOk {
if proxyConnector(fmt.Sprintf("http://%s:%d", ip, proxyPort)) {
proxyOk = true
} else {
log.Printf("Try %d failed for proxy. Retrying.", try)
}
}
}
if mockOk && proxyOk {
return c, nil
} else {
return c, errors.New("Could not set up Mockserver container.")
}
}

43
vendor/github.com/ory-am/dockertest/mongodb.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package dockertest
import (
"errors"
"fmt"
"log"
"time"
)
// SetupMongoContainer sets up a real MongoDB instance for testing purposes,
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupMongoContainer() (c ContainerID, ip string, port int, err error) {
port = RandomPort()
forward := fmt.Sprintf("%d:%d", port, 27017)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(MongoDBImageName, port, 10*time.Second, func() (string, error) {
res, err := run("--name", GenerateContainerID(), "-d", "-P", "-p", forward, MongoDBImageName)
return res, err
})
return
}
// ConnectToMongoDB starts a MongoDB image and passes the database url to the connector callback.
// The url will match the ip:port pattern (e.g. 123.123.123.123:4241)
func ConnectToMongoDB(tries int, delay time.Duration, connector func(url string) bool) (c ContainerID, err error) {
c, ip, port, err := SetupMongoContainer()
if err != nil {
return c, fmt.Errorf("Could not set up MongoDB container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
url := fmt.Sprintf("%s:%d", ip, port)
if connector(url) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up MongoDB container.")
}

75
vendor/github.com/ory-am/dockertest/mysql.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
package dockertest
import (
"database/sql"
"errors"
"fmt"
"log"
"time"
mysql "github.com/go-sql-driver/mysql"
)
const (
defaultMySQLDBName = "mysql"
)
// SetupMySQLContainer sets up a real MySQL instance for testing purposes,
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupMySQLContainer() (c ContainerID, ip string, port int, err error) {
port = RandomPort()
forward := fmt.Sprintf("%d:%d", port, 3306)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(MySQLImageName, port, 10*time.Second, func() (string, error) {
return run("--name", GenerateContainerID(), "-d", "-p", forward, "-e", fmt.Sprintf("MYSQL_ROOT_PASSWORD=%s", MySQLPassword), MySQLImageName)
})
return
}
// ConnectToMySQL starts a MySQL image and passes the database url to the connector callback function.
// The url will match the username:password@tcp(ip:port) pattern (e.g. `root:root@tcp(123.123.123.123:3131)`)
func ConnectToMySQL(tries int, delay time.Duration, connector func(url string) bool) (c ContainerID, err error) {
c, ip, port, err := SetupMySQLContainer()
if err != nil {
return c, fmt.Errorf("Could not set up MySQL container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
url := fmt.Sprintf("%s:%s@tcp(%s:%d)/mysql", MySQLUsername, MySQLPassword, ip, port)
if connector(url) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up MySQL container.")
}
// SetUpMySQLDatabase connects mysql container with given $connectURL and also creates a new database named $databaseName
// A modified url used to connect the created database will be returned
func SetUpMySQLDatabase(databaseName, connectURL string) (url string, err error) {
if databaseName == defaultMySQLDBName {
return connectURL, nil
}
db, err := sql.Open("mysql", connectURL)
if err != nil {
return "", err
}
defer db.Close()
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", databaseName))
if err != nil {
return "", err
}
// parse dsn
config, err := mysql.ParseDSN(connectURL)
if err != nil {
return "", err
}
config.DBName = databaseName // overwrite database name
return config.FormatDSN(), nil
}

90
vendor/github.com/ory-am/dockertest/nsq.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
package dockertest
import (
"errors"
"fmt"
"log"
"time"
)
// SetupNSQdContainer sets up a real NSQ instance for testing purposes
// using a Docker container and executing `/nsqd`. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupNSQdContainer() (c ContainerID, ip string, tcpPort int, httpPort int, err error) {
// --name nsqd -p 4150:4150 -p 4151:4151 nsqio/nsq /nsqd --broadcast-address=192.168.99.100 --lookupd-tcp-address=192.168.99.100:4160
tcpPort = RandomPort()
httpPort = RandomPort()
tcpForward := fmt.Sprintf("%d:%d", tcpPort, 4150)
if BindDockerToLocalhost != "" {
tcpForward = "127.0.0.1:" + tcpForward
}
httpForward := fmt.Sprintf("%d:%d", httpPort, 4151)
if BindDockerToLocalhost != "" {
httpForward = "127.0.0.1:" + httpForward
}
c, ip, err = SetupContainer(NSQImageName, tcpPort, 15*time.Second, func() (string, error) {
return run("--name", GenerateContainerID(), "-d", "-P", "-p", tcpForward, "-p", httpForward, NSQImageName, "/nsqd", fmt.Sprintf("--broadcast-address=%s", ip), fmt.Sprintf("--lookupd-tcp-address=%s:4160", ip))
})
return
}
// SetupNSQLookupdContainer sets up a real NSQ instance for testing purposes
// using a Docker container and executing `/nsqlookupd`. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupNSQLookupdContainer() (c ContainerID, ip string, tcpPort int, httpPort int, err error) {
// docker run --name lookupd -p 4160:4160 -p 4161:4161 nsqio/nsq /nsqlookupd
tcpPort = RandomPort()
httpPort = RandomPort()
tcpForward := fmt.Sprintf("%d:%d", tcpPort, 4160)
if BindDockerToLocalhost != "" {
tcpForward = "127.0.0.1:" + tcpForward
}
httpForward := fmt.Sprintf("%d:%d", httpPort, 4161)
if BindDockerToLocalhost != "" {
httpForward = "127.0.0.1:" + httpForward
}
c, ip, err = SetupContainer(NSQImageName, tcpPort, 15*time.Second, func() (string, error) {
return run("--name", GenerateContainerID(), "-d", "-P", "-p", tcpForward, "-p", httpForward, NSQImageName, "/nsqlookupd")
})
return
}
// ConnectToNSQLookupd starts a NSQ image with `/nsqlookupd` running and passes the IP, HTTP port, and TCP port to the connector callback function.
// The url will match the ip pattern (e.g. 123.123.123.123).
func ConnectToNSQLookupd(tries int, delay time.Duration, connector func(ip string, httpPort int, tcpPort int) bool) (c ContainerID, err error) {
c, ip, tcpPort, httpPort, err := SetupNSQLookupdContainer()
if err != nil {
return c, fmt.Errorf("Could not set up NSQLookupd container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
if connector(ip, httpPort, tcpPort) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up NSQLookupd container.")
}
// ConnectToNSQd starts a NSQ image with `/nsqd` running and passes the IP, HTTP port, and TCP port to the connector callback function.
// The url will match the ip pattern (e.g. 123.123.123.123).
func ConnectToNSQd(tries int, delay time.Duration, connector func(ip string, httpPort int, tcpPort int) bool) (c ContainerID, err error) {
c, ip, tcpPort, httpPort, err := SetupNSQdContainer()
if err != nil {
return c, fmt.Errorf("Could not set up NSQd container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
if connector(ip, httpPort, tcpPort) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up NSQd container.")
}

80
vendor/github.com/ory-am/dockertest/postgres.go generated vendored Normal file
View File

@ -0,0 +1,80 @@
package dockertest
import (
"database/sql"
"errors"
"fmt"
"log"
"net/url"
"time"
_ "github.com/lib/pq"
)
// SetupPostgreSQLContainer sets up a real PostgreSQL instance for testing purposes,
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupPostgreSQLContainer() (c ContainerID, ip string, port int, err error) {
port = RandomPort()
forward := fmt.Sprintf("%d:%d", port, 5432)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(PostgresImageName, port, 15*time.Second, func() (string, error) {
return run("--name", GenerateContainerID(), "-d", "-p", forward, "-e", fmt.Sprintf("POSTGRES_PASSWORD=%s", PostgresPassword), PostgresImageName)
})
return
}
// ConnectToPostgreSQL starts a PostgreSQL image and passes the database url to the connector callback.
func ConnectToPostgreSQL(tries int, delay time.Duration, connector func(url string) bool) (c ContainerID, err error) {
c, ip, port, err := SetupPostgreSQLContainer()
if err != nil {
return c, fmt.Errorf("Could not set up PostgreSQL container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
url := fmt.Sprintf("postgres://%s:%s@%s:%d/postgres?sslmode=disable", PostgresUsername, PostgresPassword, ip, port)
if connector(url) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up PostgreSQL container.")
}
// SetUpPostgreDatabase connects postgre container with given $connectURL and also creates a new database named $databaseName
// A modified url used to connect the created database will be returned
func SetUpPostgreDatabase(databaseName, connectURL string) (modifiedURL string, err error) {
db, err := sql.Open("postgres", connectURL)
if err != nil {
return "", err
}
defer db.Close()
count := 0
err = db.QueryRow(
fmt.Sprintf("SELECT COUNT(*) FROM pg_catalog.pg_database WHERE datname = '%s' ;", databaseName)).
Scan(&count)
if err != nil {
return "", err
}
if count == 0 {
// not found for $databaseName, create it
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", databaseName))
if err != nil {
return "", err
}
}
// replace dbname in url
// from: postgres://postgres:docker@192.168.99.100:9071/postgres?sslmode=disable
// to: postgres://postgres:docker@192.168.99.100:9071/$databaseName?sslmode=disable
u, err := url.Parse(connectURL)
if err != nil {
return "", err
}
u.Path = fmt.Sprintf("/%s", databaseName)
return u.String(), nil
}

43
vendor/github.com/ory-am/dockertest/rabbitmq.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package dockertest
import (
"errors"
"fmt"
"log"
"time"
)
// SetupRabbitMQContainer sets up a real RabbitMQ instance for testing purposes,
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupRabbitMQContainer() (c ContainerID, ip string, port int, err error) {
port = RandomPort()
forward := fmt.Sprintf("%d:%d", port, 5672)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(RabbitMQImageName, port, 10*time.Second, func() (string, error) {
res, err := run("--name", GenerateContainerID(), "-d", "-P", "-p", forward, RabbitMQImageName)
return res, err
})
return
}
// ConnectToRabbitMQ starts a RabbitMQ image and passes the amqp url to the connector callback.
// The url will match the ip:port pattern (e.g. 123.123.123.123:4241)
func ConnectToRabbitMQ(tries int, delay time.Duration, connector func(url string) bool) (c ContainerID, err error) {
c, ip, port, err := SetupRabbitMQContainer()
if err != nil {
return c, fmt.Errorf("Could not set up RabbitMQ container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
url := fmt.Sprintf("%s:%d", ip, port)
if connector(url) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up RabbitMQ container.")
}

42
vendor/github.com/ory-am/dockertest/redis.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
package dockertest
import (
"errors"
"fmt"
"log"
"time"
)
// SetupRedisContainer sets up a real Redis instance for testing purposes
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupRedisContainer() (c ContainerID, ip string, port int, err error) {
port = RandomPort()
forward := fmt.Sprintf("%d:%d", port, 6379)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(RedisImageName, port, 15*time.Second, func() (string, error) {
return run("--name", GenerateContainerID(), "-d", "-P", "-p", forward, RedisImageName)
})
return
}
// ConnectToRedis starts a Redis image and passes the database url to the connector callback function.
// The url will match the ip:port pattern (e.g. 123.123.123.123:6379)
func ConnectToRedis(tries int, delay time.Duration, connector func(url string) bool) (c ContainerID, err error) {
c, ip, port, err := SetupRedisContainer()
if err != nil {
return c, fmt.Errorf("Could not set up Redis container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
url := fmt.Sprintf("%s:%d", ip, port)
if connector(url) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up Redis container.")
}

43
vendor/github.com/ory-am/dockertest/rethinkdb.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package dockertest
import (
"errors"
"fmt"
"log"
"time"
)
// SetupRethinkDBContainer sets up a real RethinkDB instance for testing purposes,
// using a Docker container. It returns the container ID and its IP address,
// or makes the test fail on error.
func SetupRethinkDBContainer() (c ContainerID, ip string, port int, err error) {
port = RandomPort()
forward := fmt.Sprintf("%d:%d", port, 28015)
if BindDockerToLocalhost != "" {
forward = "127.0.0.1:" + forward
}
c, ip, err = SetupContainer(RethinkDBImageName, port, 10*time.Second, func() (string, error) {
res, err := run("--name", GenerateContainerID(), "-d", "-P", "-p", forward, RethinkDBImageName)
return res, err
})
return
}
// ConnectToRethinkDB starts a RethinkDB image and passes the database url to the connector callback.
// The url will match the ip:port pattern (e.g. 123.123.123.123:4241)
func ConnectToRethinkDB(tries int, delay time.Duration, connector func(url string) bool) (c ContainerID, err error) {
c, ip, port, err := SetupRethinkDBContainer()
if err != nil {
return c, fmt.Errorf("Could not set up RethinkDB container: %v", err)
}
for try := 0; try <= tries; try++ {
time.Sleep(delay)
url := fmt.Sprintf("%s:%d", ip, port)
if connector(url) {
return c, nil
}
log.Printf("Try %d failed. Retrying.", try)
}
return c, errors.New("Could not set up RethinkDB container.")
}

72
vendor/github.com/ory-am/dockertest/vars.go generated vendored Normal file
View File

@ -0,0 +1,72 @@
package dockertest
import "github.com/ory-am/common/env"
// Dockertest configuration
var (
// Debug if set, prevents any container from being removed.
Debug bool
// DockerMachineAvailable if true, uses docker-machine to run docker commands (for running tests on Windows and Mac OS)
DockerMachineAvailable bool
// DockerMachineName is the machine's name. You might want to use a dedicated machine for running your tests.
// You can set this variable either directly or by defining a DOCKERTEST_IMAGE_NAME env variable.
DockerMachineName = env.Getenv("DOCKERTEST_IMAGE_NAME", "default")
// BindDockerToLocalhost if set, forces docker to bind the image to localhost. This for example is required when running tests on travis-ci.
// You can set this variable either directly or by defining a DOCKERTEST_BIND_LOCALHOST env variable.
// FIXME DOCKER_BIND_LOCALHOST remove legacy support
BindDockerToLocalhost = env.Getenv("DOCKERTEST_BIND_LOCALHOST", env.Getenv("DOCKER_BIND_LOCALHOST", ""))
// ContainerPrefix will be prepended to all containers started by dockertest to make identification of these "test images" hassle-free.
ContainerPrefix = env.Getenv("DOCKERTEST_CONTAINER_PREFIX", "dockertest-")
)
// Image configuration
var (
// MongoDBImageName is the MongoDB image name on dockerhub.
MongoDBImageName = env.Getenv("DOCKERTEST_MONGODB_IMAGE_NAME", "mongo")
// MySQLImageName is the MySQL image name on dockerhub.
MySQLImageName = env.Getenv("DOCKERTEST_MYSQL_IMAGE_NAME", "mysql")
// PostgresImageName is the PostgreSQL image name on dockerhub.
PostgresImageName = env.Getenv("DOCKERTEST_POSTGRES_IMAGE_NAME", "postgres")
// ElasticSearchImageName is the ElasticSearch image name on dockerhub.
ElasticSearchImageName = env.Getenv("DOCKERTEST_ELASTICSEARCH_IMAGE_NAME", "elasticsearch")
// RedisImageName is the Redis image name on dockerhub.
RedisImageName = env.Getenv("DOCKERTEST_REDIS_IMAGE_NAME", "redis")
// NSQImageName is the NSQ image name on dockerhub.
NSQImageName = env.Getenv("DOCKERTEST_NSQ_IMAGE_NAME", "nsqio/nsq")
// RethinkDBImageName is the RethinkDB image name on dockerhub.
RethinkDBImageName = env.Getenv("DOCKERTEST_RETHINKDB_IMAGE_NAME", "rethinkdb")
// RabbitMQImage name is the RabbitMQ image name on dockerhub.
RabbitMQImageName = env.Getenv("DOCKERTEST_RABBITMQ_IMAGE_NAME", "rabbitmq")
// ActiveMQImage name is the ActiveMQ image name on dockerhub.
ActiveMQImageName = env.Getenv("DOCKERTEST_ACTIVEMQ_IMAGE_NAME", "webcenter/activemq")
// MockserverImageName name is the Mockserver image name on dockerhub.
MockserverImageName = env.Getenv("DOCKERTEST_MOCKSERVER_IMAGE_NAME", "jamesdbloom/mockserver")
)
// Username and password configuration
var (
// MySQLUsername must be passed as username when connecting to mysql
MySQLUsername = "root"
// MySQLPassword must be passed as password when connecting to mysql
MySQLPassword = "root"
// PostgresUsername must be passed as username when connecting to postgres
PostgresUsername = "postgres"
// PostgresPassword must be passed as password when connecting to postgres
PostgresPassword = "docker"
)

12
vendor/vendor.json vendored
View File

@ -18,6 +18,12 @@
"path": "appengine_internal/base",
"revision": ""
},
{
"checksumSHA1": "ZjT4/uxN8N5WXikTtY1LdmMtL9Q=",
"path": "camlistore.org/pkg/netutil",
"revision": "c332b2881d09003e01491f2658ccf1dbbb006169",
"revisionTime": "2016-06-29T15:04:51Z"
},
{
"path": "context",
"revision": ""
@ -628,6 +634,12 @@
"revision": "930cc805232909c38f2e68310b1e21f71b056d59",
"revisionTime": "2016-01-09T14:21:19Z"
},
{
"checksumSHA1": "CXJIw4GNmwqc+c4ZCeVj2mMm1Uw=",
"path": "github.com/ory-am/dockertest",
"revision": "293f0a0aac817a67dcf656af315a43f2db140c2a",
"revisionTime": "2016-06-21T08:39:56Z"
},
{
"checksumSHA1": "xN14ZoFcgefABp24aowYtQVMDsc=",
"path": "github.com/pborman/uuid",