1package influxdb 2 3import ( 4 "context" 5 "fmt" 6 "os" 7 "strconv" 8 "testing" 9 "time" 10 11 "github.com/hashicorp/errwrap" 12 "github.com/hashicorp/vault/helper/testhelpers/docker" 13 "github.com/hashicorp/vault/sdk/database/dbplugin" 14 influx "github.com/influxdata/influxdb/client/v2" 15 "github.com/ory/dockertest" 16) 17 18const testInfluxRole = `CREATE USER "{{username}}" WITH PASSWORD '{{password}}';GRANT ALL ON "vault" TO "{{username}}";` 19 20func prepareInfluxdbTestContainer(t *testing.T) (func(), string, int) { 21 if os.Getenv("INFLUXDB_HOST") != "" { 22 return func() {}, os.Getenv("INFLUXDB_HOST"), 0 23 } 24 25 pool, err := dockertest.NewPool("") 26 if err != nil { 27 t.Fatalf("Failed to connect to docker: %s", err) 28 } 29 30 ro := &dockertest.RunOptions{ 31 Repository: "influxdb", 32 Tag: "alpine", 33 Env: []string{"INFLUXDB_DB=vault", "INFLUXDB_ADMIN_USER=influx-root", "INFLUXDB_ADMIN_PASSWORD=influx-root", "INFLUXDB_HTTP_AUTH_ENABLED=true"}, 34 } 35 resource, err := pool.RunWithOptions(ro) 36 if err != nil { 37 t.Fatalf("Could not start local influxdb docker container: %s", err) 38 } 39 40 cleanup := func() { 41 docker.CleanupResource(t, pool, resource) 42 } 43 44 port, _ := strconv.Atoi(resource.GetPort("8086/tcp")) 45 address := "127.0.0.1" 46 47 // exponential backoff-retry 48 if err = pool.Retry(func() error { 49 cli, err := influx.NewHTTPClient(influx.HTTPConfig{ 50 Addr: fmt.Sprintf("http://%s:%d", address, port), 51 Username: "influx-root", 52 Password: "influx-root", 53 }) 54 if err != nil { 55 return errwrap.Wrapf("Error creating InfluxDB Client: ", err) 56 } 57 defer cli.Close() 58 _, _, err = cli.Ping(1) 59 if err != nil { 60 return errwrap.Wrapf("error checking cluster status: {{err}}", err) 61 } 62 return nil 63 }); err != nil { 64 cleanup() 65 t.Fatalf("Could not connect to influxdb docker container: %s", err) 66 } 67 return cleanup, address, port 68} 69 70func TestInfluxdb_Initialize(t *testing.T) { 71 if os.Getenv("VAULT_ACC") == "" { 72 t.SkipNow() 73 } 74 cleanup, address, port := prepareInfluxdbTestContainer(t) 75 defer cleanup() 76 77 connectionDetails := map[string]interface{}{ 78 "host": address, 79 "port": port, 80 "username": "influx-root", 81 "password": "influx-root", 82 } 83 84 db := new() 85 _, err := db.Init(context.Background(), connectionDetails, true) 86 if err != nil { 87 t.Fatalf("err: %s", err) 88 } 89 90 if !db.Initialized { 91 t.Fatal("Database should be initialized") 92 } 93 94 err = db.Close() 95 if err != nil { 96 t.Fatalf("err: %s", err) 97 } 98 99 // test a string protocol 100 connectionDetails = map[string]interface{}{ 101 "host": address, 102 "port": strconv.Itoa(port), 103 "username": "influx-root", 104 "password": "influx-root", 105 } 106 107 _, err = db.Init(context.Background(), connectionDetails, true) 108 if err != nil { 109 t.Fatalf("err: %s", err) 110 } 111} 112 113func TestInfluxdb_CreateUser(t *testing.T) { 114 if os.Getenv("VAULT_ACC") == "" { 115 t.SkipNow() 116 } 117 cleanup, address, port := prepareInfluxdbTestContainer(t) 118 defer cleanup() 119 120 connectionDetails := map[string]interface{}{ 121 "host": address, 122 "port": port, 123 "username": "influx-root", 124 "password": "influx-root", 125 } 126 127 db := new() 128 _, err := db.Init(context.Background(), connectionDetails, true) 129 if err != nil { 130 t.Fatalf("err: %s", err) 131 } 132 133 statements := dbplugin.Statements{ 134 Creation: []string{testInfluxRole}, 135 } 136 137 usernameConfig := dbplugin.UsernameConfig{ 138 DisplayName: "test", 139 RoleName: "test", 140 } 141 142 username, password, err := db.CreateUser(context.Background(), statements, usernameConfig, time.Now().Add(time.Minute)) 143 if err != nil { 144 t.Fatalf("err: %s", err) 145 } 146 147 if err := testCredsExist(t, address, port, username, password); err != nil { 148 t.Fatalf("Could not connect with new credentials: %s", err) 149 } 150} 151 152func TestMyInfluxdb_RenewUser(t *testing.T) { 153 if os.Getenv("VAULT_ACC") == "" { 154 t.SkipNow() 155 } 156 cleanup, address, port := prepareInfluxdbTestContainer(t) 157 defer cleanup() 158 159 connectionDetails := map[string]interface{}{ 160 "host": address, 161 "port": port, 162 "username": "influx-root", 163 "password": "influx-root", 164 } 165 166 db := new() 167 _, err := db.Init(context.Background(), connectionDetails, true) 168 if err != nil { 169 t.Fatalf("err: %s", err) 170 } 171 172 statements := dbplugin.Statements{ 173 Creation: []string{testInfluxRole}, 174 } 175 176 usernameConfig := dbplugin.UsernameConfig{ 177 DisplayName: "test", 178 RoleName: "test", 179 } 180 181 username, password, err := db.CreateUser(context.Background(), statements, usernameConfig, time.Now().Add(time.Minute)) 182 if err != nil { 183 t.Fatalf("err: %s", err) 184 } 185 186 if err := testCredsExist(t, address, port, username, password); err != nil { 187 t.Fatalf("Could not connect with new credentials: %s", err) 188 } 189 190 err = db.RenewUser(context.Background(), statements, username, time.Now().Add(time.Minute)) 191 if err != nil { 192 t.Fatalf("err: %s", err) 193 } 194} 195 196func TestInfluxdb_RevokeUser(t *testing.T) { 197 if os.Getenv("VAULT_ACC") == "" { 198 t.SkipNow() 199 } 200 cleanup, address, port := prepareInfluxdbTestContainer(t) 201 defer cleanup() 202 203 connectionDetails := map[string]interface{}{ 204 "host": address, 205 "port": port, 206 "username": "influx-root", 207 "password": "influx-root", 208 } 209 210 db := new() 211 _, err := db.Init(context.Background(), connectionDetails, true) 212 if err != nil { 213 t.Fatalf("err: %s", err) 214 } 215 216 statements := dbplugin.Statements{ 217 Creation: []string{testInfluxRole}, 218 } 219 220 usernameConfig := dbplugin.UsernameConfig{ 221 DisplayName: "test", 222 RoleName: "test", 223 } 224 225 username, password, err := db.CreateUser(context.Background(), statements, usernameConfig, time.Now().Add(time.Minute)) 226 if err != nil { 227 t.Fatalf("err: %s", err) 228 } 229 230 if err = testCredsExist(t, address, port, username, password); err != nil { 231 t.Fatalf("Could not connect with new credentials: %s", err) 232 } 233 234 // Test default revoke statements 235 err = db.RevokeUser(context.Background(), statements, username) 236 if err != nil { 237 t.Fatalf("err: %s", err) 238 } 239 240 if err = testCredsExist(t, address, port, username, password); err == nil { 241 t.Fatal("Credentials were not revoked") 242 } 243} 244func TestInfluxdb_RotateRootCredentials(t *testing.T) { 245 if os.Getenv("VAULT_ACC") == "" { 246 t.SkipNow() 247 } 248 cleanup, address, port := prepareInfluxdbTestContainer(t) 249 defer cleanup() 250 251 connectionDetails := map[string]interface{}{ 252 "host": address, 253 "port": port, 254 "username": "influx-root", 255 "password": "influx-root", 256 } 257 258 db := new() 259 260 connProducer := db.influxdbConnectionProducer 261 262 _, err := db.Init(context.Background(), connectionDetails, true) 263 if err != nil { 264 t.Fatalf("err: %s", err) 265 } 266 267 if !connProducer.Initialized { 268 t.Fatal("Database should be initialized") 269 } 270 271 newConf, err := db.RotateRootCredentials(context.Background(), nil) 272 if err != nil { 273 t.Fatalf("err: %v", err) 274 } 275 if newConf["password"] == "influx-root" { 276 t.Fatal("password was not updated") 277 } 278 279 err = db.Close() 280 if err != nil { 281 t.Fatalf("err: %s", err) 282 } 283} 284 285func testCredsExist(t testing.TB, address string, port int, username, password string) error { 286 cli, err := influx.NewHTTPClient(influx.HTTPConfig{ 287 Addr: fmt.Sprintf("http://%s:%d", address, port), 288 Username: username, 289 Password: password, 290 }) 291 if err != nil { 292 return errwrap.Wrapf("Error creating InfluxDB Client: ", err) 293 } 294 defer cli.Close() 295 _, _, err = cli.Ping(1) 296 if err != nil { 297 return errwrap.Wrapf("error checking server ping: {{err}}", err) 298 } 299 q := influx.NewQuery("SHOW SERIES ON vault", "", "") 300 response, err := cli.Query(q) 301 if err != nil { 302 return errwrap.Wrapf("error querying influxdb server: {{err}}", err) 303 } 304 if response.Error() != nil { 305 return errwrap.Wrapf("error using the correct influx database: {{err}}", response.Error()) 306 } 307 return nil 308} 309