---
title: "Using vaultr in packages"
author: "Rich FitzJohn"
date: "2023-11-09"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Using vaultr in packages}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

`vaultr` includes some machinery for using `vault` and `vaultr`
within packages, and within tests in particular.  They are designed
to work well with
[`testthat`](https://cran.r-project.org/package=testthat) but
should be easily adapted to work with any other testing framework.

In order to use this, you must set some environment variables:

* `VAULTR_TEST_SERVER_BIN_PATH` must be set to a directory where
  the `vault` binary can be found, the path to the vault executable,
  or to the string `auto` to find vault on the `PATH`
* `VAULTR_TEST_SERVER_PORT` can be set to the port where we start
  creating vault servers (by default this is 18200 but any high
  port number can be selected - we'll create servers *starting* at
  this port number and incrementing - see below for details)

To create a vault server, run:

```r
srv <- vaultr::vault_test_server()
```

```
## ...waiting for Vault to start
```

As soon as `srv` goes out of scope and is garbage collected, the
vault server will be stopped.  So keep `srv` within the scope of
your tests.

This object contains

* `addr`: which is vault's address
* `token`: a root token for this vault
* `keys`: a vector of unseal keys

By default the `vault` server is stared in ["Dev" server
mode](https://www.vaultproject.io/docs/concepts/dev-server.html) in
which we run with http (not https), a single unseal key and
in-memory storage.  **It is not suited for any production use**.

You can create clients using `vaultr::vault_client()` and passing
in appropriate parameters, but it may be more convenient to use
`srv$client()`:

```r
vault <- srv$client()
vault
```

```
## <vault: client>
##   Command groups:
##     audit: Interact with vault's audit devices
##     auth: administer vault's authentication methods
##     operator: Administration commands for vault operators
##     policy: Interact with policies
##     secrets: Interact with secret engines
##     token: Interact and configure vault's token support
##     tools: General tools provided by vault
##   Commands:
##     api()
##     delete(path)
##     help()
##     list(path, full_names = FALSE)
##     login(..., method = "token", mount = NULL, renew = FALSE,
##         quiet = FALSE, token_only = FALSE, use_cache = TRUE)
##     read(path, field = NULL, metadata = FALSE)
##     status()
##     unwrap(token)
##     wrap_lookup(token)
##     write(path, data)
```

```r
vault$list("secret")
```

```
## character(0)
```

By default the client is logged in, but you can pass `login =
FALSE` to create a client that needs to log in:

```r
vault <- srv$client(login = FALSE)
```


```r
vault$list("secret")
```

```
## Error: Have not authenticated against vault
```

```r
vault$login(token = srv$token)
```

```
## Verifying token
```

```r
vault$list("secret")
```

```
## character(0)
```

You can use `$export` to export appropriate environment variables
to connect to your vault:

```r
srv$export()
Sys.getenv("VAULT_ADDR")
```

```
## [1] "http://127.0.0.1:18200"
```

```r
Sys.getenv("VAULT_TOKEN")
```

```
## [1] "870e5c90-c908-bb4f-331e-c0cba32a457e"
```

## Handling lack of vault gracefully

The `vaultr::vault_test_server` function takes an argument
`if_disabled` which is a callback function that will be called on
failure to start a vault server.  This could be for reasons such as:

* the user has not opted in by setting `VAULTR_TEST_SERVER_BIN_PATH`
* the binary is not in place
* a port could not be opened



By default this calls `testthat::skip`, which interactively will
appear to cause an error but if called within a `test_that` block
in a test will gracefully skip a test

```r
Sys.setenv("VAULTR_TEST_SERVER_BIN_PATH" = NA_character_)
```

```r
srv <- vaultr::vault_test_server()
## Error: vault is not enabled
```


Alternatively, provide your own handler:

```r
srv <- vaultr::vault_test_server(if_disabled = message)
```

```
## vault is not enabled
```

```r
srv
```

```
## NULL
```

With that approach, you might wrap vault-requiring tests with

```r
if (!is.null(srv)) {
  # ... vault requiring code here ...
}
```

All together (and assuming `testthat`), use of vault within tests
might look like this example from the `vaultr` tests:

```r
test_that("list", {
  srv <- vault_test_server()
  cl <- srv$client()

  cl$write("secret/a", list(key = 1))
  cl$write("secret/b/c", list(key = 2))
  cl$write("secret/b/d/e", list(key = 2))

  expect_setequal(cl$list("secret"), c("a", "b/"))
  expect_setequal(cl$list("secret", TRUE), c("secret/a", "secret/b/"))
  expect_setequal(cl$list("secret/b"), c("c", "d/"))
  expect_setequal(cl$list("secret/b", TRUE), c("secret/b/c", "secret/b/d/"))
})
```

If you use one vault per test, as here, there's no need to clean up
- we can assume that the vault is empty at the start of the test
block and not worry about cleanup at the end.  If vault is not
enabled this test will be skipped over gracefully.

## Installing vault

To develop your package, you will need vault installed; please see [the official vault docs](https://developer.hashicorp.com/vault/docs/install) for this.

If you use github actions, you can follow the same approach as `vaultr` itself; add the environment variables `VAULTR_TEST_SERVER_BIN_PATH` and `VAULTR_TEST_SERVER_PORT`:

```yaml
    env:
      ...
      VAULTR_TEST_SERVER_BIN_PATH: auto
      VAULTR_TEST_SERVER_PORT: 18200
```

then use the [`eLco/setup-vault`](https://github.com/marketplace/actions/setup-vault-cli) action to install a suitable vault binary:

```yaml
      - uses: eLco/setup-vault@v1
```

See [the `vaultr` actions](https://github.com/vimc/vaultr/blob/master/.github/workflows/R-CMD-check.yaml) for full details.
