vignettes/how-to/manage-secrets-and-environments.Rmd
manage-secrets-and-environments.RmdManaging secrets and configuration can be a challenge in applications which need to work in different development and deployment environments. Typically, secrets are credentials for an external service such as a database. Environments, on the other hand, are used to manage multiple configurations that we can be easily switched between. Rhino recommends a way for working with either one.
Secrets are a confidential information and should not be tracked in
your version control system. Therefore, a natural place for them are
system environment variables. Variables set in system environment can be
retrieved within your code with Sys.getenv().
R provides a way to easily set environment variables. Upon a session
start (or restart) R reads .Renviron file contents and sets
environment variables.
.Renviron
# A comment in .Renviron file
DATABASE_PASSWORD="foobar123!"
API_KEY="75170fc230cd88f32e475ff4087f81d9"Secrets defined via environment variables can be read and used the following way:
db_password <- Sys.getenv("DATABASE_PASSWORD")
if (db_password == "") {
# Handle unset or empty DATABASE_PASSWORD variable
}
pool <- pool::dbPool(
drv = RMySQL::MySQL(),
dbname = "...",
host = "...",
username = "admin",
password = db_password
).Renviron..Renviron file for every environment.
Swap the whole file when changing environments.CONSTANT_CASE for variable names..Renviron file in a version control
system. Store it in a secure location, e.g. a password manager..Renviron to RStudio Connect nor
shinyapps.io. Both, RStudio
Connect and Shiny Apps, provide means to manage environment
variables.Having every configurable setting stored as an environment variable
would result in overgrown .Renviron files. That’s where
configurable environments come in.
Everything that is not confidential can be tracked by a version control system. Rhino endorses use of config package for managing environments.
config.yml
default:
rhino_log_level: !expr Sys.getenv("RHINO_LOG_LEVEL", "INFO")
rhino_log_file: !expr Sys.getenv("RHINO_LOG_FILE", NA)
database_user: "service_account"
database_schema: "dev"
dev:
rhino_log_level: !expr Sys.getenv("RHINO_LOG_LEVEL", "DEBUG")
staging:
database_schema: "stg"
production:
database_user: "service_account_prod"
database_schema: "prod".Renviron
You can access the configuration variables in the following way:
box::use(config)
config$get("rhino_log_level") # == "DEBUG"
config$get("database_user") # == "service_account"
config$get("rhino_log_level", config = "production") # == "INFO"
config$get("database_user", config = "production") # == "service_account_prod"
withr::with_envvar(list(RHINO_LOG_LEVEL = "ERROR"), {
config$get("rhino_log_level") # == "ERROR"
config$get("rhino_log_level", config = "production") # == "ERROR"
})config.yml.R_CONFIG_ACTIVE variable in
.Renviron.!expr Sys.getenv() to make settings overridable
with environment variables.box::use(config) and config$get().snake_case for field names.