2017-06-26 21:21:14 +00:00
|
|
|
package api_test
|
|
|
|
|
|
|
|
import (
|
2017-09-02 22:48:48 +00:00
|
|
|
"context"
|
2017-06-26 21:21:14 +00:00
|
|
|
"database/sql"
|
2017-09-02 22:48:48 +00:00
|
|
|
"encoding/base64"
|
2017-06-26 21:21:14 +00:00
|
|
|
"fmt"
|
2017-09-02 22:48:48 +00:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2017-06-26 21:21:14 +00:00
|
|
|
"testing"
|
2017-09-02 22:48:48 +00:00
|
|
|
"time"
|
2017-06-26 21:21:14 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/vault/api"
|
2017-09-02 22:48:48 +00:00
|
|
|
"github.com/hashicorp/vault/audit"
|
|
|
|
"github.com/hashicorp/vault/builtin/logical/database"
|
2017-06-26 21:21:14 +00:00
|
|
|
"github.com/hashicorp/vault/builtin/logical/pki"
|
|
|
|
"github.com/hashicorp/vault/builtin/logical/transit"
|
2018-11-07 01:21:24 +00:00
|
|
|
"github.com/hashicorp/vault/helper/builtinplugins"
|
2017-06-26 21:21:14 +00:00
|
|
|
"github.com/hashicorp/vault/logical"
|
|
|
|
"github.com/hashicorp/vault/vault"
|
|
|
|
|
2018-04-03 00:46:59 +00:00
|
|
|
log "github.com/hashicorp/go-hclog"
|
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
auditFile "github.com/hashicorp/vault/builtin/audit/file"
|
|
|
|
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
|
2017-06-26 21:21:14 +00:00
|
|
|
vaulthttp "github.com/hashicorp/vault/http"
|
2018-07-11 21:49:13 +00:00
|
|
|
"github.com/ory/dockertest"
|
2017-06-26 21:21:14 +00:00
|
|
|
)
|
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// testVaultServer creates a test vault cluster and returns a configured API
|
|
|
|
// client and closer function.
|
|
|
|
func testVaultServer(t testing.TB) (*api.Client, func()) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
client, _, closer := testVaultServerUnseal(t)
|
|
|
|
return client, closer
|
2017-06-26 21:21:14 +00:00
|
|
|
}
|
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// testVaultServerUnseal creates a test vault cluster and returns a configured
|
|
|
|
// API client, list of unseal keys (as strings), and a closer function.
|
|
|
|
func testVaultServerUnseal(t testing.TB) (*api.Client, []string, func()) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
return testVaultServerCoreConfig(t, &vault.CoreConfig{
|
|
|
|
DisableMlock: true,
|
|
|
|
DisableCache: true,
|
2018-04-03 00:46:59 +00:00
|
|
|
Logger: log.NewNullLogger(),
|
2017-09-02 22:48:48 +00:00
|
|
|
CredentialBackends: map[string]logical.Factory{
|
|
|
|
"userpass": credUserpass.Factory,
|
|
|
|
},
|
|
|
|
AuditBackends: map[string]audit.Factory{
|
|
|
|
"file": auditFile.Factory,
|
|
|
|
},
|
|
|
|
LogicalBackends: map[string]logical.Factory{
|
|
|
|
"database": database.Factory,
|
|
|
|
"generic-leased": vault.LeasedPassthroughBackendFactory,
|
|
|
|
"pki": pki.Factory,
|
|
|
|
"transit": transit.Factory,
|
|
|
|
},
|
2018-11-07 01:21:24 +00:00
|
|
|
BuiltinRegistry: builtinplugins.Registry,
|
2017-09-02 22:48:48 +00:00
|
|
|
})
|
2017-06-26 21:21:14 +00:00
|
|
|
}
|
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// testVaultServerCoreConfig creates a new vault cluster with the given core
|
|
|
|
// configuration. This is a lower-level test helper.
|
|
|
|
func testVaultServerCoreConfig(t testing.TB, coreConfig *vault.CoreConfig) (*api.Client, []string, func()) {
|
|
|
|
t.Helper()
|
2017-06-26 21:21:14 +00:00
|
|
|
|
2017-07-31 15:28:06 +00:00
|
|
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
|
|
|
HandlerFunc: vaulthttp.Handler,
|
|
|
|
})
|
|
|
|
cluster.Start()
|
2017-06-26 21:21:14 +00:00
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// Make it easy to get access to the active
|
2017-07-11 03:47:03 +00:00
|
|
|
core := cluster.Cores[0].Core
|
2017-06-26 21:21:14 +00:00
|
|
|
vault.TestWaitActive(t, core)
|
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// Get the client already setup for us!
|
2017-07-11 03:47:03 +00:00
|
|
|
client := cluster.Cores[0].Client
|
2017-07-31 15:28:06 +00:00
|
|
|
client.SetToken(cluster.RootToken)
|
2017-06-26 21:21:14 +00:00
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// Convert the unseal keys to base64 encoded, since these are how the user
|
|
|
|
// will get them.
|
|
|
|
unsealKeys := make([]string, len(cluster.BarrierKeys))
|
|
|
|
for i := range unsealKeys {
|
|
|
|
unsealKeys[i] = base64.StdEncoding.EncodeToString(cluster.BarrierKeys[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
return client, unsealKeys, func() { defer cluster.Cleanup() }
|
|
|
|
}
|
|
|
|
|
|
|
|
// testVaultServerBad creates an http server that returns a 500 on each request
|
|
|
|
// to simulate failures.
|
|
|
|
func testVaultServerBad(t testing.TB) (*api.Client, func()) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
2017-06-26 21:21:14 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-09-02 22:48:48 +00:00
|
|
|
|
|
|
|
server := &http.Server{
|
|
|
|
Addr: "127.0.0.1:0",
|
|
|
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
http.Error(w, "500 internal server error", http.StatusInternalServerError)
|
|
|
|
}),
|
|
|
|
ReadTimeout: 1 * time.Second,
|
|
|
|
ReadHeaderTimeout: 1 * time.Second,
|
|
|
|
WriteTimeout: 1 * time.Second,
|
|
|
|
IdleTimeout: 1 * time.Second,
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
client, err := api.NewClient(&api.Config{
|
|
|
|
Address: "http://" + listener.Addr().String(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return client, func() {
|
|
|
|
ctx, done := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
server.Shutdown(ctx)
|
2017-06-26 21:21:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// testPostgresDB creates a testing postgres database in a Docker container,
|
|
|
|
// returning the connection URL and the associated closer function.
|
|
|
|
func testPostgresDB(t testing.TB) (string, func()) {
|
|
|
|
pool, err := dockertest.NewPool("")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("postgresdb: failed to connect to docker: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
resource, err := pool.Run("postgres", "latest", []string{
|
|
|
|
"POSTGRES_PASSWORD=secret",
|
|
|
|
"POSTGRES_DB=database",
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("postgresdb: could not start container: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-08-07 16:56:33 +00:00
|
|
|
cleanup := func() {
|
|
|
|
if err := pool.Purge(resource); err != nil {
|
|
|
|
t.Fatalf("failed to cleanup local container: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-26 21:21:14 +00:00
|
|
|
addr := fmt.Sprintf("postgres://postgres:secret@localhost:%s/database?sslmode=disable", resource.GetPort("5432/tcp"))
|
|
|
|
|
|
|
|
if err := pool.Retry(func() error {
|
|
|
|
db, err := sql.Open("postgres", addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-26 15:28:58 +00:00
|
|
|
defer db.Close()
|
2017-06-26 21:21:14 +00:00
|
|
|
return db.Ping()
|
|
|
|
}); err != nil {
|
2018-08-07 16:56:33 +00:00
|
|
|
cleanup()
|
2017-06-26 21:21:14 +00:00
|
|
|
t.Fatalf("postgresdb: could not connect: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-08-07 16:56:33 +00:00
|
|
|
return addr, cleanup
|
2017-06-26 21:21:14 +00:00
|
|
|
}
|