github.com/hooklift/nomad@v0.5.7-0.20170407200202-db11e7dd7b55/testutil/vault.go (about)

     1  package testutil
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"runtime"
     8  	"testing"
     9  
    10  	"github.com/hashicorp/nomad/nomad/structs"
    11  	"github.com/hashicorp/nomad/nomad/structs/config"
    12  	vapi "github.com/hashicorp/vault/api"
    13  )
    14  
    15  // TestVault is a test helper. It uses a fork/exec model to create a test Vault
    16  // server instance in the background and can be initialized with policies, roles
    17  // and backends mounted. The test Vault instances can be used to run a unit test
    18  // and offers and easy API to tear itself down on test end. The only
    19  // prerequisite is that the Vault binary is on the $PATH.
    20  
    21  const (
    22  	// vaultStartPort is the starting port we use to bind Vault servers to
    23  	vaultStartPort uint64 = 40000
    24  )
    25  
    26  // vaultPortOffset is used to atomically increment the port numbers.
    27  var vaultPortOffset uint64
    28  
    29  // TestVault wraps a test Vault server launched in dev mode, suitable for
    30  // testing.
    31  type TestVault struct {
    32  	cmd *exec.Cmd
    33  	t   *testing.T
    34  
    35  	Addr      string
    36  	HTTPAddr  string
    37  	RootToken string
    38  	Config    *config.VaultConfig
    39  	Client    *vapi.Client
    40  }
    41  
    42  // NewTestVault returns a new TestVault instance that has yet to be started
    43  func NewTestVault(t *testing.T) *TestVault {
    44  	port := getPort()
    45  	token := structs.GenerateUUID()
    46  	bind := fmt.Sprintf("-dev-listen-address=127.0.0.1:%d", port)
    47  	http := fmt.Sprintf("http://127.0.0.1:%d", port)
    48  	root := fmt.Sprintf("-dev-root-token-id=%s", token)
    49  
    50  	bin := "vault"
    51  	if runtime.GOOS == "windows" {
    52  		bin = "vault.exe"
    53  	}
    54  	cmd := exec.Command(bin, "server", "-dev", bind, root)
    55  	cmd.Stdout = os.Stdout
    56  	cmd.Stderr = os.Stderr
    57  
    58  	// Build the config
    59  	conf := vapi.DefaultConfig()
    60  	conf.Address = http
    61  
    62  	// Make the client and set the token to the root token
    63  	client, err := vapi.NewClient(conf)
    64  	if err != nil {
    65  		t.Fatalf("failed to build Vault API client: %v", err)
    66  	}
    67  	client.SetToken(token)
    68  
    69  	enable := true
    70  	tv := &TestVault{
    71  		cmd:       cmd,
    72  		t:         t,
    73  		Addr:      bind,
    74  		HTTPAddr:  http,
    75  		RootToken: token,
    76  		Client:    client,
    77  		Config: &config.VaultConfig{
    78  			Enabled: &enable,
    79  			Token:   token,
    80  			Addr:    http,
    81  		},
    82  	}
    83  
    84  	return tv
    85  }
    86  
    87  // Start starts the test Vault server and waits for it to respond to its HTTP
    88  // API
    89  func (tv *TestVault) Start() *TestVault {
    90  	if err := tv.cmd.Start(); err != nil {
    91  		tv.t.Fatalf("failed to start vault: %v", err)
    92  	}
    93  
    94  	tv.waitForAPI()
    95  	return tv
    96  }
    97  
    98  // Stop stops the test Vault server
    99  func (tv *TestVault) Stop() {
   100  	if tv.cmd.Process == nil {
   101  		return
   102  	}
   103  
   104  	if err := tv.cmd.Process.Kill(); err != nil {
   105  		tv.t.Errorf("err: %s", err)
   106  	}
   107  	tv.cmd.Wait()
   108  }
   109  
   110  // waitForAPI waits for the Vault HTTP endpoint to start
   111  // responding. This is an indication that the agent has started.
   112  func (tv *TestVault) waitForAPI() {
   113  	WaitForResult(func() (bool, error) {
   114  		inited, err := tv.Client.Sys().InitStatus()
   115  		if err != nil {
   116  			return false, err
   117  		}
   118  		return inited, nil
   119  	}, func(err error) {
   120  		defer tv.Stop()
   121  		tv.t.Fatalf("err: %s", err)
   122  	})
   123  }
   124  
   125  // getPort returns the next available port to bind Vault against
   126  func getPort() uint64 {
   127  	p := vaultStartPort + vaultPortOffset
   128  	vaultPortOffset += 1
   129  	return p
   130  }
   131  
   132  // VaultVersion returns the Vault version as a string or an error if it couldn't
   133  // be determined
   134  func VaultVersion() (string, error) {
   135  	cmd := exec.Command("vault", "version")
   136  	out, err := cmd.Output()
   137  	return string(out), err
   138  }