github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/testutil/vault.go (about)

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