summaryrefslogtreecommitdiffstats
path: root/src/csv.rs
blob: 417e8e6b13bf5d364344b16dc86b4bca210be77e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! CSV impl and reexported types

use csv;

pub use self::csv::{Reader, Writer, Result, ReaderBuilder};
use std::path::Path;
use std::io::{Read, Write};

impl<'a> super::TableSlice<'a> {
    /// Write the table to the specified writer.
    pub fn to_csv<W: Write>(&self, w: W) -> Result<Writer<W>> {
        self.to_csv_writer(Writer::from_writer(w))
    }

    /// Write the table to the specified writer.
    ///
    /// This allows for format customisation.
    pub fn to_csv_writer<W: Write>(&self,
                                mut writer: Writer<W>)
                                -> Result<Writer<W>> {
        for title in self.titles {
            writer.write_record(title.iter().map(|c| c.get_content()))?;
        }
        for row in self.rows {
            writer.write_record(row.iter().map(|c| c.get_content()))?;
        }

        writer.flush()?;
        Ok(writer)
    }
}

impl super::Table {
    /// Create a table from a CSV string
    ///
    /// For more customisability use `from_csv()`
    pub fn from_csv_string(csv_s: &str) -> Result<Self> {
        Ok(Self::from_csv(
            &mut ReaderBuilder::new()
                .has_headers(false)
                .from_reader(csv_s.as_bytes())))
    }

    /// Create a table from a CSV file
    ///
    /// For more customisability use `from_csv()`
    pub fn from_csv_file<P: AsRef<Path>>(csv_p: P) -> Result<Self> {
        Ok(Self::from_csv(
            &mut ReaderBuilder::new()
                .has_headers(false)
                .from_path(csv_p)?))
    }

    /// Create a table from a CSV reader
    pub fn from_csv<R: Read>(reader: &mut Reader<R>) -> Self {
        Self::init(reader
                        .records()
                        .map(|row| {
                                super::Row::new(row.unwrap()
                                            .into_iter()
                                            .map(|cell| super::Cell::new(&cell))
                                            .collect())
                            })
                        .collect())
    }

    
    /// Write the table to the specified writer.
    pub fn to_csv<W: Write>(&self, w: W) -> Result<Writer<W>> {
        self.as_ref().to_csv(w)
    }

    /// Write the table to the specified writer.
    ///
    /// This allows for format customisation.
    pub fn to_csv_writer<W: Write>(&self, writer: Writer<W>) -> Result<Writer<W>> {
        self.as_ref().to_csv_writer(writer)
    }
}


#[cfg(test)]
mod tests {
    use crate::{Table, Row, Cell};

    static CSV_S: &'static str = "ABC,DEFG,HIJKLMN\n\
                                foobar,bar,foo\n\
                                foobar2,bar2,foo2\n";

    fn test_table() -> Table {
        let mut table = Table::new();
        table
            .add_row(Row::new(vec![Cell::new("ABC"), Cell::new("DEFG"), Cell::new("HIJKLMN")]));
        table.add_row(Row::new(vec![Cell::new("foobar"), Cell::new("bar"), Cell::new("foo")]));
        table.add_row(Row::new(vec![Cell::new("foobar2"),
                                    Cell::new("bar2"),
                                    Cell::new("foo2")]));
        table
    }

    #[test]
    fn from() {
        assert_eq!(test_table().to_string().replace("\r\n", "\n"),
                    Table::from_csv_string(CSV_S)
                        .unwrap()
                        .to_string()
                        .replace("\r\n", "\n"));
    }

    #[test]
    fn to() {
        assert_eq!(
            String::from_utf8(
                test_table()
                    .to_csv(Vec::new())
                    .unwrap()
                    .into_inner()
                    .unwrap()
                ).unwrap(),
                CSV_S);
    }

    #[test]
    fn trans() {
        assert_eq!(
            Table::from_csv_string(
                &String::from_utf8(
                    test_table()
                        .to_csv(Vec::new())
                        .unwrap()
                        .into_inner()
                        .unwrap()
                ).unwrap()
            ).unwrap()
            .to_string()
            .replace("\r\n", "\n"),
            test_table().to_string().replace("\r\n", "\n"));
    }

    #[test]
    fn extend_table() {
        let mut table = Table::new();
        table.add_row(Row::new(vec![Cell::new("ABC"), Cell::new("DEFG"), Cell::new("HIJKLMN")]));
        table.extend(vec![vec!["A", "B", "C"]]);
        let t2 = table.clone();
        table.extend(t2.rows);
        assert_eq!(table.get_row(1).unwrap().get_cell(2).unwrap().get_content(), "C");
        assert_eq!(table.get_row(2).unwrap().get_cell(1).unwrap().get_content(), "DEFG");
    }
}
s="nt">status: 301 url: /netdata/server2/ /netdata/server2/: proxy.preserve-host: ON proxy.reverse.url: http://198.51.100.2:19999 ``` Of course you can add as many backend servers as you like. Using the above, you access Netdata on the backend servers, like this: - `http://netdata.example.com/netdata/server1/` to reach Netdata on `198.51.100.1:19999` - `http://netdata.example.com/netdata/server2/` to reach Netdata on `198.51.100.2:19999` ### Encrypt the communication between H2O and Netdata In case Netdata's web server has been [configured to use TLS](https://github.com/netdata/netdata/blob/master/src/web/server/README.md#enabling-tls-support), it is necessary to specify inside the H2O configuration that the final destination is using TLS. To do this, change the `http://` on the `proxy.reverse.url` line in your H2O configuration with `https://` ### Enable authentication Create an authentication file to enable basic authentication via H2O, this secures your Netdata dashboard. If you don't have an authentication file, you can use the following command: ```sh printf "yourusername:$(openssl passwd -apr1)" > /etc/h2o/passwords ``` And then add a basic authentication handler to each path definition: ```yaml hosts: netdata.example.com: listen: port: 80 paths: /: mruby.handler: | require "htpasswd.rb" Htpasswd.new("/etc/h2o/passwords", "netdata.example.com") proxy.preserve-host: ON proxy.reverse.url: http://127.0.0.1:19999 ``` For more information on using basic authentication with H2O, see [their official documentation](https://h2o.examp1e.net/configure/basic_auth.html). ## Limit direct access to Netdata If your H2O server is on `localhost`, you can use this to ensure external access is only possible through H2O: ``` [web] bind to = 127.0.0.1 ::1 ``` You can also use a unix domain socket. This will provide faster communication between H2O and Netdata as well: ``` [web] bind to = unix:/run/netdata/netdata.sock ``` In the H2O configuration, use a line like the following to connect to Netdata via the unix socket: ```yaml proxy.reverse.url http://[unix:/run/netdata/netdata.sock] ``` If your H2O server is not on localhost, you can set: ``` [web] bind to = * allow connections from = IP_OF_H2O_SERVER ``` *note: Netdata v1.9+ support `allow connections from`* `allow connections from` accepts [Netdata simple patterns](https://github.com/netdata/netdata/blob/master/src/libnetdata/simple_pattern/README.md) to match against the connection IP address. ## Prevent the double access.log H2O logs accesses and Netdata logs them too. You can prevent Netdata from generating its access log, by setting this in `/etc/netdata/netdata.conf`: ``` [global] access log = none ```