mahout 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mahout
4
+ module Setup
5
+ class Postgres
6
+ def call(runner, config, tuned)
7
+ stop_existing(runner, config)
8
+ init_cluster(runner, config)
9
+ render_config(runner, config, tuned)
10
+ start_postgres(runner, config)
11
+ create_tablespace(runner, config)
12
+ create_database(runner, config)
13
+ create_user(runner, config)
14
+ set_connection_limit(runner, config, tuned)
15
+ end
16
+
17
+ private
18
+
19
+ def stop_existing(runner, config)
20
+ runner.run("systemctl stop postgresql", allow_failure: true)
21
+ end
22
+
23
+ def init_cluster(runner, config)
24
+ result = runner.run("test -f #{config.pg_data_dir}/PG_VERSION", allow_failure: true)
25
+ return if result.success?
26
+
27
+ runner.run("pg_dropcluster --stop #{config.pg_version} main", allow_failure: true)
28
+ runner.run("find #{config.data_mount}/tablespace -mindepth 1 -delete", allow_failure: true)
29
+
30
+ waldir_flag = config.pg_wal_dir ? "--waldir=#{config.pg_wal_dir}" : ""
31
+ runner.run(
32
+ "pg_createcluster #{config.pg_version} main " \
33
+ "--datadir=#{config.pg_data_dir} " \
34
+ "-- --auth-local=peer --auth-host=scram-sha-256 #{waldir_flag}"
35
+ )
36
+ end
37
+
38
+ def render_config(runner, config, tuned)
39
+ tuned ||= {}
40
+ extensions = ExtensionRegistry.resolve(config.extensions, pg_version: config.pg_version)
41
+ preload = ExtensionRegistry.preload_libraries(extensions)
42
+ preload << "pg_prewarm" unless preload.include?("pg_prewarm")
43
+
44
+ runner.render_and_upload(
45
+ "postgresql.conf.erb",
46
+ "/etc/postgresql/#{config.pg_version}/main/postgresql.conf",
47
+ locals: {
48
+ config: config,
49
+ tuned: tuned,
50
+ preload_libraries: preload
51
+ },
52
+ owner: "postgres:postgres"
53
+ )
54
+
55
+ runner.run("mkdir -p /etc/postgresql/#{config.pg_version}/main/conf.d")
56
+ runner.run("chown postgres:postgres /etc/postgresql/#{config.pg_version}/main/conf.d")
57
+ end
58
+
59
+ def start_postgres(runner, config)
60
+ runner.run("systemctl enable postgresql")
61
+ runner.run("systemctl start postgresql", allow_failure: true)
62
+ sleep(2) unless runner.dry_run?
63
+
64
+ ready = runner.run("pg_isready --timeout=10", allow_failure: true)
65
+ return if ready.success?
66
+
67
+ pg_log = runner.run("tail -10 /var/log/postgresql/postgresql-#{config.pg_version}-main.log", allow_failure: true)
68
+
69
+ if pg_log.stdout.include?("huge pages") || pg_log.stdout.include?("Cannot allocate memory")
70
+ $stdout.puts("postgres failed to start with huge_pages = on, falling back to huge_pages = try")
71
+ conf_path = "/etc/postgresql/#{config.pg_version}/main/postgresql.conf"
72
+ runner.run("sed -i 's/huge_pages = on/huge_pages = try/' #{conf_path}")
73
+ runner.run("systemctl restart postgresql", allow_failure: true)
74
+ sleep(2) unless runner.dry_run?
75
+ ready = runner.run("pg_isready --timeout=30", allow_failure: true)
76
+ raise RemoteError, "postgresql failed to start even with huge_pages = try\n#{pg_log.stdout}" unless ready.success?
77
+ else
78
+ raise RemoteError, "postgresql failed to start\n#{pg_log.stdout}"
79
+ end
80
+ end
81
+
82
+ def create_tablespace(runner, config)
83
+ result = runner.run(
84
+ "sudo -u postgres psql -tAc \"SELECT 1 FROM pg_tablespace WHERE spcname = 'mahout_data'\"",
85
+ sudo: false, allow_failure: true
86
+ )
87
+ return if result.stdout.strip == "1"
88
+
89
+ runner.run("find #{config.data_mount}/tablespace -mindepth 1 -delete", allow_failure: true)
90
+ runner.run("mkdir -p #{config.data_mount}/tablespace")
91
+ runner.run("chown postgres:postgres #{config.data_mount}/tablespace")
92
+ runner.run(
93
+ "sudo -u postgres psql -c \"CREATE TABLESPACE mahout_data LOCATION '#{config.data_mount}/tablespace'\"",
94
+ sudo: false
95
+ )
96
+ end
97
+
98
+ def create_database(runner, config)
99
+ runner.run(
100
+ "sudo -u postgres psql -tc " \
101
+ "\"SELECT 1 FROM pg_database WHERE datname = '#{config.pg_database}'\" | grep -q 1 || " \
102
+ "sudo -u postgres createdb #{config.pg_database} --tablespace=mahout_data",
103
+ sudo: false
104
+ )
105
+ end
106
+
107
+ def create_user(runner, config)
108
+ password = config.secret("pg_password")
109
+
110
+ result = runner.run(
111
+ "sudo -u postgres psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname = '#{config.pg_username}'\"",
112
+ sudo: false, allow_failure: true
113
+ )
114
+
115
+ unless result.stdout.strip == "1"
116
+ escaped_password = password.to_s.gsub("'", "''")
117
+ sql = "CREATE USER #{config.pg_username} WITH PASSWORD '#{escaped_password}';"
118
+ runner.upload(sql, "/tmp/mahout-create-user.sql", mode: "0600", owner: "postgres:postgres")
119
+ runner.run("sudo -u postgres psql -f /tmp/mahout-create-user.sql", sudo: false)
120
+ runner.run("rm -f /tmp/mahout-create-user.sql")
121
+ end
122
+
123
+ runner.run(
124
+ "sudo -u postgres psql -c " \
125
+ "\"GRANT ALL PRIVILEGES ON DATABASE #{config.pg_database} TO #{config.pg_username}\"",
126
+ sudo: false
127
+ )
128
+ end
129
+
130
+ def set_connection_limit(runner, config, tuned)
131
+ max_conn = tuned&.dig(:max_connections) || 200
132
+ app_limit = (max_conn * 0.8).to_i
133
+ runner.run(
134
+ "sudo -u postgres psql -c \"ALTER USER #{config.pg_username} CONNECTION LIMIT #{app_limit}\"",
135
+ sudo: false
136
+ )
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mahout
4
+ module Setup
5
+ class Ssl
6
+ def call(runner, config, _tuned)
7
+ cert_dir = "/etc/postgresql/#{config.pg_version}/main"
8
+ pg_conf = "#{cert_dir}/postgresql.conf"
9
+
10
+ runner.run(
11
+ "openssl req -new -x509 -days 3650 -nodes " \
12
+ "-out #{cert_dir}/server.crt " \
13
+ "-keyout #{cert_dir}/server.key " \
14
+ "-subj /CN=#{config.instance_name}"
15
+ )
16
+ runner.run("chmod 0600 #{cert_dir}/server.key")
17
+ runner.run("chown postgres:postgres #{cert_dir}/server.crt #{cert_dir}/server.key")
18
+
19
+ ssl_conf = <<~CONF
20
+ ssl = on
21
+ ssl_cert_file = '#{cert_dir}/server.crt'
22
+ ssl_key_file = '#{cert_dir}/server.key'
23
+ CONF
24
+ runner.upload(ssl_conf, "/etc/postgresql/#{config.pg_version}/main/conf.d/ssl.conf", owner: "postgres:postgres")
25
+ runner.run("systemctl reload postgresql")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mahout
4
+ module Setup
5
+ class Systemd
6
+ def call(runner, config, _tuned)
7
+ if config.pgbackrest_enabled?
8
+ install_backup_units(runner, config)
9
+ install_verify_timer(runner, config)
10
+ install_wal_check(runner, config)
11
+ end
12
+ install_health_check(runner, config)
13
+ install_oom_override(runner, config)
14
+ install_stats_reset_timer(runner)
15
+ runner.run("systemctl daemon-reload")
16
+ enable_timers(runner, config)
17
+ end
18
+
19
+ private
20
+
21
+ def install_backup_units(runner, config)
22
+ stanza = config.pgbackrest_stanza
23
+
24
+ %w[full diff incr].each do |type|
25
+ service = <<~UNIT
26
+ [Unit]
27
+ Description=pgBackRest #{type} backup
28
+ After=postgresql.service
29
+
30
+ [Service]
31
+ Type=oneshot
32
+ User=postgres
33
+ ExecStart=/usr/bin/pgbackrest --stanza=#{stanza} --type=#{type} backup
34
+ UNIT
35
+
36
+ calendar = case type
37
+ when "full" then config.pgbackrest_schedule_full
38
+ when "diff" then config.pgbackrest_schedule_diff
39
+ when "incr" then config.pgbackrest_schedule_incr
40
+ end
41
+
42
+ timer = <<~UNIT
43
+ [Unit]
44
+ Description=pgBackRest #{type} backup timer
45
+
46
+ [Timer]
47
+ OnCalendar=#{calendar}
48
+ Persistent=true
49
+
50
+ [Install]
51
+ WantedBy=timers.target
52
+ UNIT
53
+
54
+ runner.upload(service, "/etc/systemd/system/pgbackrest-#{type}-backup.service")
55
+ runner.upload(timer, "/etc/systemd/system/pgbackrest-#{type}-backup.timer")
56
+ end
57
+ end
58
+
59
+ def install_verify_timer(runner, config)
60
+ stanza = config.pgbackrest_stanza
61
+
62
+ service = <<~UNIT
63
+ [Unit]
64
+ Description=pgBackRest verify
65
+ After=postgresql.service
66
+
67
+ [Service]
68
+ Type=oneshot
69
+ User=postgres
70
+ ExecStart=/usr/bin/pgbackrest --stanza=#{stanza} verify
71
+ UNIT
72
+
73
+ timer = <<~UNIT
74
+ [Unit]
75
+ Description=pgBackRest weekly verify timer
76
+
77
+ [Timer]
78
+ OnCalendar=Sun *-*-* 04:00:00
79
+ Persistent=true
80
+
81
+ [Install]
82
+ WantedBy=timers.target
83
+ UNIT
84
+
85
+ runner.upload(service, "/etc/systemd/system/pgbackrest-verify.service")
86
+ runner.upload(timer, "/etc/systemd/system/pgbackrest-verify.timer")
87
+ end
88
+
89
+ def install_wal_check(runner, config)
90
+ stanza = config.pgbackrest_stanza
91
+
92
+ service = <<~UNIT
93
+ [Unit]
94
+ Description=pgBackRest WAL archive check
95
+ After=postgresql.service
96
+
97
+ [Service]
98
+ Type=oneshot
99
+ User=postgres
100
+ ExecStart=/usr/bin/pgbackrest --stanza=#{stanza} check
101
+ UNIT
102
+
103
+ timer = <<~UNIT
104
+ [Unit]
105
+ Description=pgBackRest WAL archive check timer
106
+
107
+ [Timer]
108
+ OnCalendar=*-*-* *:00/5:00
109
+ Persistent=true
110
+
111
+ [Install]
112
+ WantedBy=timers.target
113
+ UNIT
114
+
115
+ runner.upload(service, "/etc/systemd/system/pgbackrest-wal-check.service")
116
+ runner.upload(timer, "/etc/systemd/system/pgbackrest-wal-check.timer")
117
+ end
118
+
119
+ def install_health_check(runner, config)
120
+ service = <<~UNIT
121
+ [Unit]
122
+ Description=mahout health check
123
+ After=postgresql.service
124
+
125
+ [Service]
126
+ Type=oneshot
127
+ ExecStart=/bin/bash -c 'pg_isready -q && echo ok > /var/log/mahout/health.status || echo fail > /var/log/mahout/health.status'
128
+ UNIT
129
+
130
+ timer = <<~UNIT
131
+ [Unit]
132
+ Description=mahout health check timer
133
+
134
+ [Timer]
135
+ OnCalendar=*-*-* *:*:00
136
+ Persistent=true
137
+
138
+ [Install]
139
+ WantedBy=timers.target
140
+ UNIT
141
+
142
+ runner.run("mkdir -p /var/log/mahout")
143
+ runner.upload(service, "/etc/systemd/system/pg-health-check.service")
144
+ runner.upload(timer, "/etc/systemd/system/pg-health-check.timer")
145
+ end
146
+
147
+ def install_oom_override(runner, config)
148
+ runner.run("mkdir -p /etc/systemd/system/postgresql.service.d")
149
+ dropin = <<~UNIT
150
+ [Service]
151
+ OOMScoreAdjust=-1000
152
+ UNIT
153
+ runner.upload(dropin, "/etc/systemd/system/postgresql.service.d/oom.conf")
154
+ end
155
+
156
+ def install_stats_reset_timer(runner)
157
+ service = <<~UNIT
158
+ [Unit]
159
+ Description=Reset pg_stat_statements
160
+ After=postgresql.service
161
+
162
+ [Service]
163
+ Type=oneshot
164
+ ExecStart=/usr/bin/sudo -u postgres psql -c "SELECT pg_stat_statements_reset()"
165
+ UNIT
166
+
167
+ timer = <<~UNIT
168
+ [Unit]
169
+ Description=Weekly pg_stat_statements reset
170
+
171
+ [Timer]
172
+ OnCalendar=Mon *-*-* 03:00:00
173
+ Persistent=true
174
+
175
+ [Install]
176
+ WantedBy=timers.target
177
+ UNIT
178
+
179
+ runner.upload(service, "/etc/systemd/system/pg-stats-reset.service")
180
+ runner.upload(timer, "/etc/systemd/system/pg-stats-reset.timer")
181
+ end
182
+
183
+ def enable_timers(runner, config)
184
+ timers = %w[
185
+ pg-health-check.timer
186
+ pg-stats-reset.timer
187
+ ]
188
+
189
+ if config.pgbackrest_enabled?
190
+ timers.unshift(
191
+ "pgbackrest-full-backup.timer",
192
+ "pgbackrest-diff-backup.timer",
193
+ "pgbackrest-incr-backup.timer",
194
+ "pgbackrest-verify.timer",
195
+ "pgbackrest-wal-check.timer"
196
+ )
197
+ end
198
+
199
+ timers.each do |timer|
200
+ runner.run("systemctl enable --now #{timer}")
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mahout
4
+ class Status
5
+ def initialize(runner:, config:)
6
+ @runner = runner
7
+ @config = config
8
+ end
9
+
10
+ def call
11
+ show_pg_info
12
+ show_disk_usage
13
+ show_connections
14
+ show_backup_info if @config.pgbackrest_enabled?
15
+ show_pgbouncer_stats
16
+ show_extensions
17
+ show_timer_status
18
+ end
19
+
20
+ private
21
+
22
+ def show_pg_info
23
+ result = @runner.run(
24
+ "sudo -u postgres psql -tAc \"SELECT version()\"",
25
+ sudo: false, allow_failure: true
26
+ )
27
+ $stdout.puts("postgresql: #{result.stdout.strip.split(" ").first(2).join(" ")}") if result.success?
28
+
29
+ result = @runner.run(
30
+ "sudo -u postgres psql -tAc \"SELECT now() - pg_postmaster_start_time()\"",
31
+ sudo: false, allow_failure: true
32
+ )
33
+ $stdout.puts("uptime: #{result.stdout.strip}") if result.success?
34
+ end
35
+
36
+ def show_disk_usage
37
+ [@config.data_mount, @config.wal_mount].compact.each do |mount|
38
+ result = @runner.run("df -h --output=size,used,avail,pcent #{mount} | tail -1", allow_failure: true)
39
+ $stdout.puts("disk #{mount}: #{result.stdout.strip}") if result.success?
40
+ end
41
+ end
42
+
43
+ def show_connections
44
+ result = @runner.run(
45
+ "sudo -u postgres psql -tAc \"SELECT count(*) || '/' || current_setting('max_connections') FROM pg_stat_activity\"",
46
+ sudo: false, allow_failure: true
47
+ )
48
+ $stdout.puts("connections: #{result.stdout.strip}") if result.success?
49
+ end
50
+
51
+ def show_backup_info
52
+ result = @runner.run(
53
+ "sudo -u postgres pgbackrest --stanza=#{@config.pgbackrest_stanza} info --output json",
54
+ sudo: false, allow_failure: true
55
+ )
56
+ return unless result.success? && !result.stdout.strip.empty?
57
+
58
+ info = JSON.parse(result.stdout) rescue return
59
+ backups = info.dig(0, "backup")
60
+ return $stdout.puts("backups: none") if backups.nil? || backups.empty?
61
+
62
+ latest = backups.last
63
+ $stdout.puts("last backup: #{latest["type"]} at #{Time.at(latest.dig("timestamp", "stop")).utc}")
64
+ $stdout.puts("total backups: #{backups.length}")
65
+ end
66
+
67
+ def show_pgbouncer_stats
68
+ result = @runner.run(
69
+ "psql -h 127.0.0.1 -p #{@config.pgbouncer_port} -U #{@config.pg_username} pgbouncer -tAc 'SHOW POOLS' 2>&1",
70
+ sudo: false, allow_failure: true
71
+ )
72
+ if result.success?
73
+ $stdout.puts("pgbouncer: active")
74
+ else
75
+ $stdout.puts("pgbouncer: not responding")
76
+ end
77
+ end
78
+
79
+ def show_extensions
80
+ result = @runner.run(
81
+ "sudo -u postgres psql -d #{@config.pg_database} -tAc \"SELECT extname || ' ' || extversion FROM pg_extension ORDER BY extname\"",
82
+ sudo: false, allow_failure: true
83
+ )
84
+ return unless result.success?
85
+
86
+ exts = result.stdout.strip.split("\n").reject(&:empty?)
87
+ $stdout.puts("extensions: #{exts.join(", ")}")
88
+ end
89
+
90
+ def show_timer_status
91
+ result = @runner.run("systemctl list-timers --no-pager pgbackrest-* pg-* 2>/dev/null | head -10", allow_failure: true)
92
+ return unless result.success? && !result.stdout.strip.empty?
93
+
94
+ $stdout.puts("")
95
+ $stdout.puts("timers:")
96
+ $stdout.puts(result.stdout.strip)
97
+ end
98
+ end
99
+ end