Zero-Downtime Migration: From nginx-ingress-integrator ingress Relation to nginx-route Relation

A few months ago, the nginx-ingress-integrator charm underwent a major update. The former ingress relation was renamed to the nginx-route relation. This change was due to naming conflicts with the standard ingress relation.

We’re now introducing an update to the nginx-ingress-integrator charm in revision 80, aiming to streamline your transition from the deprecated ingress relation to the new nginx-route relation.

As juju doesn’t permit charm upgrades if the newer version doesn’t support the currently established relations. The original process necessitates users to delete the existing relation, upgrade the charm, and then relate with the new relation:

juju remove-relation app:ingress nginx-ingress-integrator
juju refresh app
juju relate app:nginx-route nginx-ingress-integrator

The above operations induce a temporary void of all relations, causing the ingress’s brief removal from the Kubernetes cluster. Such disruptions can be undesirable to mission-critical applications.

To tackle this challenge, we’re introducing a zero-downtime migration feature in the nginx-ingress-integrator charm. This ensures seamless migration to the nginx-route relation. The procedure, as outlined below, is complicated. We advise only those overseeing mission-critical applications to adopt this method.

Zero-Downtime Migration Steps

Let’s use an example application charm, which you’d like to upgrade to use the nginx-route relation.

Initial charm configuration:

# charm.py
from charms.nginx_ingress_integrator.v0.ingress import IngressRequires

class MyCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        self.ingress = IngressRequires(
            self,
            {
                "service-hostname": "example.com",
                "service-name": self.app.name,
                "service-port": 8080,
            },
        )
# metadata.yaml
requires:
  ingress:
    interface: ingress

Step 1: Update the nginx_ingress_integrator.v0.ingress Charm Library

Ensure your application is using the nginx_ingress_integrator.v0.ingress library with LIBPATCH >= 11. This is crucial. You can update the charm library using the charmcraft fetch-lib command.

Step 2: Create an Intermediate Revision Supporting Both Relations

Your charm should simultaneously support both the ingress and nginx-route relations. Ensure both relations contain identical data sets. Here’s a sample of the modified charm:

# charm.py
from charms.nginx_ingress_integrator.v0.ingress import IngressRequires
from charms.nginx_ingress_integrator.v0.nginx_route import require_nginx_route

class MyCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        self.ingress = IngressRequires(
            self,
            {
                "service-hostname": "example.com",
                "service-name": self.app.name,
                "service-port": 8080,
            },
        )
        require_nginx_route(
            charm=self,
            service_hostname=self._external_hostname,
            service_name=self.app.name,
            service_port=8080
        )
# metadata.yaml
requires:
  ingress:
    interface: ingress
  nginx-route:
    interface: nginx-route

Step 3: Upgrade to the Intermediate Revision and Swap the Relation

Upload the intermediate revision to charmhub. Execute the following commands to relate with the nginx-route relation while keeping the ingress relation:

juju refresh app
juju relate app:nginx-route nginx-ingress-integrator

The nginx-ingress-integrator will recognize and consolidate the nginx-route and ingress relations from the application. Once stable, the legacy ingress relation can be safely removed without affecting the Kubernetes ingress object:

juju remove-relation app:ingress nginx-ingress-integrator

Step 4: Finalize Transition to nginx-route

You’re now ready for the transition to nginx-route exclusively. Your charm’s next revision should be like:

# charm.py
from charms.nginx_ingress_integrator.v0.nginx_route import require_nginx_route

class MyCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        require_nginx_route(
            charm=self,
            service_hostname=self._external_hostname,
            service_name=self.app.name,
            service_port=8080
        )
# metadata.yaml
requires:
  nginx-route:
    interface: nginx-route

Having uploaded this version to charmhub, using juju refresh to update without any complications.