Skip to content

Commit

Permalink
Migrating to Rock OCI image (#118)
Browse files Browse the repository at this point in the history
* Initial switch

* Use stable tls-certificates-operator

* Temporarily manually installing ca-certs

* Revert "Temporarily manually installing ca-certs"

This reverts commit 2fd47db.

* Read version from the Rock image

* Switch to GHCR Rock
  • Loading branch information
dragomirp authored Apr 6, 2023
1 parent 9a23f1d commit 1b7f8dc
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 11 deletions.
6 changes: 3 additions & 3 deletions metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ containers:
resources:
postgresql-image:
type: oci-image
description: OCI image for PostgreSQL (dataplatformoci/postgres-patroni)
upstream-source: dataplatformoci/postgres-patroni
description: OCI image for PostgreSQL
upstream-source: ghcr.io/canonical/charmed-postgresql:14.7-22.04_edge

peers:
database-peers:
Expand All @@ -47,4 +47,4 @@ requires:
storage:
pgdata:
type: filesystem
location: /var/lib/postgresql/data
location: /var/lib/postgresql/data
3 changes: 2 additions & 1 deletion src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ def _set_primary_status_message(self) -> None:
def _patroni(self):
"""Returns an instance of the Patroni object."""
return Patroni(
self,
self._endpoint,
self._endpoints,
self.primary_endpoint,
Expand Down Expand Up @@ -814,7 +815,7 @@ def _postgresql_layer(self) -> Layer:
self._postgresql_service: {
"override": "replace",
"summary": "entrypoint of the postgresql + patroni image",
"command": f"/usr/bin/python3 /usr/local/bin/patroni {self._storage_path}/patroni.yml",
"command": f"patroni {self._storage_path}/patroni.yml",
"startup": "enabled",
"user": WORKLOAD_OS_USER,
"group": WORKLOAD_OS_GROUP,
Expand Down
14 changes: 14 additions & 0 deletions src/patroni.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from typing import List, Optional

import requests
import yaml
from jinja2 import Template
from tenacity import (
AttemptManager,
Expand Down Expand Up @@ -40,6 +41,7 @@ class Patroni:

def __init__(
self,
charm,
endpoint: str,
endpoints: List[str],
primary_endpoint: str,
Expand All @@ -50,6 +52,7 @@ def __init__(
rewind_password: str,
tls_enabled: bool,
):
self._charm = charm
self._endpoint = endpoint
self._endpoints = endpoints
self._primary_endpoint = primary_endpoint
Expand All @@ -70,6 +73,16 @@ def _patroni_url(self) -> str:
"""Patroni REST API URL."""
return f"{'https' if self._tls_enabled else 'http'}://{self._endpoint}:8008"

@property
def rock_postgresql_version(self) -> Optional[str]:
"""Version of Postgresql installed in the Rock image."""
container = self._charm.unit.get_container("postgresql")
if not container.can_connect():
logger.debug("Cannot get Postgresql version from Rock. Container inaccessible")
return
snap_meta = container.pull("/meta.charmed-postgresql/snap.yaml")
return yaml.safe_load(snap_meta)["version"]

def _get_alternative_patroni_url(self, attempt: AttemptManager) -> str:
"""Get an alternative REST API URL from another member each time.
Expand Down Expand Up @@ -231,6 +244,7 @@ def render_patroni_yml_file(
restoring_backup=backup_id is not None,
backup_id=backup_id,
stanza=stanza,
version=self.rock_postgresql_version.split(".")[0],
)
self._render_file(f"{self._storage_path}/patroni.yml", rendered, 0o644)

Expand Down
3 changes: 2 additions & 1 deletion templates/patroni.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ bootstrap:
use_pg_rewind: true
remove_data_directory_on_rewind_failure: true
remove_data_directory_on_diverged_timelines: true
bin_dir: /usr/lib/postgresql/{{ version }}/bin
parameters:
{%- if enable_pgbackrest %}
archive_command: 'pgbackrest --stanza={{ stanza }} archive-push %p'
Expand All @@ -24,7 +25,6 @@ bootstrap:
- auth-host: md5
- auth-local: trust
- encoding: UTF8
- locale: en_US.UTF-8
- data-checksums
{%- endif %}
pg_hba:
Expand Down Expand Up @@ -52,6 +52,7 @@ postgresql:
connect_address: '{{ endpoint }}:5432'
custom_conf: {{ storage_path }}/postgresql-k8s-operator.conf
data_dir: {{ storage_path }}/pgdata
bin_dir: /usr/lib/postgresql/{{ version }}/bin
listen: 0.0.0.0:5432
parameters:
{%- if enable_pgbackrest %}
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_backups.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async def test_backup_and_restore(ops_test: OpsTest, cloud_configs: Tuple[Dict,
# Deploy S3 Integrator and TLS Certificates Operator.
await ops_test.model.deploy(S3_INTEGRATOR_APP_NAME, channel="edge")
config = {"generate-self-signed-certificates": "true", "ca-common-name": "Test CA"}
await ops_test.model.deploy(TLS_CERTIFICATES_APP_NAME, channel="beta", config=config)
await ops_test.model.deploy(TLS_CERTIFICATES_APP_NAME, config=config)

for cloud, config in cloud_configs[0].items():
# Deploy and relate PostgreSQL to S3 integrator (one database app for each cloud for now
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ async def test_mattermost_db(ops_test: OpsTest) -> None:
async with ops_test.fast_forward():
# Deploy TLS Certificates operator.
config = {"generate-self-signed-certificates": "true", "ca-common-name": "Test CA"}
await ops_test.model.deploy(TLS_CERTIFICATES_APP_NAME, channel="beta", config=config)
await ops_test.model.deploy(TLS_CERTIFICATES_APP_NAME, config=config)
# Relate it to the PostgreSQL to enable TLS.
await ops_test.model.relate(DATABASE_APP_NAME, TLS_CERTIFICATES_APP_NAME)
await ops_test.model.wait_for_idle(status="active", timeout=1000)
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ def test_postgresql_layer(self, _, __, ___, ____):
self._postgresql_service: {
"override": "replace",
"summary": "entrypoint of the postgresql + patroni image",
"command": "/usr/bin/python3 /usr/local/bin/patroni /var/lib/postgresql/data/patroni.yml",
"command": "patroni /var/lib/postgresql/data/patroni.yml",
"startup": "enabled",
"user": "postgres",
"group": "postgres",
Expand Down
22 changes: 19 additions & 3 deletions tests/unit/test_patroni.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,30 @@
# See LICENSE file for licensing details.

import unittest
from unittest.mock import mock_open, patch
from unittest.mock import PropertyMock, mock_open, patch

from jinja2 import Template
from ops.testing import Harness
from tenacity import RetryError

from charm import PostgresqlOperatorCharm
from constants import REWIND_USER
from patroni import Patroni
from tests.helpers import STORAGE_PATH
from tests.helpers import STORAGE_PATH, patch_network_get


class TestPatroni(unittest.TestCase):
@patch("charm.KubernetesServicePatch", lambda x, y: None)
@patch_network_get(private_address="1.1.1.1")
def setUp(self):
self.harness = Harness(PostgresqlOperatorCharm)
self.addCleanup(self.harness.cleanup)
self.harness.begin()
self.charm = self.harness.charm

# Setup Patroni wrapper.
self.patroni = Patroni(
self.charm,
"postgresql-k8s-0",
["postgresql-k8s-0"],
"postgresql-k8s-primary.dev.svc.cluster.local",
Expand Down Expand Up @@ -77,8 +87,11 @@ def test_render_file(self, _temp_file, _pwnam, _chown, _chmod):
# Ensure the file is chown'd correctly.
_chown.assert_called_with(filename, uid=35, gid=35)

@patch("charm.Patroni.rock_postgresql_version", new_callable=PropertyMock)
@patch("charm.Patroni._render_file")
def test_render_patroni_yml_file(self, _render_file):
def test_render_patroni_yml_file(self, _render_file, _rock_postgresql_version):
_rock_postgresql_version.return_value = "14.7"

# Get the expected content from a file.
with open("templates/patroni.yml.j2") as file:
template = Template(file.read())
Expand All @@ -92,6 +105,7 @@ def test_render_patroni_yml_file(self, _render_file):
replication_password=self.patroni._replication_password,
rewind_user=REWIND_USER,
rewind_password=self.patroni._rewind_password,
version="14",
)

# Setup a mock for the `open` method, set returned data to postgresql.conf template.
Expand Down Expand Up @@ -125,6 +139,7 @@ def test_render_patroni_yml_file(self, _render_file):
replication_password=self.patroni._replication_password,
rewind_user=REWIND_USER,
rewind_password=self.patroni._rewind_password,
version="14",
)
self.assertNotEqual(expected_content_with_tls, expected_content)

Expand Down Expand Up @@ -180,6 +195,7 @@ def test_render_postgresql_conf_file(self, _render_file):

# Also test with multiple planned units (synchronous_commit is turned on).
self.patroni = Patroni(
self.charm,
"postgresql-k8s-0",
["postgresql-k8s-0", "postgresql-k8s-1"],
"postgresql-k8s-primary.dev.svc.cluster.local",
Expand Down

0 comments on commit 1b7f8dc

Please sign in to comment.