Merge ~ballot/charm-k8s-postgresql/+git/charm-k8s-postgresql:ballot_review into charm-k8s-postgresql:master

Proposed by Benjamin Allot
Status: Superseded
Proposed branch: ~ballot/charm-k8s-postgresql/+git/charm-k8s-postgresql:ballot_review
Merge into: charm-k8s-postgresql:master
Prerequisite: ~stub/charm-k8s-postgresql:fix-failover
Diff against target: 256 lines (+37/-72)
4 files modified
Dockerfile (+1/-1)
Makefile (+7/-16)
files/docker_entrypoint.py (+1/-0)
files/pgcharm.py (+28/-55)
Reviewer Review Type Date Requested Status
Canonical IS Reviewers Pending
PostgreSQL Charm Maintainers Pending
Review via email: mp+391211@code.launchpad.net

This proposal has been superseded by a proposal from 2020-09-23.

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

Unmerged commits

db7d9b6... by Benjamin Allot

Remove useless makefile target

Also, allow variables to be passed as environment variables

b1e1ace... by Stuart Bishop

Merge branch 'missing-test' of git+ssh://git.launchpad.net/~mthaddon/charm-k8s-postgresql/+git/charm-k8s-postgresql into integration

e2bdc6d... by Stuart Bishop

Use a hack to enable DNS resolution of pods

Pods need a stable address to each other to register with
repmgr. However, pods get a new IP address each time they
get restarted. Register a k8s Headless Service for each
pod, using a selector to ensure connections are routed
correctly, allowing us to use the DNS name of the
service to connect to PostgreSQL on the pods.

1ac0c84... by Stuart Bishop

Run repmgrd as pid 1

0632041... by Stuart Bishop

No need for a custom readyness script

db91540... by Stuart Bishop

Readyness check and make code more robust

981719e... by Stuart Bishop

Note that custom services bypass 'juju expose'

bceb86a... by Stuart Bishop

Move k8s Service creation to podspec

Requires Juju 2.8.2

9a5144d... by Tom Haddon

Add test for missing image password, and include psycopg2-binary in requirements so tests run locally

c5ff3b8... by Tom Haddon

Make sure Dockerfile and docker related files won't be included in the built charm

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/Dockerfile b/Dockerfile
2index f5910bc..2b8d6ef 100644
3--- a/Dockerfile
4+++ b/Dockerfile
5@@ -52,7 +52,7 @@ ENV PGDATA="/srv/pgdata/${PG_MAJOR}/main" \
6 PATH="$PATH:/usr/lib/postgresql/${PG_MAJOR}/bin" \
7 PG_MAJOR="${PG_MAJOR}"
8
9-ARG PKGS_TO_INSTALL="postgresql postgresql-${PG_MAJOR}-repack repmgr python3 python3-psycopg2 python3-tenacity python3-yaml python3-pip less vim sudo"
10+ARG PKGS_TO_INSTALL="postgresql postgresql-${PG_MAJOR}-repack repmgr python3 python3-psycopg2 python3-yaml python3-pip less vim sudo"
11
12 RUN \
13 # Install remaining packages
14diff --git a/Makefile b/Makefile
15index 40ffda9..1bfa93a 100644
16--- a/Makefile
17+++ b/Makefile
18@@ -13,13 +13,13 @@
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22-PG_MAJOR := 12
23-DIST_RELEASE := focal
24+PG_MAJOR ?= 12
25+DIST_RELEASE ?= focal
26
27-IMAGE_REGISTRY :=
28-IMAGE_NAME := pgcharm
29-IMAGE_TAG := latest
30-NO_CACHE :=
31+IMAGE_REGISTRY ?=
32+IMAGE_NAME ?= pgcharm
33+IMAGE_TAG ?= latest
34+NO_CACHE ?=
35 # NO_CACHE := --no-cache
36
37 REGISTRY_IMAGE := $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
38@@ -47,16 +47,7 @@ clean:
39 postgresql.charm: src/*.py requirements.txt *.yaml .jujuignore
40 charmcraft build
41
42-image-deps:
43- @echo "Checking shellcheck is present."
44- @command -v shellcheck >/dev/null || { echo "Please install shellcheck to continue ('sudo snap install shellcheck')" && false; }
45-
46-image-lint: image-deps
47- @echo "Running shellcheck."
48- shellcheck files/docker-entrypoint.sh
49- shellcheck files/docker-readyness.sh
50-
51-image-build: image-lint
52+image-build:
53 @echo "Building the $(LOCAL_IMAGE) image"
54 docker build $(NO_CACHE) -t $(LOCAL_IMAGE) --build-arg BUILD_DATE=$$(date -u +'%Y-%m-%dT%H:%M:%SZ') .
55
56diff --git a/files/docker_entrypoint.py b/files/docker_entrypoint.py
57index 95e5c0f..c1ac0cc 100644
58--- a/files/docker_entrypoint.py
59+++ b/files/docker_entrypoint.py
60@@ -21,3 +21,4 @@ import pgcharm
61
62 if __name__ == "__main__":
63 pgcharm.docker_entrypoint()
64+ # pgcharm.debug_docker_entrypoint()
65diff --git a/files/pgcharm.py b/files/pgcharm.py
66index e82f242..8f03fb3 100644
67--- a/files/pgcharm.py
68+++ b/files/pgcharm.py
69@@ -24,7 +24,6 @@ import time
70
71 import kubernetes
72 import psycopg2
73-from tenacity import before_log, retry, retry_if_exception_type, stop_after_delay, wait_random_exponential
74
75
76 PGDATA = os.environ["PGDATA"] # No underscore, PostgreSQL config
77@@ -45,7 +44,7 @@ JUJU_EXPECTED_UNITS = os.environ["JUJU_EXPECTED_UNITS"].split(" ")
78 NAMESPACE = os.environ["JUJU_POD_NAMESPACE"]
79 HOSTNAME = os.environ["HOSTNAME"]
80
81-AS_PG_CMD = ["sudo", "-u", "postgres", "-EH", "--"]
82+AS_PG_CMD = ["sudo", "-u", "postgres", "-H", "--"]
83 REPMGR_CMD = AS_PG_CMD + ["repmgr", "-f", REPMGR_CONF]
84
85 log = logging.getLogger(__name__)
86@@ -68,7 +67,7 @@ def fix_mounts():
87 shutil.chown(pgpath, user="postgres", group="postgres")
88
89
90-def db_exists() -> bool:
91+def db_exists():
92 return os.path.isdir(PGDATA)
93
94
95@@ -118,12 +117,15 @@ def initdb():
96 "--locale=en_US.UTF-8",
97 "--port=5432",
98 "--datadir=" + PGDATA,
99- "--",
100 "--auth-local=trust",
101 "--auth-host=scram-sha-256",
102 ]
103 log.info(f"Running {' '.join(cmd)}")
104- subprocess.run(cmd, check=True, text=True)
105+ subprocess.run(
106+ ["pg_createcluster", PG_MAJOR, "main", "--locale=en_US.UTF-8", "--port=5432", "--datadir=" + PGDATA],
107+ check=True,
108+ text=True,
109+ )
110
111
112 def start_db():
113@@ -139,7 +141,7 @@ def update_postgresql_conf():
114 outf.write(
115 dedent(
116 f"""\
117- # This file is maintained by the Juju PostgreSQL k8s charm
118+ # This file maintained by the Juju PostgreSQL k8s charm
119 listen_addresses = '*'
120 hot_standby = on
121 wal_level = replica
122@@ -156,7 +158,7 @@ def update_postgresql_conf():
123 os.chmod(pgconf_override, 0o644)
124
125 hba = open(PG_HBA_CONF, "r").readlines()
126- marker = "# These rules are appended by Juju"
127+ marker = "# These rules appended by Juju"
128 if (marker + "\n") not in hba:
129 with open(PG_HBA_CONF, "a") as outf:
130 outf.write("\n")
131@@ -242,14 +244,6 @@ def update_repmgr_conf():
132 os.chmod(REPMGR_CONF, 0o644)
133
134
135-# Retry in case local PostgreSQL is still starting up.
136-@retry(
137- retry=retry_if_exception_type(psycopg2.OperationalError),
138- stop=stop_after_delay(300),
139- wait=wait_random_exponential(multiplier=1, max=15),
140- reraise=True,
141- before=before_log(log, logging.DEBUG),
142-)
143 def update_repmgr_db():
144 log.info(f"Resetting repmgr database user password")
145 con = psycopg2.connect("dbname=postgres user=postgres")
146@@ -280,28 +274,10 @@ def register_repmgr_master():
147 subprocess.run(cmd, check=True, text=True)
148
149
150-# Retry in case the master is not running, or gets shut down midway.
151-@retry(
152- stop=stop_after_delay(300),
153- wait=wait_random_exponential(multiplier=1, max=15),
154- reraise=True,
155- before=before_log(log, logging.DEBUG),
156-)
157-def register_repmgr_standby(master):
158- log.info(f"Registering PostgreSQL hot standby server with {master}")
159+def register_repmgr_standby():
160+ log.info(f"Registering PostgreSQL hot standby server with repmgr")
161 # Always reregister with force, as our IP address might have changed.
162- cmd = REPMGR_CMD + [
163- "standby",
164- "register",
165- "--force",
166- "--wait-sync=60",
167- "-h",
168- get_pod_hostname(master),
169- "-U",
170- "repmgr",
171- "-d",
172- "repmgr",
173- ]
174+ cmd = REPMGR_CMD + ["standby", "register", "--force", "--wait-sync=60"]
175 log.info(f"Running {' '.join(cmd)}")
176 subprocess.run(cmd, check=True, text=True)
177
178@@ -335,22 +311,12 @@ def get_master() -> str:
179 return None
180
181
182-class NoMasterException(Exception):
183- pass
184-
185-
186-@retry(
187- retry=retry_if_exception_type(NoMasterException),
188- stop=stop_after_delay(300),
189- wait=wait_random_exponential(max=12),
190- reraise=True,
191- before=before_log(log, logging.DEBUG),
192-)
193 def wait_master() -> str:
194- master = get_master()
195- if master:
196- return master
197- raise NoMasterException()
198+ while True:
199+ master = get_master()
200+ if master:
201+ return master
202+ time.sleep(5)
203
204
205 def set_master():
206@@ -389,6 +355,13 @@ def get_pod_hostname(name) -> str:
207 return f"{JUJU_APPLICATION}-{name}"
208
209
210+# def get_pod_ip(name) -> str:
211+# cl = kubernetes.client.ApiClient()
212+# api = kubernetes.client.CoreV1Api(cl)
213+# pod = api.read_namespaced_pod(name, NAMESPACE)
214+# return pod.status.pod_ip
215+
216+
217 def init_logging():
218 logging.basicConfig(format="%(asctime)-15s %(levelname)8s: %(message)s")
219 log.setLevel(logging.DEBUG)
220@@ -431,6 +404,7 @@ def docker_entrypoint():
221
222 start_db() # TODO: Ensure DB shutdown cleanly
223
224+ # TODO: Wait until DB is available, via DNS lookup.
225 if is_master():
226 update_repmgr_db()
227 register_repmgr_master()
228@@ -438,8 +412,7 @@ def docker_entrypoint():
229 # pods to continue.
230 set_master()
231 else:
232- master = wait_master()
233- register_repmgr_standby(master)
234+ register_repmgr_standby()
235 set_standby()
236
237 exec_repmgrd() # Does not return
238@@ -455,8 +428,8 @@ def exec_repmgrd():
239 def promote_entrypoint():
240 init_logging()
241 configure_k8s_api()
242- set_master() # First, lessening chance connections go to an existing master.
243 subprocess.check_call(["repmgr", "standby", "promote", "-v", "-f", REPMGR_CONF, "--log-to-file"], text=True)
244+ set_master()
245
246
247 def follow_entrypoint():
248@@ -477,7 +450,7 @@ def follow_entrypoint():
249 ],
250 text=True,
251 )
252- set_standby()
253+ set_master()
254
255
256 def hang_forever():

Subscribers

People subscribed via source and target branches