Line data Source code
1 : use std::fs::{File, OpenOptions};
2 : use std::io;
3 : use std::io::prelude::*;
4 : use std::path::Path;
5 :
6 : use anyhow::Result;
7 :
8 : use crate::pg_helpers::escape_conf_value;
9 : use crate::pg_helpers::PgOptionsSerialize;
10 : use compute_api::spec::{ComputeMode, ComputeSpec};
11 :
12 : /// Check that `line` is inside a text file and put it there if it is not.
13 : /// Create file if it doesn't exist.
14 657 : pub fn line_in_file(path: &Path, line: &str) -> Result<bool> {
15 657 : let mut file = OpenOptions::new()
16 657 : .read(true)
17 657 : .write(true)
18 657 : .create(true)
19 657 : .append(false)
20 657 : .open(path)?;
21 657 : let buf = io::BufReader::new(&file);
22 657 : let mut count: usize = 0;
23 :
24 64097 : for l in buf.lines() {
25 64097 : if l? == line {
26 1 : return Ok(false);
27 64096 : }
28 64096 : count = 1;
29 : }
30 :
31 656 : write!(file, "{}{}", "\n".repeat(count), line)?;
32 656 : Ok(true)
33 657 : }
34 :
35 : /// Create or completely rewrite configuration file specified by `path`
36 663 : pub fn write_postgres_conf(
37 663 : path: &Path,
38 663 : spec: &ComputeSpec,
39 663 : extension_server_port: Option<u16>,
40 663 : ) -> Result<()> {
41 : // File::create() destroys the file content if it exists.
42 663 : let mut file = File::create(path)?;
43 :
44 : // Write the postgresql.conf content from the spec file as is.
45 663 : if let Some(conf) = &spec.cluster.postgresql_conf {
46 663 : writeln!(file, "{}", conf)?;
47 0 : }
48 :
49 663 : write!(file, "{}", &spec.cluster.settings.as_pg_settings())?;
50 :
51 : // Add options for connecting to storage
52 663 : writeln!(file, "# Neon storage settings")?;
53 663 : if let Some(s) = &spec.pageserver_connstring {
54 663 : writeln!(file, "neon.pageserver_connstring={}", escape_conf_value(s))?;
55 0 : }
56 663 : if !spec.safekeeper_connstrings.is_empty() {
57 574 : writeln!(
58 574 : file,
59 574 : "neon.safekeepers={}",
60 574 : escape_conf_value(&spec.safekeeper_connstrings.join(","))
61 574 : )?;
62 89 : }
63 663 : if let Some(s) = &spec.tenant_id {
64 663 : writeln!(file, "neon.tenant_id={}", escape_conf_value(&s.to_string()))?;
65 0 : }
66 663 : if let Some(s) = &spec.timeline_id {
67 663 : writeln!(
68 663 : file,
69 663 : "neon.timeline_id={}",
70 663 : escape_conf_value(&s.to_string())
71 663 : )?;
72 0 : }
73 :
74 663 : match spec.mode {
75 574 : ComputeMode::Primary => {}
76 88 : ComputeMode::Static(lsn) => {
77 88 : // hot_standby is 'on' by default, but let's be explicit
78 88 : writeln!(file, "hot_standby=on")?;
79 88 : writeln!(file, "recovery_target_lsn='{lsn}'")?;
80 : }
81 : ComputeMode::Replica => {
82 : // hot_standby is 'on' by default, but let's be explicit
83 1 : writeln!(file, "hot_standby=on")?;
84 : }
85 : }
86 :
87 : // If there are any extra options in the 'settings' field, append those
88 663 : if spec.cluster.settings.is_some() {
89 0 : writeln!(file, "# Managed by compute_ctl: begin")?;
90 0 : write!(file, "{}", spec.cluster.settings.as_pg_settings())?;
91 0 : writeln!(file, "# Managed by compute_ctl: end")?;
92 663 : }
93 :
94 663 : if let Some(port) = extension_server_port {
95 663 : writeln!(file, "neon.extension_server_port={}", port)?;
96 0 : }
97 :
98 663 : Ok(())
99 663 : }
|