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 }