diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index cfe048c..0b0c9f3 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -32,17 +32,8 @@ jobs: - name: Load Image into Kind run: kind load docker-image ext-cardano-dbsync:1.0 --name k8scluster - - name: Apply manifest - run: kubectl apply -f test/manifest.yaml - - - name: Wait containers is ready - run: sleep 8; - - name: Apply manifests - run: kubectl apply -f test/dbsyncport.yaml + run: kubectl apply -f test - - name: Validate if controller was executed - run: | - username=$(kubectl describe dbsyncports.demeter.run --namespace project useraccess | grep -oP 'Username: \K\S+') - password=$(kubectl describe dbsyncports.demeter.run --namespace project useraccess | grep -oP 'Password: \K\S+') - if [ -z "$username" ] && [ -z "$password" ]; then echo "Error: controller not executed" && exit 1; fi + - name: Validate controller + run: ./test/validate-execution diff --git a/Cargo.toml b/Cargo.toml index 8a90b9f..a6114fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,16 +24,13 @@ prometheus = "0.13.3" actix-web = "4.4.0" [[bin]] -doc = false name = "controller" path = "src/main.rs" [[bin]] -doc = false name = "crdgen" path = "src/crdgen.rs" [lib] -name = "controller" path = "src/lib.rs" diff --git a/README.md b/README.md index 4f6a9c5..3012731 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,30 @@ This project is a Kubernetes custom controller to create users on dbsync's Postgres. This controller defines a new CRD DbSyncPort on Kubernetes and when the new users enable the External Dbsync, the Demiter will generate a manifest with the kind DbSyncPort and the controller will be watching for creating a new user on Postgres. +> [!IMPORTANT] +> The metrics collector uses the `pg_stat_statements` extension enabled on Postgres. To enable that extension follow the steps bellow. + +- set pg_stat_statements at `shared_preload_libraries` on postgresql.conf + ``` + shared_preload_libraries = 'pg_stat_statements' + ``` +- create the extension on postgres + ``` + CREATE EXTENSION IF NOT EXISTS pg_stat_statements; + ``` + ## Environment -| Key | Value | -| -------------- | ------------------------------------- | -| ADDR | 0.0.0.0:5000 | -| DB_URL_MAINNET | postgres://user:password@host:post/db | -| DB_URL_PREPROD | postgres://user:password@host:post/db | -| DB_URL_PREVIEW | postgres://user:password@host:post/db | +| Key | Value | +| ---------------------- | ------------------------------------- | +| ADDR | 0.0.0.0:5000 | +| DB_URL_MAINNET | postgres://user:password@host:post/db | +| DB_URL_PREPROD | postgres://user:password@host:post/db | +| DB_URL_PREVIEW | postgres://user:password@host:post/db | +| METRICS_DELAY | 30 | +| DCU_PER_SECOND_MAINNET | 10 | +| DCU_PER_SECOND_PREPROD | 10 | +| DCU_PER_SECOND_PREVIEW | 10 | ## Commands diff --git a/src/controller.rs b/src/controller.rs index 4597e98..35cd572 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -1,7 +1,3 @@ -use crate::{ - postgres::{self, user_already_exists, user_create, user_disable, user_enable}, - Config, Error, Metrics, Result, -}; use futures::StreamExt; use kube::{ api::{Patch, PatchParams}, @@ -20,6 +16,8 @@ use serde_json::json; use std::{sync::Arc, time::Duration}; use tracing::error; +use crate::{postgres::Postgres, Config, Error, Metrics, State, Network}; + pub static DB_SYNC_PORT_FINALIZER: &str = "dbsyncports.demeter.run"; struct Context { @@ -36,25 +34,6 @@ impl Context { } } } -#[derive(Clone, Default)] -pub struct State { - registry: prometheus::Registry, -} -impl State { - pub fn metrics(&self) -> Vec { - self.registry.gather() - } -} - -#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] -pub enum Network { - #[serde(rename = "mainnet")] - Mainnet, - #[serde(rename = "preprod")] - Preprod, - #[serde(rename = "preview")] - Preview, -} #[derive(CustomResource, Deserialize, Serialize, Clone, Debug, JsonSchema)] #[kube(kind = "DbSyncPort", group = "demeter.run", version = "v1", namespaced)] @@ -75,11 +54,7 @@ impl DbSyncPort { .unwrap_or(false) } - async fn reconcile( - &self, - ctx: Arc, - pg_client: &mut tokio_postgres::Client, - ) -> Result { + async fn reconcile(&self, ctx: Arc, pg: &mut Postgres) -> Result { let client = ctx.client.clone(); let ns = self.namespace().unwrap(); let name = self.name_any(); @@ -89,9 +64,9 @@ impl DbSyncPort { let password = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); if !self.was_executed() { - match user_already_exists(pg_client, &username).await? { - true => user_enable(pg_client, &username, &password).await?, - false => user_create(pg_client, &username, &password).await?, + match pg.user_already_exists(&username).await? { + true => pg.user_enable(&username, &password).await?, + false => pg.user_create(&username, &password).await?, }; let new_status = Patch::Apply(json!({ @@ -114,19 +89,15 @@ impl DbSyncPort { Ok(Action::requeue(Duration::from_secs(5 * 60))) } - async fn cleanup( - &self, - ctx: Arc, - pg_client: &mut tokio_postgres::Client, - ) -> Result { + async fn cleanup(&self, ctx: Arc, pg: &mut Postgres) -> Result { let username = self.status.as_ref().unwrap().username.clone(); - user_disable(pg_client, &username).await?; + pg.user_disable(&username).await?; ctx.metrics.count_user_deactivated(&username); Ok(Action::await_change()) } } -async fn reconcile(crd: Arc, ctx: Arc) -> Result { +async fn reconcile(crd: Arc, ctx: Arc) -> Result { let url = match crd.spec.network { Network::Mainnet => &ctx.config.db_url_mainnet, Network::Preprod => &ctx.config.db_url_preprod, @@ -136,12 +107,12 @@ async fn reconcile(crd: Arc, ctx: Arc) -> Result { let ns = crd.namespace().unwrap(); let crds: Api = Api::namespaced(ctx.client.clone(), &ns); - let mut pg_client = postgres::connect(url).await?; + let mut postgres = Postgres::new(url).await?; finalizer(&crds, DB_SYNC_PORT_FINALIZER, crd, |event| async { match event { - Event::Apply(crd) => crd.reconcile(ctx.clone(), &mut pg_client).await, - Event::Cleanup(crd) => crd.cleanup(ctx.clone(), &mut pg_client).await, + Event::Apply(crd) => crd.reconcile(ctx.clone(), &mut postgres).await, + Event::Cleanup(crd) => crd.cleanup(ctx.clone(), &mut postgres).await, } }) .await @@ -154,11 +125,11 @@ fn error_policy(crd: Arc, err: &Error, ctx: Arc) -> Action Action::requeue(Duration::from_secs(5)) } -pub async fn run(state: State, config: Config) -> Result<(), Error> { +pub async fn run(state: Arc, config: Config) -> Result<(), Error> { let client = Client::try_default().await?; let crds = Api::::all(client.clone()); - let metrics = Metrics::default().register(&state.registry).unwrap(); - let ctx = Context::new(client, metrics, config); + + let ctx = Context::new(client, state.metrics.clone(), config); Controller::new(crds, WatcherConfig::default().any_semantic()) .shutdown_on_signal() diff --git a/src/crdgen.rs b/src/crdgen.rs index 57ad587..a3f9a52 100644 --- a/src/crdgen.rs +++ b/src/crdgen.rs @@ -1,3 +1,4 @@ +use ext_cardano_dbsync::controller; use kube::CustomResourceExt; fn main() { diff --git a/src/lib.rs b/src/lib.rs index bdef2a0..97097c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,10 @@ +use prometheus::Registry; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; use thiserror::Error; +use std::{fmt::Display, time::Duration}; + #[derive(Error, Debug)] pub enum Error { #[error("Postgres Error: {0}")] @@ -10,6 +15,18 @@ pub enum Error { #[error("Finalizer Error: {0}")] FinalizerError(#[source] Box>), + + #[error("Env Error: {0}")] + EnvError(#[source] std::env::VarError), + + #[error("Prometheus Error: {0}")] + PrometheusError(#[source] prometheus::Error), + + #[error("Parse Int Error: {0}")] + ParseIntError(#[source] std::num::ParseIntError), + + #[error("Parse Float Error: {0}")] + ParseFloatError(#[source] std::num::ParseFloatError), } impl Error { @@ -17,44 +34,112 @@ impl Error { format!("{self:?}").to_lowercase() } } - -pub type Result = std::result::Result; - impl From for Error { fn from(value: tokio_postgres::Error) -> Self { Error::PgError(value) } } - impl From for Error { fn from(value: kube::Error) -> Self { Error::KubeError(value) } } +impl From for Error { + fn from(value: std::env::VarError) -> Self { + Error::EnvError(value) + } +} +impl From for Error { + fn from(value: prometheus::Error) -> Self { + Error::PrometheusError(value) + } +} +impl From for Error { + fn from(value: std::num::ParseIntError) -> Self { + Error::ParseIntError(value) + } +} +impl From for Error { + fn from(value: std::num::ParseFloatError) -> Self { + Error::ParseFloatError(value) + } +} +#[derive(Clone, Default)] +pub struct State { + registry: Registry, + pub metrics: Metrics, +} +impl State { + pub fn new() -> Self { + let registry = Registry::default(); + let metrics = Metrics::default().register(®istry).unwrap(); + Self { registry, metrics } + } + + pub fn metrics_collected(&self) -> Vec { + self.registry.gather() + } +} + +#[derive(Clone)] pub struct Config { pub db_url_mainnet: String, pub db_url_preprod: String, pub db_url_preview: String, + + pub dcu_per_second_mainnet: f64, + pub dcu_per_second_preprod: f64, + pub dcu_per_second_preview: f64, + + pub metrics_delay: Duration, } impl Config { - pub fn new() -> Self { - Self { - db_url_mainnet: std::env::var("DB_URL_MAINNET").expect("DB_URL_MAINNET must be set"), - db_url_preprod: std::env::var("DB_URL_PREPROD").expect("DB_URL_PREPROD must be set"), - db_url_preview: std::env::var("DB_URL_PREVIEW").expect("DB_URL_PREVIEW must be set"), - } + pub fn try_new() -> Result { + let db_url_mainnet = std::env::var("DB_URL_MAINNET")?; + let db_url_preprod = std::env::var("DB_URL_PREPROD")?; + let db_url_preview = std::env::var("DB_URL_PREVIEW")?; + + let metrics_delay = Duration::from_secs(std::env::var("METRICS_DELAY")?.parse::()?); + + let dcu_per_second_mainnet = std::env::var("DCU_PER_SECOND_MAINNET")?.parse::()?; + let dcu_per_second_preprod = std::env::var("DCU_PER_SECOND_PREPROD")?.parse::()?; + let dcu_per_second_preview = std::env::var("DCU_PER_SECOND_PREVIEW")?.parse::()?; + + Ok(Self { + db_url_mainnet, + db_url_preprod, + db_url_preview, + metrics_delay, + dcu_per_second_mainnet, + dcu_per_second_preprod, + dcu_per_second_preview, + }) } } -impl Default for Config { - fn default() -> Self { - Self::new() + +#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] +pub enum Network { + #[serde(rename = "mainnet")] + Mainnet, + #[serde(rename = "preprod")] + Preprod, + #[serde(rename = "preview")] + Preview, +} +impl Display for Network { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Network::Mainnet => write!(f, "mainnet"), + Network::Preprod => write!(f, "preprod"), + Network::Preview => write!(f, "preview"), + } } } pub mod controller; +pub mod metrics; pub mod postgres; -pub use crate::controller::*; -mod metrics; -pub use metrics::Metrics; +pub use controller::*; +pub use metrics::*; diff --git a/src/main.rs b/src/main.rs index 610e1f8..36b36c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,16 @@ -use std::io; - use actix_web::{ get, middleware, web::Data, App, HttpRequest, HttpResponse, HttpServer, Responder, }; -use controller::{Config, State}; use dotenv::dotenv; use prometheus::{Encoder, TextEncoder}; +use std::{io, sync::Arc}; +use tracing::error; + +use ext_cardano_dbsync::{controller, metrics as metrics_collector, Config, State}; #[get("/metrics")] -async fn metrics(c: Data, _req: HttpRequest) -> impl Responder { - let metrics = c.metrics(); +async fn metrics(c: Data>, _req: HttpRequest) -> impl Responder { + let metrics = c.metrics_collected(); let encoder = TextEncoder::new(); let mut buffer = vec![]; encoder.encode(&metrics, &mut buffer).unwrap(); @@ -25,10 +26,11 @@ async fn health(_: HttpRequest) -> impl Responder { async fn main() -> io::Result<()> { dotenv().ok(); - let state = State::default(); - let config = Config::default(); + let state = Arc::new(State::new()); + let config = Config::try_new().unwrap(); - let controller = controller::run(state.clone(), config); + let controller = controller::run(state.clone(), config.clone()); + let metrics_collector = metrics_collector::run_metrics_collector(state.clone(), config.clone()); let addr = std::env::var("ADDR").unwrap_or("0.0.0.0:8080".into()); @@ -41,7 +43,11 @@ async fn main() -> io::Result<()> { }) .bind(addr)?; - tokio::join!(controller, server.run()).1?; + let result = tokio::join!(controller, metrics_collector, server.run()).1; + if let Err(err) = result { + error!("{err}"); + std::process::exit(1) + } Ok(()) } diff --git a/src/metrics.rs b/src/metrics.rs index 61ca56b..2092e84 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -1,12 +1,19 @@ -use crate::{DbSyncPort, Error}; -use kube::ResourceExt; +use kube::{Resource, ResourceExt}; use prometheus::{opts, IntCounterVec, Registry}; +use std::{sync::Arc, thread::sleep}; +use tracing::error; + +use crate::{ + postgres::{Postgres, UserStatements}, + Config, DbSyncPort, Error, Network, State, +}; #[derive(Clone)] pub struct Metrics { pub users_created: IntCounterVec, pub users_deactivated: IntCounterVec, pub failures: IntCounterVec, + pub dcu: IntCounterVec, } impl Default for Metrics { @@ -38,10 +45,17 @@ impl Default for Metrics { ) .unwrap(); + let dcu = IntCounterVec::new( + opts!("dmtr_consumed_dcus", "quantity of dcu consumed",), + &["project", "service", "service_type", "tenancy"], + ) + .unwrap(); + Metrics { users_created, users_deactivated, failures, + dcu, } } } @@ -51,6 +65,7 @@ impl Metrics { registry.register(Box::new(self.failures.clone()))?; registry.register(Box::new(self.users_created.clone()))?; registry.register(Box::new(self.users_deactivated.clone()))?; + registry.register(Box::new(self.dcu.clone()))?; Ok(self) } @@ -67,4 +82,92 @@ impl Metrics { pub fn count_user_deactivated(&self, username: &str) { self.users_deactivated.with_label_values(&[username]).inc(); } + + pub fn count_dcu_consumed(&self, usename: &str, network: &Network, dcu: f64) { + let project = usename.split_once("prj-").unwrap().1; + let service = format!("{}-{}", DbSyncPort::kind(&()), network); + let service_type = format!("{}.{}", DbSyncPort::plural(&()), DbSyncPort::group(&())); + let tenancy = "proxy"; + + let dcu: u64 = dcu.ceil() as u64; + + self.dcu + .with_label_values(&[project, &service, &service_type, tenancy]) + .inc_by(dcu); + } +} + +pub async fn run_metrics_collector(state: Arc, config: Config) -> Result<(), Error> { + let mut network_state: Vec<(Network, String, f64, Option>)> = vec![ + ( + Network::Mainnet, + config.db_url_mainnet, + config.dcu_per_second_mainnet, + None, + ), + ( + Network::Preprod, + config.db_url_preprod, + config.dcu_per_second_preprod, + None, + ), + ( + Network::Preview, + config.db_url_preview, + config.dcu_per_second_preview, + None, + ), + ]; + + loop { + for (network, url, dcu_per_second, latest_execution) in network_state.iter_mut() { + let postgres_result = Postgres::new(url).await; + if let Err(err) = postgres_result { + error!("Error to connect postgres: {err}"); + continue; + } + let postgres = postgres_result.unwrap(); + + let user_statements_result = postgres.user_metrics().await; + if let Err(err) = user_statements_result { + error!("Error get user statements: {err}"); + continue; + } + + let user_statements = user_statements_result.unwrap(); + if user_statements.is_none() { + continue; + } + + let user_statements = user_statements.unwrap(); + + if let Some(latest_execution) = latest_execution { + for user_statement in user_statements.iter() { + let latest_user_statement = latest_execution + .iter() + .find(|le| le.usename.eq(&user_statement.usename)); + + let mut total_exec_time = user_statement.total_exec_time; + + if let Some(latest_user_statement) = latest_user_statement { + total_exec_time = + user_statement.total_exec_time - latest_user_statement.total_exec_time; + } + + if total_exec_time == 0.0 { + continue; + } + + let dcu = (total_exec_time / 1000.) * dcu_per_second as &f64; + state + .metrics + .count_dcu_consumed(&user_statement.usename, network, dcu); + } + } + + *latest_execution = Some(user_statements); + } + + sleep(config.metrics_delay) + } } diff --git a/src/postgres.rs b/src/postgres.rs index 7fcad96..d0fcdce 100644 --- a/src/postgres.rs +++ b/src/postgres.rs @@ -1,82 +1,127 @@ -use tokio_postgres::{Client, NoTls}; +use tokio_postgres::{Client, NoTls, Row}; use crate::Error; -pub async fn connect(url: &str) -> Result { - let (client, connection) = tokio_postgres::connect(url, NoTls).await?; - - tokio::spawn(async move { - if let Err(e) = connection.await { - eprintln!("connection error: {}", e); - } - }); - - Ok(client) +pub struct Postgres { + client: Client, } -pub async fn user_create(client: &mut Client, username: &str, password: &str) -> Result<(), Error> { - let query_create_user = format!("create user \"{username}\" with password '{password}';"); - let query_create_role = - format!("grant select on all tables in schema public to \"{username}\";"); +impl Postgres { + pub async fn new(url: &str) -> Result { + let (client, connection) = tokio_postgres::connect(url, NoTls).await?; - let tx = client.transaction().await?; + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("connection error: {}", e); + } + }); - let user_stmt = tx.prepare(&query_create_user).await?; - let user_result = tx.execute(&user_stmt, &[]).await; - if let Err(err) = user_result { - tx.rollback().await?; - return Err(Error::PgError(err)); + Ok(Self { client }) } - let role_stmt = tx.prepare(&query_create_role).await?; - let role_result = tx.execute(&role_stmt, &[]).await; - if let Err(err) = role_result { - tx.rollback().await?; - return Err(Error::PgError(err)); - } + pub async fn user_create(&mut self, username: &str, password: &str) -> Result<(), Error> { + let query_create_user = format!("create user \"{username}\" with password '{password}';"); + let query_create_role = + format!("grant select on all tables in schema public to \"{username}\";"); - tx.commit().await?; - Ok(()) -} + let tx = self.client.transaction().await?; -pub async fn user_disable(client: &mut Client, username: &str) -> Result<(), Error> { - let query_revoke_login = format!("alter user \"{username}\" with nologin;"); + let user_stmt = tx.prepare(&query_create_user).await?; + let user_result = tx.execute(&user_stmt, &[]).await; + if let Err(err) = user_result { + tx.rollback().await?; + return Err(Error::PgError(err)); + } - let revoke_stmt = client.prepare(&query_revoke_login).await?; - client.execute(&revoke_stmt, &[]).await?; + let role_stmt = tx.prepare(&query_create_role).await?; + let role_result = tx.execute(&role_stmt, &[]).await; + if let Err(err) = role_result { + tx.rollback().await?; + return Err(Error::PgError(err)); + } - Ok(()) -} + tx.commit().await?; + Ok(()) + } -pub async fn user_enable(client: &mut Client, username: &str, password: &str) -> Result<(), Error> { - let query_grant_login = format!("alter user \"{username}\" with login;"); - let query_alter_password = format!("alter user \"{username}\" with password '{password}';"); + pub async fn user_disable(&self, username: &str) -> Result<(), Error> { + let query_revoke_login = format!("alter user \"{username}\" with nologin;"); - let tx = client.transaction().await?; + let revoke_stmt = self.client.prepare(&query_revoke_login).await?; + self.client.execute(&revoke_stmt, &[]).await?; - let login_stmt = tx.prepare(&query_grant_login).await?; - let login_result = tx.execute(&login_stmt, &[]).await; - if let Err(err) = login_result { - tx.rollback().await?; - return Err(Error::PgError(err)); + Ok(()) } - let alter_stmt = tx.prepare(&query_alter_password).await?; - let alter_result = tx.execute(&alter_stmt, &[]).await; - if let Err(err) = alter_result { - tx.rollback().await?; - return Err(Error::PgError(err)); + pub async fn user_enable(&mut self, username: &str, password: &str) -> Result<(), Error> { + let query_grant_login = format!("alter user \"{username}\" with login;"); + let query_alter_password = format!("alter user \"{username}\" with password '{password}';"); + + let tx = self.client.transaction().await?; + + let login_stmt = tx.prepare(&query_grant_login).await?; + let login_result = tx.execute(&login_stmt, &[]).await; + if let Err(err) = login_result { + tx.rollback().await?; + return Err(Error::PgError(err)); + } + + let alter_stmt = tx.prepare(&query_alter_password).await?; + let alter_result = tx.execute(&alter_stmt, &[]).await; + if let Err(err) = alter_result { + tx.rollback().await?; + return Err(Error::PgError(err)); + } + + tx.commit().await?; + Ok(()) } - tx.commit().await?; - Ok(()) -} + pub async fn user_already_exists(&self, username: &str) -> Result { + let query = "select rolname from pg_roles where rolname = $1;"; + + let user_stmt = self.client.prepare(query).await?; + let result = self.client.query_opt(&user_stmt, &[&username]).await?; + + Ok(result.is_some()) + } -pub async fn user_already_exists(client: &mut Client, username: &str) -> Result { - let query = "select rolname from pg_roles where rolname = $1;"; + pub async fn user_metrics(&self) -> Result>, Error> { + let query_metrics = "SELECT + usename, + SUM(total_exec_time) AS total_exec_time + FROM + pg_stat_statements + inner join + pg_catalog.pg_user on pg_catalog.pg_user.usesysid = userid + where + pg_catalog.pg_user.usename like '%.prj-%' + group by + usename;"; + + let stmt = self.client.prepare(query_metrics).await?; + let result = self.client.query(&stmt, &[]).await?; + + if !result.is_empty() { + let user_statements: Vec = + result.iter().map(|row| row.into()).collect(); + return Ok(Some(user_statements)); + } - let user_stmt = client.prepare(query).await?; - let result = client.query_opt(&user_stmt, &[&username]).await?; + Ok(None) + } +} - Ok(result.is_some()) +#[derive(Debug)] +pub struct UserStatements { + pub usename: String, + pub total_exec_time: f64, +} +impl From<&Row> for UserStatements { + fn from(row: &Row) -> Self { + Self { + usename: row.get("usename"), + total_exec_time: row.get("total_exec_time"), + } + } } diff --git a/test/dbsyncport.yaml b/test/dbsyncport.yaml deleted file mode 100644 index 475f3f5..0000000 --- a/test/dbsyncport.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: demeter.run/v1 -kind: DbSyncPort -metadata: - name: useraccess - namespace: project -spec: - network: "preview" - diff --git a/test/manifest.yaml b/test/manifest.yaml index 184ad4b..719d77c 100644 --- a/test/manifest.yaml +++ b/test/manifest.yaml @@ -2,14 +2,14 @@ apiVersion: v1 kind: Namespace metadata: - name: project + name: prj-ci-test --- # Postgres dependence apiVersion: apps/v1 kind: Deployment metadata: name: postgres - namespace: project + namespace: prj-ci-test labels: app: postgres spec: @@ -41,7 +41,7 @@ apiVersion: v1 kind: Service metadata: name: app - namespace: project + namespace: prj-ci-test labels: app: postgres spec: @@ -55,7 +55,7 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: controller - namespace: project + namespace: prj-ci-test labels: app: controller app.kubernetes.io/name: controller @@ -107,7 +107,7 @@ metadata: app: controller app.kubernetes.io/name: controller app.kubernetes.io/version: "0.12.11" - namespace: project + namespace: prj-ci-test automountServiceAccountToken: true --- # Access for the service account @@ -130,7 +130,7 @@ metadata: name: controller subjects: - kind: ServiceAccount - namespace: project + namespace: prj-ci-test name: controller roleRef: kind: ClusterRole @@ -142,7 +142,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: controller - namespace: project + namespace: prj-ci-test labels: app: controller spec: @@ -164,11 +164,19 @@ spec: - name: ADDR value: "0.0.0.0:80" - name: DB_URL_MAINNET - value: "postgres://user:password@app.project.svc.cluster.local:5432/postgres?sslmode=disable" + value: "postgres://user:password@app.prj-ci-test.svc.cluster.local:5432/postgres?sslmode=disable" - name: DB_URL_PREPROD - value: "postgres://user:password@app.project.svc.cluster.local:5432/postgres?sslmode=disable" + value: "postgres://user:password@app.prj-ci-test.svc.cluster.local:5432/postgres?sslmode=disable" - name: DB_URL_PREVIEW - value: "postgres://user:password@app.project.svc.cluster.local:5432/postgres?sslmode=disable" + value: "postgres://user:password@app.prj-ci-test.svc.cluster.local:5432/postgres?sslmode=disable" + - name: METRICS_DELAY + value: "30" + - name: DCU_PER_SECOND_MAINNET + value: "10" + - name: DCU_PER_SECOND_PREPROD + value: "10" + - name: DCU_PER_SECOND_PREVIEW + value: "10" - name: RUST_LOG value: info,kube=debug,controller=debug --- @@ -176,7 +184,7 @@ apiVersion: v1 kind: Service metadata: name: api - namespace: project + namespace: prj-ci-test labels: app: controller spec: @@ -188,3 +196,11 @@ spec: port: 80 targetPort: 80 protocol: TCP +--- +apiVersion: demeter.run/v1 +kind: DbSyncPort +metadata: + name: ci-user + namespace: prj-ci-test +spec: + network: "preview" diff --git a/test/validate-execution b/test/validate-execution new file mode 100755 index 0000000..146358a --- /dev/null +++ b/test/validate-execution @@ -0,0 +1,15 @@ +ATTEMPT=1 +MAX_ATTEMPT=60 + +echo "Checking if the controller was executed." +while [ -z "$EXT_USERNAME" ] && [ -z "$EXT_PASSWORD" ] && [ $ATTEMPT -lt $MAX_ATTEMPT ]; do + let ATTEMPT=ATTEMPT+1 + EXT_USERNAME=$(kubectl describe dbsyncports.demeter.run --namespace prj-ci-test ci-user | grep -oP 'Username: \K\S+') + EXT_PASSWORD=$(kubectl describe dbsyncports.demeter.run --namespace prj-ci-test ci-user | grep -oP 'Password: \K\S+') + sleep 2 +done + +if [ -z "$EXT_USERNAME" ] && [ -z "$EXT_PASSWORD" ]; then + echo "Error: controller not executed" + exit 1 +fi \ No newline at end of file diff --git a/yaml/user-access.yaml b/yaml/user-access.yaml index d298a31..9a17e30 100644 --- a/yaml/user-access.yaml +++ b/yaml/user-access.yaml @@ -1,8 +1,8 @@ apiVersion: demeter.run/v1 kind: DbSyncPort metadata: - name: useraccess - namespace: cardanowarriors + name: mainnet-user + namespace: prj-mainnet-test spec: - network: "preview" + network: "mainnet" diff --git a/yaml/user-namespace.yaml b/yaml/user-namespace.yaml index cac731d..c212fec 100644 --- a/yaml/user-namespace.yaml +++ b/yaml/user-namespace.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: cardanowarriors \ No newline at end of file + name: prj-mainnet-test \ No newline at end of file