Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Infra** New `job-runner` crate responsible for managing the OCI bundle runtime & log shipping on the machine
- **Infra** Jobs now log an explicit rate message when logs are rate limited & truncated
- **Infra** `infra-artifacts` Terraform plan & S3 bucket used for automating building & uploading internal binaries, etc.
- **Infra** Aiven Redis provider
- **Bolt** `bolt secret set <path> <value>` command

### Changed
Expand Down
79 changes: 79 additions & 0 deletions infra/tf/redis_aiven/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
locals {
db_configs = {
ephemeral = {
plan = var.redis_aiven.plan_ephemeral
maxmemory_policy = "allkeys-lru"
persistence = "off"
}
persistent = {
plan = var.redis_aiven.plan_persistent
maxmemory_policy = "noeviction"
persistence = "rdb"
}
}
}

module "secrets" {
source = "../modules/secrets"

keys = flatten([
["aiven/api_token"],
[
for k, v in var.redis_dbs:
[
"redis/${k}/username",
"redis/${k}/password",
]
],
])
}

resource "aiven_redis" "main" {
for_each = var.redis_dbs

project = var.redis_aiven.project
cloud_name = var.redis_aiven.cloud
plan = local.db_configs[each.key].plan
service_name = "rivet-${var.namespace}-${each.key}"
maintenance_window_dow = "monday"
maintenance_window_time = "10:00:00"
Comment on lines +38 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, anything special about this time for us?


tag {
key = "rivet:namespace"
value = var.namespace
}

redis_user_config {
redis_ssl = true
redis_maxmemory_policy = local.db_configs[each.key].maxmemory_policy
redis_persistence = local.db_configs[each.key].persistence

dynamic "ip_filter_object" {
for_each = sort(data.terraform_remote_state.k8s_cluster_aws.outputs.nat_public_ips)

content {
network = "${ip_filter_object.value}/32"
description = "AWS NAT"
}
}

public_access {
redis = true
}
}
}

resource "aiven_redis_user" "main" {
for_each = var.redis_dbs

project = var.redis_aiven.project
service_name = aiven_redis.main[each.key].service_name
username = module.secrets.values["redis/${each.key}/username"]
password = module.secrets.values["redis/${each.key}/password"]

redis_acl_categories = ["+@all"]
redis_acl_commands = []
redis_acl_channels = ["*"]
redis_acl_keys = ["*"]
}

14 changes: 14 additions & 0 deletions infra/tf/redis_aiven/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
output "host" {
value = {
for k, v in var.redis_dbs:
k => aiven_redis.main[k].service_host
}
}

output "port" {
value = {
for k, v in var.redis_dbs:
k => aiven_redis.main[k].service_port
}
}

13 changes: 13 additions & 0 deletions infra/tf/redis_aiven/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
terraform {
required_providers {
aiven = {
source = "aiven/aiven"
version = "4.12.1"
}
}
}

provider "aiven" {
api_token = module.secrets.values["aiven/api_token"]
}

19 changes: 19 additions & 0 deletions infra/tf/redis_aiven/vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
variable "namespace" {
type = string
}

variable "redis_dbs" {
type = map(object({
persistent = bool
}))
}

variable "redis_aiven" {
type = object({
project = string
cloud = string
plan_ephemeral = string
plan_persistent = string
})
}

4 changes: 2 additions & 2 deletions lib/bolt/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions lib/bolt/config/src/ns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,13 @@ pub enum RedisProvider {
Kubernetes {},
#[serde(rename = "aws")]
Aws {},
#[serde(rename = "aiven")]
Aiven {
project: String,
cloud: String,
plan_ephemeral: String,
plan_persistent: String,
},
}

impl Default for RedisProvider {
Expand Down
3 changes: 2 additions & 1 deletion lib/bolt/core/src/context/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,8 @@ impl ServiceContextData {

// Read auth secrets
let (username, password) = match project_ctx.ns().redis.provider {
config::ns::RedisProvider::Kubernetes {} => (
config::ns::RedisProvider::Kubernetes {}
| config::ns::RedisProvider::Aiven { .. } => (
project_ctx
.read_secret(&["redis", &db_name, "username"])
.await?,
Expand Down
2 changes: 1 addition & 1 deletion lib/bolt/core/src/dep/k8s/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ async fn build_volumes(
})
}));
}
config::ns::RedisProvider::Aws { .. } => {
config::ns::RedisProvider::Aws { .. } | config::ns::RedisProvider::Aiven { .. } => {
// Uses publicly signed cert
}
}
Expand Down
37 changes: 28 additions & 9 deletions lib/bolt/core/src/dep/terraform/gen.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{Context, Result};
use indoc::{formatdoc, indoc};
use serde_json::json;
use std::collections::HashMap;
Expand Down Expand Up @@ -82,7 +82,9 @@ pub async fn gen_bolt_tf(ctx: &ProjectContext, plan_id: &str) -> Result<()> {
format!("# This is generated by Bolt. Do not modify.\n\n{backend}\n\n{remote_states}");
let path = ctx.tf_path().join(plan_id).join("_bolt.tf");

tokio::fs::write(&path, bolt_tf).await?;
tokio::fs::write(&path, bolt_tf)
.await
.context(format!("write _bolt.tf to {}", path.display()))?;

Ok(())
}
Expand Down Expand Up @@ -439,14 +441,31 @@ async fn vars(ctx: &ProjectContext) {
}
}

let redis_provider = match &config.redis.provider {
ns::RedisProvider::Kubernetes { .. } => "kubernetes",
ns::RedisProvider::Aws { .. } => "aws",
ns::RedisProvider::Aiven {
project,
cloud,
plan_ephemeral,
plan_persistent,
} => {
vars.insert(
"redis_aiven".into(),
json!({
"project": project,
"cloud": cloud,
"plan_ephemeral": plan_ephemeral,
"plan_persistent": plan_persistent,
}),
);

"aiven"
}
};

vars.insert("redis_replicas".into(), json!(config.redis.replicas));
vars.insert(
"redis_provider".into(),
json!(match config.redis.provider {
ns::RedisProvider::Kubernetes { .. } => "kubernetes",
ns::RedisProvider::Aws { .. } => "aws",
}),
);
vars.insert("redis_provider".into(), json!(redis_provider));
vars.insert("redis_dbs".into(), json!(redis_dbs));
}

Expand Down
10 changes: 5 additions & 5 deletions lib/bolt/core/src/dep/terraform/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ pub async fn read_clickhouse(ctx: &ProjectContext) -> ClickHouse {
}

pub async fn read_redis(ctx: &ProjectContext) -> Redis {
// match &ctx.ns().cluster.kind {
// config::ns::ClusterKind::SingleNode { .. } => read_plan::<Redis>(ctx, "redis_k8s").await,
// config::ns::ClusterKind::Distributed { .. } => read_plan::<Redis>(ctx, "redis_aws").await,
// }
read_plan::<Redis>(ctx, "redis_k8s").await
match &ctx.ns().redis.provider {
config::ns::RedisProvider::Kubernetes { .. } => read_plan::<Redis>(ctx, "redis_k8s").await,
config::ns::RedisProvider::Aws { .. } => read_plan::<Redis>(ctx, "redis_aws").await,
config::ns::RedisProvider::Aiven { .. } => read_plan::<Redis>(ctx, "redis_aiven").await,
}
}

/// Reads a Terraform plan's output and decodes in to type.
Expand Down
3 changes: 3 additions & 0 deletions lib/bolt/core/src/dep/terraform/remote_states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use crate::context::ProjectContext;
pub fn dependency_graph(_ctx: &ProjectContext) -> HashMap<&'static str, Vec<RemoteState>> {
hashmap! {
"dns" => vec![RemoteStateBuilder::default().plan_id("pools").build().unwrap(), RemoteStateBuilder::default().plan_id("k8s_infra").build().unwrap()],
"redis_aiven" => vec![
RemoteStateBuilder::default().plan_id("k8s_cluster_aws").build().unwrap()
],
"redis_aws" => vec![
RemoteStateBuilder::default().plan_id("k8s_cluster_aws").build().unwrap()
],
Expand Down
2 changes: 1 addition & 1 deletion lib/bolt/core/src/tasks/config/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ pub async fn generate(project_path: &Path, ns_id: &str) -> Result<()> {
"ephemeral"
};
let (db_name, username) = match &ctx.ns().redis.provider {
config::ns::RedisProvider::Kubernetes {} => {
config::ns::RedisProvider::Kubernetes {} | config::ns::RedisProvider::Aiven { .. } => {
(db_name.to_string(), "default".to_string())
}
config::ns::RedisProvider::Aws {} => {
Expand Down
65 changes: 42 additions & 23 deletions lib/bolt/core/src/tasks/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ async fn redis_shell(shell_ctx: ShellContext<'_>) -> Result<()> {

// Read auth secrets
let (username, password) = match ctx.ns().redis.provider {
config::ns::RedisProvider::Kubernetes {} => (
config::ns::RedisProvider::Kubernetes {} | config::ns::RedisProvider::Aiven { .. } => (
ctx.read_secret(&["redis", &db_name, "username"]).await?,
ctx.read_secret_opt(&["redis", &db_name, "password"])
.await?,
Expand All @@ -93,6 +93,10 @@ async fn redis_shell(shell_ctx: ShellContext<'_>) -> Result<()> {
(username, password)
}
};
let mount_ca = matches!(
ctx.ns().redis.provider,
config::ns::RedisProvider::Kubernetes {}
);

if let LogType::Default = log_type {
rivet_term::status::progress("Connecting to Redis", &db_name);
Expand All @@ -110,6 +114,7 @@ async fn redis_shell(shell_ctx: ShellContext<'_>) -> Result<()> {
} else {
Vec::new()
};

let cmd = formatdoc!(
"
sleep 1 &&
Expand All @@ -118,9 +123,15 @@ async fn redis_shell(shell_ctx: ShellContext<'_>) -> Result<()> {
-p {port} \
--user {username} \
-c \
--tls --cacert /local/redis-ca.crt
"
--tls {cacert}
",
cacert = if mount_ca {
"--cacert /local/redis-ca.crt"
} else {
""
}
);

let overrides = json!({
"apiVersion": "v1",
"metadata": {
Expand All @@ -146,28 +157,36 @@ async fn redis_shell(shell_ctx: ShellContext<'_>) -> Result<()> {
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": [{
"name": "redis-ca",
"mountPath": "/local/redis-ca.crt",
"subPath": "redis-ca.crt"
}]
"volumeMounts": if mount_ca {
json!([{
"name": "redis-ca",
"mountPath": "/local/redis-ca.crt",
"subPath": "redis-ca.crt"
}])
} else {
json!([])
}
}
],
"volumes": [{
"name": "redis-ca",
"configMap": {
"name": format!("redis-{}-ca", db_name),
"defaultMode": 420,
// Distributed clusters don't need a CA for redis
"optional": true,
"items": [
{
"key": "ca.crt",
"path": "redis-ca.crt"
}
]
}
}]
"volumes": if mount_ca {
json!([{
"name": "redis-ca",
"configMap": {
"name": format!("redis-{}-ca", db_name),
"defaultMode": 420,
// Distributed clusters don't need a CA for redis
"optional": true,
"items": [
{
"key": "ca.crt",
"path": "redis-ca.crt"
}
]
}
}])
} else {
json!([])
}
}
});

Expand Down
9 changes: 9 additions & 0 deletions lib/bolt/core/src/tasks/infra/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,15 @@ pub fn build_plan(
},
});
}
ns::RedisProvider::Aiven { .. } => {
plan.push(PlanStep {
name_id: "redis-aiven",
kind: PlanStepKind::Terraform {
plan_id: "redis_aiven".into(),
needs_destroy: true,
},
});
}
}

// CockroachDB
Expand Down