mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Dockerize Postgres secret backend acceptance tests
Additionally enable them on all unit test runs.
This commit is contained in:
@@ -6,14 +6,68 @@ 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")
|
||||
}
|
||||
|
||||
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 +109,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 +162,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 +183,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 +208,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 +222,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)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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/github.com/ory-am/dockertest/LICENSE
generated
vendored
Normal file
202
vendor/github.com/ory-am/dockertest/LICENSE
generated
vendored
Normal 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
258
vendor/github.com/ory-am/dockertest/README.md
generated
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
# [ory.am](https://ory.am)/dockertest
|
||||
|
||||
[](https://travis-ci.org/ory-am/dockertest?branch=master)
|
||||
[](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
43
vendor/github.com/ory-am/dockertest/activemq.go
generated
vendored
Normal 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
65
vendor/github.com/ory-am/dockertest/container.go
generated
vendored
Normal 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
39
vendor/github.com/ory-am/dockertest/custom.go
generated
vendored
Normal 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
5
vendor/github.com/ory-am/dockertest/doc.go
generated
vendored
Normal 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
260
vendor/github.com/ory-am/dockertest/docker.go
generated
vendored
Normal 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
42
vendor/github.com/ory-am/dockertest/elasticsearch.go
generated
vendored
Normal 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
66
vendor/github.com/ory-am/dockertest/mockserver.go
generated
vendored
Normal 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
43
vendor/github.com/ory-am/dockertest/mongodb.go
generated
vendored
Normal 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
75
vendor/github.com/ory-am/dockertest/mysql.go
generated
vendored
Normal 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
90
vendor/github.com/ory-am/dockertest/nsq.go
generated
vendored
Normal 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
80
vendor/github.com/ory-am/dockertest/postgres.go
generated
vendored
Normal 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
43
vendor/github.com/ory-am/dockertest/rabbitmq.go
generated
vendored
Normal 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
42
vendor/github.com/ory-am/dockertest/redis.go
generated
vendored
Normal 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
43
vendor/github.com/ory-am/dockertest/rethinkdb.go
generated
vendored
Normal 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
72
vendor/github.com/ory-am/dockertest/vars.go
generated
vendored
Normal 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"
|
||||
)
|
||||
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
@@ -598,6 +598,12 @@
|
||||
"revision": "ce444d6d47c51d4dda9202cd38f5094dd8e27e86",
|
||||
"revisionTime": "2016-05-19T16:21:37Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "CXJIw4GNmwqc+c4ZCeVj2mMm1Uw=",
|
||||
"path": "github.com/ory-am/dockertest",
|
||||
"revision": "293f0a0aac817a67dcf656af315a43f2db140c2a",
|
||||
"revisionTime": "2016-06-21T08:39:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "kH9SzL77hzya1KSvkkVfGmbBtmM=",
|
||||
"path": "github.com/ryanuber/columnize",
|
||||
|
||||
Reference in New Issue
Block a user