Skip to content

Conversation

@marceloneppel
Copy link
Member

Issue

Solution

Add a lightweight witness/voter charm that participates in Raft consensus to provide quorum in 2-node PostgreSQL clusters without storing any PostgreSQL data.

Key components:

  • Watcher charm with Raft controller integration
  • Health checking for PostgreSQL endpoints
  • Relation interface (postgresql_watcher) for PostgreSQL operator
  • Topology and health check actions

Checklist

  • I have added or updated any relevant documentation.
  • I have cleaned any remaining cloud resources from my accounts.

Add a lightweight witness/voter charm that participates in Raft
consensus to provide quorum in 2-node PostgreSQL clusters without
storing any PostgreSQL data.

Key components:
- Watcher charm with Raft controller integration
- Health checking for PostgreSQL endpoints
- Relation interface (postgresql_watcher) for PostgreSQL operator
- Topology and health check actions

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
@github-actions github-actions bot added the Libraries: Out of sync The charm libs used are out-of-sync label Jan 27, 2026
content = secret.get_content(refresh=True)
return content.get("raft-password")
except SecretNotFoundError:
logger.warning(f"Secret {secret_id} not found")

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (secret)
as clear text.
# Get the secret ID for sharing
try:
secret = self.charm.model.get_secret(label=WATCHER_SECRET_LABEL)
logger.info(f"Got secret for update: {secret}")

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (secret)
as clear text.
secret = self.charm.model.get_secret(label=WATCHER_SECRET_LABEL)
logger.info(f"Got secret for update: {secret}")
secret_id = secret.id
logger.info(f"Initial secret_id: {secret_id}")

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (secret)
as clear text.
# the ops library lazily loads the ID. We need the ID to share with the watcher.
logger.info("Applying secret ID workaround")
secret_info = secret.get_info()
logger.info(f"Secret info: {secret_info}, id={secret_info.id}")

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (secret)
as clear text.
# Use the ID directly from get_info() - it already has the full URI
secret._id = secret_info.id
secret_id = secret.id
logger.info(f"Workaround secret_id: {secret_id}")

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (secret)
as clear text.
"raft-partner-addrs": json.dumps(sorted(raft_partner_addrs)),
"raft-port": str(RAFT_PORT),
}
logger.info(f"Updating relation app data: {update_data}")

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
@codecov
Copy link

codecov bot commented Jan 27, 2026

Codecov Report

❌ Patch coverage is 59.37500% with 91 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.98%. Comparing base (8ae83a7) to head (cb8276c).
⚠️ Report is 1 commits behind head on 16/edge.

Files with missing lines Patch % Lines
src/relations/watcher.py 65.07% 54 Missing and 12 partials ⚠️
src/cluster.py 9.09% 20 Missing ⚠️
src/charm.py 37.50% 4 Missing and 1 partial ⚠️

❌ Your project check has failed because the head coverage (69.98%) is below the target coverage (70.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##           16/edge    #1401      +/-   ##
===========================================
- Coverage    70.53%   69.98%   -0.56%     
===========================================
  Files           16       17       +1     
  Lines         4297     4521     +224     
  Branches       691      720      +29     
===========================================
+ Hits          3031     3164     +133     
- Misses        1055     1133      +78     
- Partials       211      224      +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

… pysyncobj Raft service

Add standalone raft_service.py that implements KVStoreTTL-compatible
Raft node managed as a systemd service, eliminating the dependency on
the charmed-postgresql snap. Remove automatic health checks in favor of
on-demand checks via action, since the watcher lacks PostgreSQL credentials.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
return

# Write service file
Path(SERVICE_FILE).write_text(service_content)

Check failure

Code scanning / CodeQL

Clear-text storage of sensitive information High

This expression stores
sensitive data (password)
as clear text.
This expression stores
sensitive data (password)
as clear text.
…tereo mode tests

Replace cut_network_from_unit_without_ip_change with cut_network_from_unit
in stereo mode integration tests. The iptables-based approach with REJECT
was still causing timeouts; removing the interface entirely triggers faster
TCP connection failures. Added use_ip_from_inside=True for check_writes
since restored units get new IPs. Also adds spread task for stereo mode tests.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Libraries: Out of sync The charm libs used are out-of-sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants