LCOV - code coverage report
Current view: top level - compute_tools/src - config.rs (source / functions) Coverage Total Hit
Test: 6df3fc19ec669bcfbbf9aba41d1338898d24eaa0.info Lines: 13.0 % 138 18
Test Date: 2025-03-12 18:28:53 Functions: 20.0 % 5 1

            Line data    Source code
       1              : use anyhow::Result;
       2              : use std::fmt::Write as FmtWrite;
       3              : use std::fs::{File, OpenOptions};
       4              : use std::io;
       5              : use std::io::Write;
       6              : use std::io::prelude::*;
       7              : use std::path::Path;
       8              : 
       9              : use compute_api::spec::{ComputeAudit, ComputeMode, ComputeSpec, GenericOption};
      10              : 
      11              : use crate::pg_helpers::{
      12              :     GenericOptionExt, GenericOptionsSearch, PgOptionsSerialize, escape_conf_value,
      13              : };
      14              : 
      15              : /// Check that `line` is inside a text file and put it there if it is not.
      16              : /// Create file if it doesn't exist.
      17            3 : pub fn line_in_file(path: &Path, line: &str) -> Result<bool> {
      18            3 :     let mut file = OpenOptions::new()
      19            3 :         .read(true)
      20            3 :         .write(true)
      21            3 :         .create(true)
      22            3 :         .append(false)
      23            3 :         .truncate(false)
      24            3 :         .open(path)?;
      25            3 :     let buf = io::BufReader::new(&file);
      26            3 :     let mut count: usize = 0;
      27              : 
      28            5 :     for l in buf.lines() {
      29            5 :         if l? == line {
      30            1 :             return Ok(false);
      31            4 :         }
      32            4 :         count = 1;
      33              :     }
      34              : 
      35            2 :     write!(file, "{}{}", "\n".repeat(count), line)?;
      36            2 :     Ok(true)
      37            3 : }
      38              : 
      39              : /// Create or completely rewrite configuration file specified by `path`
      40            0 : pub fn write_postgres_conf(
      41            0 :     path: &Path,
      42            0 :     spec: &ComputeSpec,
      43            0 :     extension_server_port: u16,
      44            0 : ) -> Result<()> {
      45              :     // File::create() destroys the file content if it exists.
      46            0 :     let mut file = File::create(path)?;
      47              : 
      48              :     // Write the postgresql.conf content from the spec file as is.
      49            0 :     if let Some(conf) = &spec.cluster.postgresql_conf {
      50            0 :         writeln!(file, "{}", conf)?;
      51            0 :     }
      52              : 
      53              :     // Add options for connecting to storage
      54            0 :     writeln!(file, "# Neon storage settings")?;
      55            0 :     if let Some(s) = &spec.pageserver_connstring {
      56            0 :         writeln!(file, "neon.pageserver_connstring={}", escape_conf_value(s))?;
      57            0 :     }
      58            0 :     if let Some(stripe_size) = spec.shard_stripe_size {
      59            0 :         writeln!(file, "neon.stripe_size={stripe_size}")?;
      60            0 :     }
      61            0 :     if !spec.safekeeper_connstrings.is_empty() {
      62            0 :         let mut neon_safekeepers_value = String::new();
      63            0 :         tracing::info!(
      64            0 :             "safekeepers_connstrings is not zero, gen: {:?}",
      65              :             spec.safekeepers_generation
      66              :         );
      67              :         // If generation is given, prepend sk list with g#number:
      68            0 :         if let Some(generation) = spec.safekeepers_generation {
      69            0 :             write!(neon_safekeepers_value, "g#{}:", generation)?;
      70            0 :         }
      71            0 :         neon_safekeepers_value.push_str(&spec.safekeeper_connstrings.join(","));
      72            0 :         writeln!(
      73            0 :             file,
      74            0 :             "neon.safekeepers={}",
      75            0 :             escape_conf_value(&neon_safekeepers_value)
      76            0 :         )?;
      77            0 :     }
      78            0 :     if let Some(s) = &spec.tenant_id {
      79            0 :         writeln!(file, "neon.tenant_id={}", escape_conf_value(&s.to_string()))?;
      80            0 :     }
      81            0 :     if let Some(s) = &spec.timeline_id {
      82            0 :         writeln!(
      83            0 :             file,
      84            0 :             "neon.timeline_id={}",
      85            0 :             escape_conf_value(&s.to_string())
      86            0 :         )?;
      87            0 :     }
      88              : 
      89              :     // Locales
      90            0 :     if cfg!(target_os = "macos") {
      91            0 :         writeln!(file, "lc_messages='C'")?;
      92            0 :         writeln!(file, "lc_monetary='C'")?;
      93            0 :         writeln!(file, "lc_time='C'")?;
      94            0 :         writeln!(file, "lc_numeric='C'")?;
      95              :     } else {
      96            0 :         writeln!(file, "lc_messages='C.UTF-8'")?;
      97            0 :         writeln!(file, "lc_monetary='C.UTF-8'")?;
      98            0 :         writeln!(file, "lc_time='C.UTF-8'")?;
      99            0 :         writeln!(file, "lc_numeric='C.UTF-8'")?;
     100              :     }
     101              : 
     102            0 :     match spec.mode {
     103            0 :         ComputeMode::Primary => {}
     104            0 :         ComputeMode::Static(lsn) => {
     105            0 :             // hot_standby is 'on' by default, but let's be explicit
     106            0 :             writeln!(file, "hot_standby=on")?;
     107            0 :             writeln!(file, "recovery_target_lsn='{lsn}'")?;
     108              :         }
     109              :         ComputeMode::Replica => {
     110              :             // hot_standby is 'on' by default, but let's be explicit
     111            0 :             writeln!(file, "hot_standby=on")?;
     112              :         }
     113              :     }
     114              : 
     115            0 :     if cfg!(target_os = "linux") {
     116              :         // Check /proc/sys/vm/overcommit_memory -- if it equals 2 (i.e. linux memory overcommit is
     117              :         // disabled), then the control plane has enabled swap and we should set
     118              :         // dynamic_shared_memory_type = 'mmap'.
     119              :         //
     120              :         // This is (maybe?) temporary - for more, see https://github.com/neondatabase/cloud/issues/12047.
     121            0 :         let overcommit_memory_contents = std::fs::read_to_string("/proc/sys/vm/overcommit_memory")
     122            0 :             // ignore any errors - they may be expected to occur under certain situations (e.g. when
     123            0 :             // not running in Linux).
     124            0 :             .unwrap_or_else(|_| String::new());
     125            0 :         if overcommit_memory_contents.trim() == "2" {
     126            0 :             let opt = GenericOption {
     127            0 :                 name: "dynamic_shared_memory_type".to_owned(),
     128            0 :                 value: Some("mmap".to_owned()),
     129            0 :                 vartype: "enum".to_owned(),
     130            0 :             };
     131            0 : 
     132            0 :             writeln!(file, "{}", opt.to_pg_setting())?;
     133            0 :         }
     134            0 :     }
     135              : 
     136              :     // If there are any extra options in the 'settings' field, append those
     137            0 :     if spec.cluster.settings.is_some() {
     138            0 :         writeln!(file, "# Managed by compute_ctl: begin")?;
     139            0 :         write!(file, "{}", spec.cluster.settings.as_pg_settings())?;
     140            0 :         writeln!(file, "# Managed by compute_ctl: end")?;
     141            0 :     }
     142              : 
     143              :     // If audit logging is enabled, configure pgaudit.
     144              :     //
     145              :     // Note, that this is called after the settings from spec are written.
     146              :     // This way we always override the settings from the spec
     147              :     // and don't allow the user or the control plane admin to change them.
     148            0 :     if let ComputeAudit::Hipaa = spec.audit_log_level {
     149            0 :         writeln!(file, "# Managed by compute_ctl audit settings: begin")?;
     150              :         // This log level is very verbose
     151              :         // but this is necessary for HIPAA compliance.
     152            0 :         writeln!(file, "pgaudit.log='all'")?;
     153            0 :         writeln!(file, "pgaudit.log_parameter=on")?;
     154              :         // Disable logging of catalog queries
     155              :         // The catalog doesn't contain sensitive data, so we don't need to audit it.
     156            0 :         writeln!(file, "pgaudit.log_catalog=off")?;
     157              :         // Set log rotation to 5 minutes
     158              :         // TODO: tune this after performance testing
     159            0 :         writeln!(file, "pgaudit.log_rotation_age=5")?;
     160              : 
     161              :         // Add audit shared_preload_libraries, if they are not present.
     162              :         //
     163              :         // The caller who sets the flag is responsible for ensuring that the necessary
     164              :         // shared_preload_libraries are present in the compute image,
     165              :         // otherwise the compute start will fail.
     166            0 :         if let Some(libs) = spec.cluster.settings.find("shared_preload_libraries") {
     167            0 :             let mut extra_shared_preload_libraries = String::new();
     168            0 :             if !libs.contains("pgaudit") {
     169            0 :                 extra_shared_preload_libraries.push_str(",pgaudit");
     170            0 :             }
     171            0 :             if !libs.contains("pgauditlogtofile") {
     172            0 :                 extra_shared_preload_libraries.push_str(",pgauditlogtofile");
     173            0 :             }
     174            0 :             writeln!(
     175            0 :                 file,
     176            0 :                 "shared_preload_libraries='{}{}'",
     177            0 :                 libs, extra_shared_preload_libraries
     178            0 :             )?;
     179              :         } else {
     180              :             // Typically, this should be unreacheable,
     181              :             // because we always set at least some shared_preload_libraries in the spec
     182              :             // but let's handle it explicitly anyway.
     183            0 :             writeln!(
     184            0 :                 file,
     185            0 :                 "shared_preload_libraries='neon,pgaudit,pgauditlogtofile'"
     186            0 :             )?;
     187              :         }
     188            0 :         writeln!(file, "# Managed by compute_ctl audit settings: end")?;
     189            0 :     }
     190              : 
     191            0 :     writeln!(file, "neon.extension_server_port={}", extension_server_port)?;
     192              : 
     193            0 :     if spec.drop_subscriptions_before_start {
     194            0 :         writeln!(file, "neon.disable_logical_replication_subscribers=true")?;
     195              :     } else {
     196              :         // be explicit about the default value
     197            0 :         writeln!(file, "neon.disable_logical_replication_subscribers=false")?;
     198              :     }
     199              : 
     200              :     // This is essential to keep this line at the end of the file,
     201              :     // because it is intended to override any settings above.
     202            0 :     writeln!(file, "include_if_exists = 'compute_ctl_temp_override.conf'")?;
     203              : 
     204            0 :     Ok(())
     205            0 : }
     206              : 
     207            0 : pub fn with_compute_ctl_tmp_override<F>(pgdata_path: &Path, options: &str, exec: F) -> Result<()>
     208            0 : where
     209            0 :     F: FnOnce() -> Result<()>,
     210            0 : {
     211            0 :     let path = pgdata_path.join("compute_ctl_temp_override.conf");
     212            0 :     let mut file = File::create(path)?;
     213            0 :     write!(file, "{}", options)?;
     214              : 
     215            0 :     let res = exec();
     216            0 : 
     217            0 :     file.set_len(0)?;
     218              : 
     219            0 :     res
     220            0 : }
        

Generated by: LCOV version 2.1-beta