diff --git a/keramik/src/advanced_configuration.md b/keramik/src/advanced_configuration.md index 2e94b73b..b77d39ae 100644 --- a/keramik/src/advanced_configuration.md +++ b/keramik/src/advanced_configuration.md @@ -35,6 +35,23 @@ spec: casApiUrl: "https://some-anchor-service.com" ``` +# Adjusting Ceramic Environment +Ceramic environment can be adjusted by specifying environment variables in the network configuration + +```yaml +# network configuration +--- +apiVersion: "keramik.3box.io/v1alpha1" +kind: Network +metadata: + name: small +spec: + replicas: 2 + ceramic: + - env: + CERAMIC_PUBSUB_QPS_LIMIT: 500 +``` + # Disabling AWS Functionality Certain functionality in CAS depends on AWS services. If you are running Keramik in a non-AWS environment, you can disable this by editing the statefulset for CAS diff --git a/operator/src/network/ceramic.rs b/operator/src/network/ceramic.rs index 055e2468..40815dcb 100644 --- a/operator/src/network/ceramic.rs +++ b/operator/src/network/ceramic.rs @@ -138,6 +138,7 @@ pub struct CeramicConfig { pub image_pull_policy: String, pub ipfs: IpfsConfig, pub resource_limits: ResourceLimitsConfig, + pub env: Option>, } /// Bundles all relevant config for a ceramic spec. @@ -350,6 +351,7 @@ impl Default for CeramicConfig { memory: Quantity("1Gi".to_owned()), storage: Quantity("1Gi".to_owned()), }, + env: None, } } } @@ -379,6 +381,7 @@ impl From for CeramicConfig { value.resource_limits, default.resource_limits, ), + env: value.env, } } } @@ -661,6 +664,23 @@ pub fn stateful_set_spec(ns: &str, bundle: &CeramicBundle<'_>) -> StatefulSetSpe }, ]; + if let Some(extra_env) = &bundle.config.env { + extra_env.iter().for_each(|(key, value)| { + if let Some((pos, _)) = ceramic_env + .iter() + .enumerate() + .find(|(_, var)| &var.name == key) + { + ceramic_env.swap_remove(pos); + } + ceramic_env.push(EnvVar { + name: key.to_string(), + value: Some(value.to_string()), + ..Default::default() + }) + }); + } + let mut init_env = vec![EnvVar { name: "CERAMIC_ADMIN_PRIVATE_KEY".to_owned(), value_from: Some(EnvVarSource { diff --git a/operator/src/network/controller.rs b/operator/src/network/controller.rs index 39700dda..d2ea259d 100644 --- a/operator/src/network/controller.rs +++ b/operator/src/network/controller.rs @@ -3151,4 +3151,52 @@ mod tests { .expect("reconciler"); timeout_after_1s(mocksrv).await; } + #[tokio::test] + async fn ceramic_environment() { + // Setup network spec and status + let mut env = HashMap::default(); + env.insert("SOME_ENV_VAR".to_string(), "SOME_ENV_VALUE".to_string()); + let network = Network::test().with_spec(NetworkSpec { + ceramic: vec![CeramicSpec { + env: Some(env), + ..Default::default() + }], + ..Default::default() + }); + let mock_rpc_client = default_ipfs_rpc_mock(); + let mut stub = Stub::default().with_network(network.clone()); + stub.ceramics[0].stateful_set.patch(expect![[r#" + --- original + +++ modified + @@ -79,6 +79,10 @@ + { + "name": "CERAMIC_LOG_LEVEL", + "value": "2" + + }, + + { + + "name": "SOME_ENV_VAR", + + "value": "SOME_ENV_VALUE" + } + ], + "image": "ceramicnetwork/composedb:latest", + @@ -271,6 +275,10 @@ + { + "name": "CERAMIC_LOG_LEVEL", + "value": "2" + + }, + + { + + "name": "SOME_ENV_VAR", + + "value": "SOME_ENV_VALUE" + } + ], + "image": "ceramicnetwork/composedb:latest", + "#]]); + let (testctx, api_handle) = Context::test(mock_rpc_client); + let fakeserver = ApiServerVerifier::new(api_handle); + let mocksrv = stub.run(fakeserver); + reconcile(Arc::new(network), testctx) + .await + .expect("reconciler"); + timeout_after_1s(mocksrv).await; + } } diff --git a/operator/src/network/spec.rs b/operator/src/network/spec.rs index 67942b05..81dba020 100644 --- a/operator/src/network/spec.rs +++ b/operator/src/network/spec.rs @@ -96,6 +96,9 @@ pub struct CeramicSpec { pub ipfs: Option, /// Resource limits for ceramic nodes, applies to both requests and limits. pub resource_limits: Option, + /// Extra env values to pass to the image. + /// CAUTION: Any env vars specified in this set will override any predefined values. + pub env: Option>, } /// Describes how the IPFS node for a peer should behave.