This tutorial follows on from part 1:
Here, we go into more detail about what relations actually are. It also covers the mechanisms that you have available to introspect Juju.
Scenario: informing two applications of each other’s presence
The most visible use of relations is when they’re linking two applications. The terminology is confusing here. The link provided by a relation is conceptual. No networking rules are established when the relation is added.
A relation is:
- A unit to unit mechanism. We’ll cover the exceptions in a later post.
- A signal that another unit has joined (or left) the model
- How units share data
We saw in the earlier turorial that relations are unit to unit. This post explains the last two bullet points.
Let’s start with an example: adding HTTPS to a webservice via Let’s Encrypt.
juju deploy ghost juju deploy cs:~tengu-team/ssl-termination-proxy
At this point, both charms will begin the deployment process. That involves ensuring a host machine is present and agents for the machine and the unit are active. The unit agent takes responsibility for installing the software defined by each charm.
Before moving on, agree to the terms of the Let’s Encrypt service:
juju agree isrg-lets-encrypt/2
After a few minutes, the ghost charm will be ready. Use the
juju status command to find its IP address:
juju status --format=oneline
- ghost/0: 10.110.147.29 (agent:idle, workload:active) 2368/tcp - ssl-termination-proxy/0: 10.110.147.129 (agent:idle, workload:blocked) 80/tcp, 443/tcp
Ghost can receive HTTP requests:
curl --head 10.110.147.29:2368
HTTP/1.1 200 OK Server: nginx/1.10.3 (Ubuntu) Date: Wed, 27 Nov 2019 20:09:47 GMT Content-Type: text/html; charset=utf-8 Content-Length: 4557 Connection: keep-alive X-Powered-By: Express Cache-Control: public, max-age=0 ETag: W/"11cd-sWYHXlO9+Ren81NIJDNffpjLGp8" Vary: Accept-Encoding
You may have noticed that the
ssl-termination-proxy/0 line includes
workload:blocked. The full status output for the
ssl-termination-proxy/0 unit provides a hint:
Which includes the line:
ssl-termination-proxy/0* blocked idle 3 10.110.147.129 80/tcp,443/tcp waiting for fqdn subordinates
The unit is waiting for web services to protect. The process for supporting multiple applications is more complex, but for a single one, adding a single HTTPS support is a 2 step process.
- Tell ssl-termination-proxy the domain name
- Relate the applications
Step 1 requires you to use a domain name registrar to update DNS records. Add an A record to point to the IP address of the
Step 2 is easier. A single Juju command.
juju relate ghost ssl-termination-proxy
If we check the output from
juju status again, when adding the
--relations option, we’ll receive an extra line:
juju status --relations
Includes the output:
Relation provider Requirer Interface Type Message ghost:website ssl-termination-proxy:reverseproxy http regular
Now, you should be able to visit Ghost over HTTPS via the domain name that you’ve set up with the DNS record.
Things now work. The relation has allowed both sides to update their configuration according to the new situation. That’s great, but it can feel a little magical. Let’s focus on explaining what relations do to enable this sort of change.
How do relations work?
juju relate instructs the unit agents to invoke commands provided by the charm. When a relation is added, the code within the charm is responsible for making any necessary changes.
Juju will only perform actions that are dictated by charms. It won’t update any configuration values by itself. Charm authors retain full control over what happens when two applications are related.
juju show-status-log command provides a complete history of the hooks that a unit agent:
juju show-status-log --type juju-unit ghost/0
Time Type Status Message 28 Nov 2019 10:00:30+13:00 juju-unit allocating 28 Nov 2019 10:01:51+13:00 juju-unit executing running app-storage-attached hook 28 Nov 2019 10:05:07+13:00 juju-unit executing running install hook 28 Nov 2019 10:05:08+13:00 juju-unit executing running leader-elected hook 28 Nov 2019 10:05:08+13:00 juju-unit executing running config-changed hook 28 Nov 2019 10:05:09+13:00 juju-unit executing running start hook 28 Nov 2019 10:05:09+13:00 juju-unit executing running website-relation-joined hook 28 Nov 2019 10:05:10+13:00 juju-unit executing running website-relation-changed hook 28 Nov 2019 10:05:11+13:00 juju-unit idle
The important lines, from the point of view of relations, are what happens after the start hook. All of the configuration changes for the ghost must have happened within the
ssl-termination-proxy side, a similar pattern occurs. Here though, the unit agent was able to enter the idle state before executing its relation hooks.
juju show-status-log --type juju-unit ssl-termination-proxy/0
Time Type Status Message 28 Nov 2019 10:01:14+13:00 juju-unit allocating 28 Nov 2019 10:02:59+13:00 juju-unit executing running install hook 28 Nov 2019 10:05:02+13:00 juju-unit executing running leader-elected hook 28 Nov 2019 10:05:02+13:00 juju-unit executing running config-changed hook 28 Nov 2019 10:05:03+13:00 juju-unit executing running start hook 28 Nov 2019 10:05:04+13:00 juju-unit idle 28 Nov 2019 10:05:10+13:00 juju-unit executing running reverseproxy-relation-joined hook 28 Nov 2019 10:05:10+13:00 juju-unit executing running reverseproxy-relation-changed hook 28 Nov 2019 10:05:11+13:00 juju-unit idle
If you’re wondering why the hooks have different names in each case, it’s because hook names are somewhat arbitrary. They’re defined as endpoints within a charm’s metadata.yaml file that both speak one end the “http” interface. A future post will explain interfaces and endpoints.
The relation-joined hook signals to units that another unit is available. But we still haven’t explained the process by which they share data.
Juju offers charm writers two hook tools,
relation-set. They’re used by unit agents to send key value pairs. The controller relays the data between agents (there is no peer to peer communication system available to unit agents). It’s possible to simulate what’s happening from the client side, but the process is somewhat convoluted.
juju exec command in the context of the
ghost/0 unit, we’ll run the
relation-ids hook tool. Once we have the internal relation ID, we’ll use that to inspect the data sent over the wire.
First, let’s check what the
ssl-termination-proxy is making available to any ghost units:
$ juju exec --unit ghost/0 "relation-ids website" website:0 $ juju exec --unit ghost/0 "relation-get -r website:0 - ssl-termination-proxy/0" egress-subnets: 10.110.147.57/32 ingress-address: 10.110.147.57 private-address: 10.110.147.57
ghost/0 is able to access IP addresses, but not much else. What is happening on the other side?
$ juju exec --unit ssl-termination-proxy/0 "relation-ids reverseproxy" reverseproxy:0 $ juju exec --unit ssl-termination-proxy/0 "relation-get -r reverseproxy:0 - ghost/0" egress-subnets: 10.110.147.53/32 hostname: 10.110.147.53 ingress-address: 10.110.147.53 port: "2368" private-address: 10.110.147.53
And, that’s how things work. The relation hooks signal to a unit that another unit has appeared and the hook tools are how units share data.
To get a fuller understanding, we need to detangle the terms endpoint, interface and relation. That’s next!