TLA 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 CBC 635 : pub fn line_in_file(path: &Path, line: &str) -> Result<bool> {
15 635 : let mut file = OpenOptions::new()
16 635 : .read(true)
17 635 : .write(true)
18 635 : .create(true)
19 635 : .append(false)
20 635 : .open(path)?;
21 635 : let buf = io::BufReader::new(&file);
22 635 : let mut count: usize = 0;
23 :
24 61941 : for l in buf.lines() {
25 61941 : if l? == line {
26 1 : return Ok(false);
27 61940 : }
28 61940 : count = 1;
29 : }
30 :
31 634 : write!(file, "{}{}", "\n".repeat(count), line)?;
32 634 : Ok(true)
33 635 : }
34 :
35 : /// Create or completely rewrite configuration file specified by `path`
36 641 : pub fn write_postgres_conf(
37 641 : path: &Path,
38 641 : spec: &ComputeSpec,
39 641 : extension_server_port: Option<u16>,
40 641 : ) -> Result<()> {
41 : // File::create() destroys the file content if it exists.
42 641 : let mut file = File::create(path)?;
43 :
44 : // Write the postgresql.conf content from the spec file as is.
45 641 : if let Some(conf) = &spec.cluster.postgresql_conf {
46 641 : writeln!(file, "{}", conf)?;
47 UBC 0 : }
48 :
49 : // Add options for connecting to storage
50 CBC 641 : writeln!(file, "# Neon storage settings")?;
51 641 : if let Some(s) = &spec.pageserver_connstring {
52 641 : writeln!(file, "neon.pageserver_connstring={}", escape_conf_value(s))?;
53 UBC 0 : }
54 CBC 641 : if !spec.safekeeper_connstrings.is_empty() {
55 552 : writeln!(
56 552 : file,
57 552 : "neon.safekeepers={}",
58 552 : escape_conf_value(&spec.safekeeper_connstrings.join(","))
59 552 : )?;
60 89 : }
61 641 : if let Some(s) = &spec.tenant_id {
62 641 : writeln!(file, "neon.tenant_id={}", escape_conf_value(&s.to_string()))?;
63 UBC 0 : }
64 CBC 641 : if let Some(s) = &spec.timeline_id {
65 641 : writeln!(
66 641 : file,
67 641 : "neon.timeline_id={}",
68 641 : escape_conf_value(&s.to_string())
69 641 : )?;
70 UBC 0 : }
71 :
72 CBC 641 : match spec.mode {
73 552 : ComputeMode::Primary => {}
74 88 : ComputeMode::Static(lsn) => {
75 88 : // hot_standby is 'on' by default, but let's be explicit
76 88 : writeln!(file, "hot_standby=on")?;
77 88 : writeln!(file, "recovery_target_lsn='{lsn}'")?;
78 : }
79 : ComputeMode::Replica => {
80 : // hot_standby is 'on' by default, but let's be explicit
81 1 : writeln!(file, "hot_standby=on")?;
82 : }
83 : }
84 :
85 : // If there are any extra options in the 'settings' field, append those
86 641 : if spec.cluster.settings.is_some() {
87 UBC 0 : writeln!(file, "# Managed by compute_ctl: begin")?;
88 0 : write!(file, "{}", spec.cluster.settings.as_pg_settings())?;
89 0 : writeln!(file, "# Managed by compute_ctl: end")?;
90 CBC 641 : }
91 :
92 641 : if let Some(port) = extension_server_port {
93 641 : writeln!(file, "neon.extension_server_port={}", port)?;
94 UBC 0 : }
95 :
96 CBC 641 : Ok(())
97 641 : }
|