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
Output:
- 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
Output:
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:
juju status
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 ssl-termination-proxy/0
unit.
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?
Like juju deploy
, 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.
The 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
Output:
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 website-relation-joined
and website-relation-changed
hooks.
On 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
Output:
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-get
and 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.
Using the 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!