Skip to content

Make mysql-test-app container-aware for Juju 4.0 compatibility#53

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/make-mysql-test-app-container-aware
Draft

Make mysql-test-app container-aware for Juju 4.0 compatibility#53
Copilot wants to merge 3 commits intomainfrom
copilot/make-mysql-test-app-container-aware

Conversation

Copy link
Copy Markdown

Copilot AI commented Jan 27, 2026

The charm sets status based on database relation availability without checking container readiness, causing Juju 4.0 to display incorrect status (unknown/running when charm is active but container isn't ready).

Changes

Container Definition

Added Kubernetes container and OCI image resource to metadata.yaml:

containers:
  mysql-test-app:
    resource: mysql-test-app-image

resources:
  mysql-test-app-image:
    type: oci-image
    upstream-source: ghcr.io/canonical/charmed-mysql-rock:8.0-22.04-edge

Pebble Integration

  • Added _container property and _is_container_ready() helper
  • Observe mysql_test_app_pebble_ready event to configure Pebble layer
  • Centralized status logic in _update_status() that enforces check order: container readiness → database availability
  • Updated all event handlers (_on_start, _on_update_status, _on_relation_changed, _on_relation_broken, _on_peer_relation_changed) to use centralized status method

Status Progression

Initial: "Waiting for container"
  → Container ready, no DB: "Waiting for database relation"
    → DB related: "Active"

Container readiness now gates all status transitions, preventing premature ActiveStatus reports.

Original prompt

Make mysql-test-app container-aware to fix Juju 4.0 status display

Context

This charm is being used on Kubernetes but lacks proper container definitions and Pebble awareness. This causes status display issues in Juju 4.0 (see juju/juju#21641 and juju/juju#21671).

The charm currently sets WaitingStatus() or ActiveStatus() based solely on database relation availability, without checking if its own container is ready. This causes Juju to show inconsistent status displays:

  • Juju 3.6: Shows waiting (due to compatibility hacks)
  • Juju 4.0: Shows unknown or running when the charm thinks it's active but the container isn't ready

Required Changes

1. Add container and resource definitions to metadata.yaml

Add the following to metadata.yaml:

containers:
  mysql-test-app:
    resource: mysql-test-app-image

resources:
  mysql-test-app-image:
    type: oci-image
    description: OCI image for mysql-test-app
    # Use a lightweight Python image that can run the charm code
    upstream-source: ghcr.io/canonical/charmed-mysql-rock:8.0-22.04-edge

2. Update src/charm.py to be Pebble-aware

Add the following changes to src/charm.py:

A. Update imports:

from ops.model import ActiveStatus, Relation, WaitingStatus, MaintenanceStatus
from ops.pebble import Layer

B. Add container property and helper methods after the existing properties:

@property
def _container(self):
    """Return the mysql-test-app container."""
    return self.unit.get_container("mysql-test-app")

def _is_container_ready(self) -> bool:
    """Check if the container is ready."""
    try:
        return self._container.can_connect()
    except Exception:
        return False

C. Add pebble-ready observer in __init__:

def __init__(self, *args):
    super().__init__(*args)

    # ADD THIS before other observers:
    self.framework.observe(
        self.on.mysql_test_app_pebble_ready, self._on_pebble_ready
    )

    # ... rest of existing observers ...

D. Add the pebble-ready handler method:

def _on_pebble_ready(self, event) -> None:
    """Handle pebble-ready event."""
    # Define the pebble layer
    # Note: We don't define services here because continuous_writes runs as subprocess
    layer = Layer({
        "summary": "mysql-test-app layer",
        "description": "pebble config for mysql-test-app",
    })
    
    self._container.add_layer("mysql-test-app", layer, combine=True)
    self._container.replan()
    
    # Re-evaluate status now that container is ready
    self._update_status()

E. Add centralized status update method:

def _update_status(self) -> None:
    """Update the unit status based on container and database availability."""
    # CRITICAL: Check container readiness FIRST
    if not self._is_container_ready():
        self.unit.status = WaitingStatus("Waiting for container")
        return
        
    # Now check database connectivity
    if self._database_config:
        self.unit.status = ActiveStatus()
    else:
        self.unit.status = WaitingStatus("Waiting for database relation")

F. Update _on_start to check container readiness:

def _on_start(self, event) -> None:
    """Handle the start event."""
    self.unit.set_workload_version("0.0.2")
    
    # Defer if container not ready
    if not self._is_container_ready():
        self.unit.status = WaitingStatus("Waiting for container")
        event.defer()
        return
    
    self._update_status()

    if self.unit_peer_data.get(PROC_PID_KEY) and not self.is_writes_running:
        # Auto start writes for restarted unit/container
        self._on_start_continuous_writes_action(None)

G. Update _on_update_status to check container:

def _on_update_status(self, _) -> None:
    """Get last written value and update status."""
    if not self._is_container_ready():
        self.unit.status = WaitingStatus("Waiting for container")
        return
        
    if self.unit_peer_data.get(PROC_PID_KEY):
        try:
            value = self._max_written_value()
            if value > 0:
                logger.info(f"Last written {value=}")
                self.unit.status = ActiveStatus(f"Last written {value=}")
            else:
                logger.info("No connection data available")
        except Exception:
            logger.info("Unable to get last written value")
    else:
        self._update_status()

H. Replace existing status setting calls:

Replace these existing method implementations:

def _on_relation_changed(self, _) -> None:
    """Handle the database relation changed event."""
    self._update_status()

def _on_relation_broken(self, _) -> None:
    """Handle the database relation broken event."""
    self._stop_continuous_writes()
    if self.unit.is_leader():
        self.app_peer_data.pop("database-start", Non...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 2 commits January 27, 2026 17:40
- Add container and resource definitions to metadata.yaml
- Update charm.py with Pebble awareness:
  - Add Layer import
  - Add container property and _is_container_ready() helper
  - Add pebble-ready observer and handler
  - Add centralized _update_status() method
  - Update event handlers to check container readiness first
- All handlers now use _update_status() for consistent status management
- Fix typo: "estabilshed" -> "established"

Co-authored-by: astrojuanlu <316517+astrojuanlu@users.noreply.github.com>
Co-authored-by: astrojuanlu <316517+astrojuanlu@users.noreply.github.com>
Copilot AI changed the title [WIP] Make mysql-test-app container-aware for Juju 4.0 status display Make mysql-test-app container-aware for Juju 4.0 compatibility Jan 27, 2026
Copilot AI requested a review from astrojuanlu January 27, 2026 17:43
@astrojuanlu
Copy link
Copy Markdown
Contributor

This was me playing with Copilot, will have a proper look soon.

@github-actions github-actions Bot added the Libraries: Out of sync The charm libs used are out-of-sync label Jan 27, 2026
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