Skip to content

Commit

Permalink
adds restapi support for app_services for local that uses postgrest…
Browse files Browse the repository at this point in the history
… under the hood (#521)
  • Loading branch information
shahadarsh authored Feb 1, 2024
1 parent 763aca7 commit 5b861f2
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 8 deletions.
3 changes: 2 additions & 1 deletion tembo-cli/Cargo.lock

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

3 changes: 2 additions & 1 deletion tembo-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
workspace = { members = ["temboclient", "tembodataclient"] }
[package]
name = "tembo-cli"
version = "0.15.2"
version = "0.16.0"
edition = "2021"
authors = ["Tembo.io"]
description = "The CLI for Tembo"
Expand Down Expand Up @@ -59,6 +59,7 @@ cli-table = "0.4.7"
tiny-gradient = "0.1.0"
urlencoding = "2.1.3"
spinoff = "0.8.0"
k8s-openapi = { version = "0.18.0", features = ["v1_25", "schemars"], default-features = false }

[target.aarch64-unknown-linux-musl.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
Expand Down
2 changes: 2 additions & 0 deletions tembo-cli/examples/multiple-instances/tembo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ storage = "50Gi"
replicas = 1
stack_type = "OLTP"

[instance-2.app_services.restapi]
restapi = { }
4 changes: 2 additions & 2 deletions tembo-cli/src/cli/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ pub fn list_credential_profiles() -> Result<Vec<Profile>, anyhow::Error> {
let contents = fs::read_to_string(&filename)
.map_err(|err| anyhow!("Error reading file {filename}: {err}"))?;

let credential: Credential =
toml::from_str(&contents).map_err(|err| anyhow!("Issue with the format of the TOML file {filename}: {err}"))?;
let credential: Credential = toml::from_str(&contents)
.map_err(|err| anyhow!("Issue with the format of the TOML file {filename}: {err}"))?;

Ok(credential.profile)
}
24 changes: 24 additions & 0 deletions tembo-cli/src/cli/tembo_config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use controller::app_service::types::{AppService, EnvVar};
use k8s_openapi::api::core::v1::ResourceRequirements;
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
use toml::Value;
Expand Down Expand Up @@ -29,10 +31,32 @@ pub struct InstanceSettings {
default = "default_extensions"
)]
pub extensions: Option<HashMap<String, Extension>>,
pub app_services: Option<HashMap<String, AppType>>,
pub extra_domains_rw: Option<Vec<String>>,
pub ip_allow_list: Option<Vec<String>>,
}

#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum AppType {
#[serde(rename = "restapi")]
RestAPI(Option<AppConfig>),
#[serde(rename = "http")]
HTTP(Option<AppConfig>),
#[serde(rename = "mq-api")]
MQ(Option<AppConfig>),
#[serde(rename = "embeddings")]
Embeddings(Option<AppConfig>),
#[serde(rename = "custom")]
Custom(AppService),
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct AppConfig {
pub env: Option<Vec<EnvVar>>,
pub resources: Option<ResourceRequirements>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct OverlayInstanceSettings {
pub cpu: Option<String>,
Expand Down
1 change: 1 addition & 0 deletions tembo-cli/src/cmd/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ fn merge_settings(base: &InstanceSettings, overlay: OverlayInstanceSettings) ->
.postgres_configurations
.or_else(|| base.postgres_configurations.clone()),
extensions: overlay.extensions.or_else(|| base.extensions.clone()),
app_services: None,
extra_domains_rw: overlay
.extra_domains_rw
.or_else(|| base.extra_domains_rw.clone()),
Expand Down
22 changes: 22 additions & 0 deletions tembo-cli/tembo/docker-compose.yml.template
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ services:
- "traefik.tcp.routers.{{instance.instance_name}}.entrypoints=postgresql"
- "traefik.tcp.routers.{{instance.instance_name}}.tls.passthrough=true"
- "traefik.tcp.services.{{instance.instance_name}}.loadbalancer.server.port=5432"

{% if instance.app_services.restapi %}
{{instance.instance_name}}-postgrest:
image: postgrest/postgrest:v10.0.0
environment:
PGRST_DB_URI: "postgresql://postgres:postgres@{{instance.instance_name}}:5432/postgres"
PGRST_DB_SCHEMA: "public, graphql"
PGRST_DB_ANON_ROLE: "postgres"
PGRST_LOG_LEVEL: "info"
networks:
- tembo
labels:
- "traefik.enable=true"
# The settings here depends on the app service settings
- "traefik.http.routers.{{instance.instance_name}}-postgrest.rule=Host(`{{instance.instance_name}}.local.tembo.io`)"
# && (PathPrefix(`/rest/v1`) || PathPrefix(`/graphql/v1`))"
# in cloud, this is websecure instead of just web
- "traefik.http.routers.{{instance.instance_name}}-postgrest.entrypoints=web"
- "traefik.http.services.{{instance.instance_name}}-postgrest.loadbalancer.server.port=3000"
# - "traefik.http.middlewares.postgrest-stripprefix.stripprefix.prefixes=/rest/v1,/graphql/v1"
# - "traefik.http.routers.postgrest.middlewares=postgrest-stripprefix"
{% endif %}
{% endfor %}

traefik:
Expand Down
62 changes: 58 additions & 4 deletions tembo-cli/tests/integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use assert_cmd::prelude::*; // Add methods on commands

use colorful::core::StrMarker;
use curl::easy::Easy;
use predicates::prelude::*;
use sqlx::postgres::PgConnectOptions;
use std::env;
Expand Down Expand Up @@ -115,6 +116,9 @@ async fn data_warehouse() -> Result<(), Box<dyn Error>> {

#[tokio::test]
async fn multiple_instances() -> Result<(), Box<dyn Error>> {
let instance1_name = "instance-1";
let instance2_name = "instance-2";

let root_dir = env!("CARGO_MANIFEST_DIR");
let test_dir = PathBuf::from(root_dir)
.join("examples")
Expand Down Expand Up @@ -144,17 +148,45 @@ async fn multiple_instances() -> Result<(), Box<dyn Error>> {
sleep(Duration::from_secs(5));

// check can connect
assert_can_connect("instance-1".to_string()).await?;
assert_can_connect("instance-2".to_string()).await?;
assert_can_connect(instance1_name.to_string()).await?;
assert_can_connect(instance2_name.to_string()).await?;

execute_sql(
instance2_name.to_string(),
"create table public.todos (id serial primary key,
done boolean not null default false,
task text not null,
due timestamptz
);"
.to_string(),
)
.await?;

execute_sql(
instance2_name.to_string(),
"insert into public.todos (task) values
('finish tutorial 0'), ('pat self on back');"
.to_string(),
)
.await?;

let mut easy = Easy::new();
easy.url(&format!(
"http://{}.local.tembo.io:8000/todos",
instance2_name.to_string()
))
.unwrap();
easy.perform().unwrap();
assert_eq!(easy.response_code().unwrap(), 200);

// tembo delete
let mut cmd = Command::cargo_bin(CARGO_BIN)?;
cmd.arg("delete");
let _ = cmd.ok();

// check can't connect
assert!(assert_can_connect("instance-1".to_str()).await.is_err());
assert!(assert_can_connect("instance-2".to_str()).await.is_err());
assert!(assert_can_connect(instance1_name.to_str()).await.is_err());
assert!(assert_can_connect(instance2_name.to_str()).await.is_err());

Ok(())
}
Expand Down Expand Up @@ -182,6 +214,28 @@ async fn get_output_from_sql(instance_name: String, sql: String) -> Result<Strin
Ok(result.0.to_string())
}

async fn execute_sql(instance_name: String, sql: String) -> Result<(), Box<dyn Error>> {
// Configure SQLx connection options
let connect_options = PgConnectOptions::new()
.username("postgres")
.password("postgres")
.host(&format!("{}.local.tembo.io", instance_name))
.database("postgres");

// Connect to the database
let pool = sqlx::PgPool::connect_with(connect_options).await?;

// Simple query
sqlx::query(&sql).fetch_optional(&pool).await?;

println!(
"Successfully connected to the database: {}",
&format!("{}.local.tembo.io", instance_name)
);

Ok(())
}

async fn assert_can_connect(instance_name: String) -> Result<(), Box<dyn Error>> {
let result: String = get_output_from_sql(instance_name, "SELECT 1".to_string()).await?;
assert!(result.contains('1'), "Query did not return 1");
Expand Down

0 comments on commit 5b861f2

Please sign in to comment.