summary refs log tree commit diff
path: root/modules/services/databases/postgresql.nix
blob: 56e98e4a3f999adb527763f690456e5ca5d0cb50 (plain) (blame)
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
{ config, pkgs, ... }:

with pkgs.lib;

let

  cfg = config.services.postgresql;

  # see description of extraPlugins
  postgresqlAndPlugins = pg:
    if cfg.extraPlugins == [] then pg
    else pkgs.buildEnv {
      name = "postgresql-and-plugins";
      paths = [ pg ] ++ cfg.extraPlugins;
      postBuild =
        ''
          mkdir -p $out/bin
          rm $out/bin/{pg_config,postgres,pg_ctl}
          cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl}
        '';
    };

  postgresql = postgresqlAndPlugins pkgs.postgresql;

  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} postgres";

  flags = optional cfg.enableTCPIP "-i";

  # The main PostgreSQL configuration file.
  configFile = pkgs.writeText "postgresql.conf"
    ''
      hba_file = '${pkgs.writeText "pg_hba.conf" cfg.authentication}'
      ident_file = '${pkgs.writeText "pg_ident.conf" cfg.identMap}'
      log_destination = 'syslog'
    '';  

in

{

  ###### interface
  
  options = {
  
    services.postgresql = {
    
      enable = mkOption {
        default = false;
        description = ''
          Whether to run PostgreSQL.
        '';
      };
      
      port = mkOption {
        default = "5432";
        description = ''
          Port for PostgreSQL.
        '';
      };
      
      logDir = mkOption {
        default = "/var/log/postgresql";
        description = ''
          Log directory for PostgreSQL.
        '';
      };
      
      dataDir = mkOption {
        default = "/var/db/postgresql";
        description = ''
          Data directory for PostgreSQL.
        '';
      };
      
      authentication = mkOption {
        default = ''
          # Generated file; do not edit!
          local all mediawiki        ident mediawiki-users
          local all all              ident sameuser
          host  all all 127.0.0.1/32 md5
          host  all all ::1/128      md5
        '';
        description = ''
          Defines how users authenticate themselves to the server.
        '';
      };
      
      identMap = mkOption {
        default = "";
        description = ''
          Defines the mapping from system users to database users.
        '';
      };
      
      authMethod = mkOption {
        default = " ident sameuser ";
        description = ''
          How to authorize users. 
          Note: ident needs absolute trust to all allowed client hosts.
        '';
      };
      
      enableTCPIP = mkOption {
        default = false;
        description = ''
          Whether to run PostgreSQL with -i flag to enable TCP/IP connections.
        '';
      };

      extraPlugins = mkOption {
        default = [];
        example = "pkgs.postgis"; # of course don't use a string here!
        description = ''
          When this list contains elements a new store path is created.
          PostgreSQL and the elments are symlinked into it. Then pg_config,
          postgres and pc_ctl are copied to make them use the new
          $out/lib directory as pkglibdir. This makes it possible to use postgis
          without patching the .sql files which reference $libdir/postgis-1.5.
        '';
        # Note: the duplication of executables is about 4MB size.
        # So a nicer solution was patching postgresql to allow setting the
        # libdir explicitely.
      };
      
    };

  };


  ###### implementation
  
  config = mkIf config.services.postgresql.enable {

    users.extraUsers = singleton
      { name = "postgres";
        description = "PostgreSQL server user";
      };

    users.extraGroups = singleton
      { name = "postgres"; };

    environment.systemPackages = [postgresql];

    # !!! This should be be in the mediawiki module, obviously.
    services.postgresql.identMap =
      ''
        mediawiki-users root   mediawiki
        mediawiki-users wwwrun mediawiki
      '';

    jobs.postgresql =
      { description = "PostgreSQL server";

        startOn = "started network-interfaces and filesystem";

        environment =
          { TZ = config.time.timeZone;
            PGDATA = cfg.dataDir;
          };

        preStart =
          ''
            # Initialise the database.
            if ! test -e ${cfg.dataDir}; then
                mkdir -m 0700 -p ${cfg.dataDir}
                chown -R postgres ${cfg.dataDir}
                ${run} -c '${postgresql}/bin/initdb -U root'
                rm -f ${cfg.dataDir}/*.conf
            fi

            ln -sfn ${configFile} ${cfg.dataDir}/postgresql.conf

            # We'd like to use the `-w' flag here to wait until the
            # database is up, but it requires a `postgres' user to
            # exist.  And we can't call `createuser' before the
            # database is running.
            ${run} -c '${postgresql}/bin/pg_ctl start -o "${toString flags}"'

            # So wait until the server is up.  `pg_ctl status' doesn't
            # work (apparently, it just checks whether the server is
            # running), so try to connect with psql.
            while ! ${postgresql}/bin/psql postgres -c ""; do
                sleep 1
            done
          ''; # */

        postStop =
          ''
            ${run} -c '${postgresql}/bin/pg_ctl stop -m fast'
          '';
      };

  };
  
}