Conversation
There was a problem hiding this comment.
This PR is being reviewed by Cursor Bugbot
Details
Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
| .danger_accept_invalid_certs(true) | ||
| .tls_built_in_root_certs(false) | ||
| .build() | ||
| .expect("Failed to create HTTP client"); |
There was a problem hiding this comment.
Bug: HTTP client recreated on every discovery call
A new reqwest::Client is created on every call to discover_cluster(), which is invoked repeatedly by the background refresh task. Creating HTTP clients is expensive as it involves setting up connection pools. The original design stored the client in self.client and reused it. This change loses that efficiency and could cause resource churn during frequent cluster state refreshes.
There was a problem hiding this comment.
Pull request overview
This PR migrates the Patroni cluster discovery mechanism from using a VPC proxy with custom headers to directly accessing the gateway URL. The change makes the implementation more generic and removes the dependency on the cloud-api living in the same compose environment as dstack-service.
Key Changes:
- Added
gateway_subdomainconfiguration parameter to construct gateway URLs dynamically - Updated URL format from
http://dstack-service/clusterwith custom headers tohttps://{postgres_app_id}-8008.{gateway_subdomain}/cluster - Modified HTTP client configuration to handle self-signed certificates
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| crates/config/src/types.rs | Added gateway_subdomain field to DatabaseConfig with corresponding GATEWAY_SUBDOMAIN environment variable |
| crates/database/src/patroni_discovery.rs | Updated PatroniDiscovery to accept gateway subdomain, construct gateway URLs, and configure TLS for self-signed certificates |
| crates/database/src/lib.rs | Passed gateway subdomain configuration to PatroniDiscovery constructor |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Allow self-signed certificates | ||
| let client = Client::builder() | ||
| .tls_info(true) | ||
| .danger_accept_invalid_certs(true) |
There was a problem hiding this comment.
Using danger_accept_invalid_certs(true) disables certificate validation, making the connection vulnerable to man-in-the-middle attacks. This undermines the security benefits of using HTTPS.
If self-signed certificates are expected in the gateway environment, consider using a proper certificate authority or certificate pinning instead. At minimum, add a comment explaining why this is necessary and document the security implications.
Alternative approaches:
- Configure the gateway with proper TLS certificates from a trusted CA
- Use
add_root_certificate()to trust specific self-signed certificates - If this is only for development/testing, guard this with a configuration flag
| client: Client::builder() | ||
| .timeout(Duration::from_secs(10)) | ||
| .build() | ||
| .expect("Failed to create HTTP client"), |
There was a problem hiding this comment.
The self.client field initialized here is no longer used after the changes in discover_cluster() method (lines 77-82), where a new client is created instead. This creates confusion about which client is being used and wastes resources.
Either remove this client initialization and the field from the struct, or update the discover_cluster() method to use self.client after configuring it with the necessary TLS settings during initialization.
| "https://{}-8008.{}/cluster", | ||
| self.postgres_app_id, self.gateway_subdomain |
There was a problem hiding this comment.
The URL format https://{app_id}-8008.{gateway_subdomain}/cluster hardcodes the port number (8008) in the URL path. This tightly couples the implementation to a specific port and makes it difficult to change the Patroni API port without code changes.
Consider making the port configurable through the DatabaseConfig or PatroniDiscovery constructor to improve flexibility.
| let client = Client::builder() | ||
| .tls_info(true) | ||
| .danger_accept_invalid_certs(true) | ||
| .tls_built_in_root_certs(false) | ||
| .build() | ||
| .expect("Failed to create HTTP client"); |
There was a problem hiding this comment.
Creating a new HTTP client on every request is inefficient and defeats the purpose of the existing self.client field. The client should be configured once during initialization in the new() method with the necessary TLS settings, then reused for all requests.
Consider updating the new() method to configure the client with TLS settings:
Self {
client: Client::builder()
.timeout(Duration::from_secs(10))
.tls_info(true)
.danger_accept_invalid_certs(true)
.tls_built_in_root_certs(false)
.build()
.expect("Failed to create HTTP client"),
// ... other fields
}Then use self.client here instead of creating a new one.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .header("Host", "vpc-server") | ||
| .send() | ||
| .await | ||
| .map_err(|e| anyhow!("Failed to connect to Patroni API: {e}"))?; |
There was a problem hiding this comment.
The error message should include the URL that failed to connect for better debugging. This is especially important now that the URL is dynamically constructed from configuration rather than being a static value.
Consider updating the error message:
.map_err(|e| anyhow!("Failed to connect to Patroni API at {}: {e}", url))?;| .map_err(|e| anyhow!("Failed to connect to Patroni API: {e}"))?; | |
| .map_err(|e| anyhow!("Failed to connect to Patroni API at {}: {e}", url))?; |
| "https://{}-8008.{}/cluster", | ||
| self.postgres_app_id, self.gateway_subdomain | ||
| ); | ||
| // Allow self-signed certificates |
There was a problem hiding this comment.
This comment is redundant since the same information is already conveyed by the HTTP client configuration at lines 55-56. The comment should be removed or updated to explain why self-signed certificates are being accepted (e.g., "Gateway uses self-signed certificates in the current deployment environment").
| // Allow self-signed certificates |
| gateway_subdomain: env::var("GATEWAY_SUBDOMAIN") | ||
| .map_err(|_| "GATEWAY_SUBDOMAIN not set".to_string())?, |
There was a problem hiding this comment.
The gateway_subdomain value is not validated after being loaded from the environment variable. Invalid values (e.g., empty string, containing invalid characters, malformed domain) will cause runtime failures when constructing the URL in PatroniDiscovery.
Consider adding validation:
gateway_subdomain: {
let subdomain = env::var("GATEWAY_SUBDOMAIN")
.map_err(|_| "GATEWAY_SUBDOMAIN not set".to_string())?;
if subdomain.trim().is_empty() {
return Err("GATEWAY_SUBDOMAIN cannot be empty".to_string());
}
subdomain
},| "https://{}-8008.{}/cluster", | ||
| self.postgres_app_id, self.gateway_subdomain | ||
| ); | ||
| // Allow self-signed certificates |
There was a problem hiding this comment.
Bug: Comment indicates self-signed cert support but not implemented
The comment at line 76 states "Allow self-signed certificates" but the reqwest::Client::builder() at lines 53-56 doesn't actually configure danger_accept_invalid_certs(true). When the code switches from HTTP to HTTPS to connect to the gateway, HTTPS requests will fail with certificate verification errors if the gateway uses self-signed certificates. The comment suggests this was intended to be handled, but the implementation is missing.
Additional Locations (1)
The previous implementation relied on cloud-api living in a compose file where dstack-service exists. This approach is more generic by relying on the gateway directly rather than using the vpc proxy.
84272e8 to
c41d344
Compare
The previous implementation relied on cloud-api living in a compose file where dstack-service exists. This approach is more generic by relying on the gateway directly rather than using the vpc proxy.
Note
Switch Patroni discovery to gateway-based URL and introduce
gateway_subdomaininDatabaseConfig, updating construction and tests.PatroniDiscoveryto build URLhttps://{primary_app_id}-8008.{gateway_subdomain}/cluster(remove VPC proxy headers anddstack-servicepath).PatroniDiscovery::newsignature to includegateway_subdomainand wire it throughDatabase::from_config.gateway_subdomaintoconfig::DatabaseConfigand requireGATEWAY_SUBDOMAINenv var infrom_env.database.gateway_subdomainin test configs acrosscrates/api/src/lib.rs,crates/api/tests/common/mod.rs, andcrates/api/tests/e2e_repositories.rs.Written by Cursor Bugbot for commit c41d344. This will update automatically on new commits. Configure here.