jhack
This is a homegrown collection of opinionated scripts and utilities to make the charm dev's life somewhat easier.
Installation:
from sources (dev setup):
Clone the repo; alias '/path/to/jhack/main.py' as 'jhack', or something. Ensure the requirements are installed:
$ pip install -r requirements.txt
as package:
$ python setup.py bdist_wheel
$ pip install ./dist/jhack-v...whl
as snap (coming soon!):
$ sudo snap install --edge --devmode jhack
Usage:
jhack [category] [command]
for example:
$ jhack utils tail
$ jhack model rm
Happy hacking!
utils
sync
jhack utils sync ./src application-name/0
Will watch the ./src folder for changes and push any to application-name/0 under /charm/src/.
unbork-juju
jhack utils unbork-juju
Does exactly what it says, and it does it pretty well.
ffwd
jhack utils ffwd
Fast-forwards the firing of update-status
hooks, and restores it to a 'slow' firing rate after the process is killed or after a given timeout.
Self-explanation:
jhack utils ffwd
--timeout 10 # exits after 10 seconds
--fast-interval 5 # update-status fires each 5 seconds
--slow-interval 50m # when done, set update-status firing rate to 50 minutes.
tail
Monitors the logs and gathers all logs concerning events being fired on the units. Will pprint the last N in a nice format. Keeps listening and updates in the background as new units are added.
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ timestamp ┃ traefik-k8s/0 ┃ prometheus-k8s/1 ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ 13:37:15 │ │ ingress-relation-changed │
│ 13:37:14 │ │ ingress-relation-joined │
│ 13:37:14 │ │ ingress-relation-changed │
│ 13:37:13 │ │ prometheus-peers-relation-c… │
│ 13:37:12 │ │ prometheus-peers-relation-j… │
│ 13:37:12 │ │ prometheus-pebble-ready │
│ 13:37:11 │ │ start │
│ 13:37:10 │ │ config-changed │
│ 13:37:09 │ │ │
│ 13:37:09 │ │ database-storage-attached │
│ 13:37:09 │ ingress-per-unit-relation-c… │ │
│ 13:37:08 │ │ leader-settings-changed │
│ 13:37:08 │ ingress-per-unit-relation-c… │ │
│ 13:37:08 │ │ │
│ 13:37:08 │ │ ingress-relation-created │
│ 13:37:07 │ ingress-per-unit-relation-j… │ │
│ 13:37:07 │ │ │
│ 13:37:07 │ │ prometheus-peers-relation-c… │
│ 13:37:06 │ │ install │
└───────────┴──────────────────────────────┴──────────────────────────────┘
There's more!
You can use tail
to visualize deferrals in ops
.
If you pass the -d
flag, short for --show-defer
, whenever an event is deferred, reemitted, or re-deferred, you'll be able to follow it right along the tail.
You might see then something like:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ timestamp ┃ trfk/0 ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ 14:02:53 │ │ │
│ 14:01:36 │ event_3 ❯─┘ │
│ 13:56:49 │ ingress_per_unit_relation_changed │
│ 13:56:47 │ ingress_per_unit_relation_changed │
│ 13:56:47 │ ingress_per_unit_relation_changed │
│ 13:56:46 │ ingress_per_unit_relation_joined │
│ 13:56:46 │ event_3 ❮─┐ │
│ 13:56:46 │ ingress_per_unit_relation_created │ │
│ 13:46:30 │ event_3 ⭘┤ │
│ 13:41:51 │ event_3 ❯─┘ │
│ 13:41:51 │ event_2 ❮─┐ │
│ 13:36:50 │ event_2 ❯─┘ │
│ 13:36:50 │ event_1 ❮─┐ │
│ 13:31:29 │ event_1 ❯─┘ │
[...]
The little circle is event-3
getting re-emitted and immediately re-deferred!
The graph can get nice and messy if multiple events get deferred in an interleaved fashion, enabling you to see what's going on. Which is nice.
update_status ❮──────┐
update_status .....│
update_status ⭘─────┤
update_status .....│
update_status ⭘─────┤
update_status ❮─────┐│
update_status ❯─────┼┘
update_status ⭘────┤
update_status ❮────┐│
update_status ❯────┼┘
update_status ❮────┼┐
update_status ⭘───┤│
update_status ❯────┼┘
update_status ⭘───┤
update_status ❮───┐│
update_status ❮──┐││
update_status ❯──┼┼┘
update_status ⭘─┼┤
update_status ⭘─┤│
update_status ❯──┼┘
And did I mention that there's colors?
tail
saved logs
You can also Say you have saved two debug-logs with:
juju debug-log --date -i prom/0 > prom.log
juju debug-log --date -i trfk/0 > trfk.log
Yielding files:
prom.txt
unit-prom-0: 2022-07-20 10:00:00 INFO juju.worker.uniter.operation ran "install" hook (via hook dispatching script: dispatch)
unit-prom-0: 2022-07-21 5:00:00 INFO juju.worker.uniter.operation ran "prometheus-peers-relation-created" hook (via hook dispatching script: dispatch)
trfk.txt
unit-trfk-0: 2022-07-20 11:00:00 INFO juju.worker.uniter.operation ran "start" hook (via hook dispatching script: dispatch)
unit-trfk-0: 2022-07-20 12:00:00 INFO juju.worker.uniter.operation ran "traefik-pebble-ready" hook (via hook dispatching script: dispatch)
You can run jhack utils tail --file=prom.txt --file=trfk.txt
to see the events in all the logs, interlaced in the correct chronological order as expected:
┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ timestamp ┃ prom/0 ┃ trfk/0 ┃
┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ 5:00:00 │ prometheus_peers_relation_created │ │
│ 12:00:00 │ │ traefik_pebble_ready │
│ 11:00:00 │ │ start │
│ 10:00:00 │ install │ │
├────────────────┼──────────────────────────────────────┼─────────────────────────┤
│ The end. │ prom/0 │ trfk/0 │
├────────────────┼──────────────────────────────────────┼─────────────────────────┤
│ events emitted │ 2 │ 2 │
└────────────────┴──────────────────────────────────────┴─────────────────────────┘
show-relation
Displays the databags of units involved in a relation.
if the endpoint is of the form app-name/id:relation-name
: it will display the application databag and the one for the unit with id=id
.
If the endpoint is of the form app-name:relation-name
: it will display the application databag and the databags for all units.
Examples:
jhack utils show-relation ipu:ingress-per-unit traefik-k8s:ingress-per-unit
jhack utils show-relation ipu/0:ingress-per-unit traefik-k8s:ingress-per-unit
jhack utils show-relation ipu/0:ingress-per-unit traefik-k8s/2:ingress-per-unit
Example output:
relation data v0.2
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ category ┃ traefik-k8s ┃ ipu ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ relation name │ ingress-per-unit │ ingress-per-unit │
│ interface │ ingress_per_unit │ ingress_per_unit │
│ leader unit │ 0 │ 0 │
├──────────────────┼─────────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ application data │ ╭─────────────────────────────────────────────────╮ │ ╭────────────────────────────────────────────────╮ │
│ │ │ │ │ │ <empty> │ │
│ │ │ ingress ipu/0: │ │ ╰────────────────────────────────────────────────╯ │
│ │ │ url: │ │ │
│ │ │ http://my.it:80/test-charm-ipu-9dg8… │ │ │
│ │ │ ipu/1: │ │ │
│ │ │ url: │ │ │
│ │ │ http://my.it:80/test-charm-ipu-9dg8… │ │ │
│ │ │ ipu/2: │ │ │
│ │ │ url: │ │ │
│ │ │ http://my.it:80/test-charm-ipu-9dg8… │ │ │
│ │ ╰─────────────────────────────────────────────────╯ │ │
│ unit data │ ╭─ traefik-k8s/0* ─╮ ╭─ traefik-k8s/1 ─╮ │ ╭─ ipu/0* ────────────────────╮ │
│ │ │ <empty> │ │ <empty> │ │ │ │ │
│ │ ╰──────────────────╯ ╰─────────────────╯ │ │ host foo.bar │ │
│ │ ╭─ traefik-k8s/2 ──╮ ╭─ traefik-k8s/3 ─╮ │ │ model test-charm-ipu-9dg8 │ │
│ │ │ <empty> │ │ <empty> │ │ │ name ipu/0 │ │
│ │ ╰──────────────────╯ ╰─────────────────╯ │ │ port 80 │ │
│ │ │ ╰──────────────────────────────╯ │
│ │ │ ╭─ ipu/1 ─────────────────────╮ │
│ │ │ │ │ │
│ │ │ │ host foo.bar │ │
│ │ │ │ model test-charm-ipu-9dg8 │ │
│ │ │ │ name ipu/1 │ │
│ │ │ │ port 80 │ │
│ │ │ ╰──────────────────────────────╯ │
│ │ │ ╭─ ipu/2 ─────────────────────╮ │
│ │ │ │ │ │
│ │ │ │ host foo.bar │ │
│ │ │ │ model test-charm-ipu-9dg8 │ │
│ │ │ │ name ipu/2 │ │
│ │ │ │ port 80 │ │
│ │ │ ╰──────────────────────────────╯ │
└──────────────────┴─────────────────────────────────────────────────────┴────────────────────────────────────────────────────┘
Since v0.3, also peer relations are supported. Additionally, it supports “show me the nth relation” instead of having to type out the whole app-name:endpoint thing: if you have 3 relations in your model, you can simply do jhack show-relation -n 1 and jhack will print out the 2nd relation from the top (of the same list appearing when you do juju status --relations, that is.
nuke
This utility is the swiss army knife of "just get rid of this thing already". The broad goal is to have one easy-to-use command to destroy things in the most dirty and unsafe way possible, just please make it fast and please don't make me type all those letters out.
The tool is designed to be used with juju status --relations
and juju models
.
The basic usage is as follows:
jhack nuke
-> will nuke the current model and that's that.
jhack nuke foo*
-> will:
- scan
juju models
for models whose name begins with "foo" and nuke each one of them. - For each model it did not target as nukeable in the previous step, it will scan
juju status -m that-model
and:- for each app whose name begins with "foo", it will nuke it.
- for each relation NOT involving an app selected for nukage in the previous step, if either the provider or requirer starts with "foo", it will nuke it.
You can switch between "starts with" / "ends with" and "contains" matching modes by placing stars around the string:
jhack nuke foo
--> same asjhack nuke foo*
jhack nuke *foo
--> same algorithm as above, but will nuke stuff whose name ends with "foo".jhack nuke *foo*
--> ... will nuke stuff that contains "foo"jhack nuke !foo
--> exact match only. So it will presumably only nuke one thing, except if you have many models with identically-named apps or relations in them. Then they'll all be vanquished.
For targeting relations only, you can type out the endpoint name up to and including the colon. For example, for purging all relations involving your my-db
application,
you could do:
jhack nuke "my-db:"
, that will match all the relations of your app. They're history now.
By using this tool you acknowledge the possibility of it bricking your model or controller. Hopefully nothing beyond that.
Safety tips
-
Learn to use the command by trying out the
--dry-run
flag first, that will print out what it would nuke without actually nuking anything. -
The command has an optional
-n
flag that allows you to specify the expected number of nukes that should be fired out. If more or less thann
nukeables are matched, the command will print an error message and abort. -
The command has a
--selectors
(-s
) option that can be used to specify what to include/exclude in the bombardment.- 'a' for apps, 'A' for all except apps
- 'm' for models, 'M' for all except models
- 'r' for relations, 'R' for all except relations (although, the resulting nuke will probably also wipe the relations that would have been matched had this flag been omitted)
So, for example,
jhack nuke -s M foo
will nuke all apps and relations it can find matching 'foo', equivalent tojhack nuke -s ar foo
.
model
clear
jhack model clear
Will nuke all applications in the current model.
rm
jhack model rm
Will nuke the current model.
charm
update
Updates a packed .charm file by dumping into it any number of directories.
jhack charm update ./my_charm_file-amd64.charm --src ./src --dst src
This will take ./src and recursively copy it into the packed charm's /src dir (it will destroy any existing content).
sync
Like update, but keeps watching for changes in the provided directories and pushes them into the packed charm whenever there's one.
jhack charm sync ./my_charm_file-amd64.charm --src ./src --dst src
repack
Used to pack a charm and refresh it in a juju model. Useful when developing.
If used without arguments, it will assume the cwd is the charm's root, will run
charmcraft pack
, and grab the application name from the charm's name.
jhack charm repack
Otherwise, you can specify a folder where the packing should be done, and an application name to target with the refresh.
jhack charm repack --root /where/my/charm/root/is --name juju-app-name
jinx
Used to play around with jinx (YAMLess Charms)
install
jhack jinx install
Downloads the jinx source.
init
jhack jinx init
Basically jinxcraft init
.