operator icon indicating copy to clipboard operation
operator copied to clipboard

Expose in ops a jhack-eval-like entrypoint

Open PietroPasotti opened this issue 1 year ago • 8 comments

jhack eval is a command that allows you to evaluate python expressions in the context of a charm, without having to wait for the charm to receive an event.

jhack eval traefik/0 "self.unit.name" will print to stdout traefik/0.

This has proved to be extremely useful in debugging and troubleshooting. It is implemented by scp'ing a file into the unit and then juju-exec'ing it. The file contains much of the machinery in ops.main.main, but never calls the _emit_charm_event function; instead, it eval's the user-provided expression while binding the following globals:

  • self: the charm instance
  • ops: the ops module, to let the user evaluate expressions such as setattr(self.unit.status, ops.ActiveStatus())
  • json: the json module, to let the user output json instead of plain str/repr.

In certain environments where jhack is not available, it would be handy to have a way to ask the user to evaluate an expression for debugging purposes, to inspect the charm's current state outside of what the logging output shows. It would be fantastic if we could offer a way for the user to achieve the same functionality without much hackery.

My proposal is to add to ops.main logic to skip event emission if the charm is being executed with a special envvar set.

The user would need to run: juju exec --unit traefik/0 OPS_EVAL_EXPR="self.unit.name" ./dispatch

(or something like that).

When called with that envvar set, ops.main will not emit an event on the charm, but instead will evaluate the provided exception while binding the same globals that jhack exposes.

see https://github.com/canonical/operator/pull/1450 for a proof of concept implementation

PietroPasotti avatar Nov 06 '24 08:11 PietroPasotti

I would have thought that setattr(self.unit.status, ops.ActiveStatus()) wouldn't actually work outside the context of a hook. Am I missing something?

dimaqq avatar Nov 07 '24 00:11 dimaqq

@dimaqq No, it looks like the status-set hook tool works fine outside the context of a hook (at least with juju exec):

$ juju exec --unit snappass-test/0 -- status-set blocked 'blocky!'
$ juju status
Model  Controller          Cloud/Region        Version    SLA          Timestamp
t      microk8s-localhost  microk8s/localhost  3.6-beta1  unsupported  15:47:44+13:00

App            Version  Status   Scale  Charm          Channel        Rev  Address         Exposed  Message
snappass-test           waiting      1  snappass-test  latest/stable    9  10.152.183.112  no       installing agent

Unit              Workload  Agent  Address       Ports  Message
snappass-test/0*  blocked   idle   10.1.164.163         blocky!

benhoyt avatar Nov 07 '24 02:11 benhoyt

This is an interesting idea, though I wonder about the UI. On the one hand, it's nice that

juju exec --unit traefik/0 OPS_EVAL='self.unit.name' ./dispatch

would work out of the box. But that command is also non-obvious (documentation could solve that) and awkward (why the environment variable? what is ./dispatch doing there?).

What if it was a two-step thing, where you jhack install-ops-eval first once, and then you could run:

juju exec --unit traefik/0 ops-eval 1+1

Alternatively, what if charmcraft included an ops-eval script in every charm? Is that even feasible? (CC: @lengau) Then you'd have to use ./ I think:

juju exec --unit traefik/0 ./ops-eval 1+1

The other question I had is whether it's eval or exec that you want. setattr(self.unit.status, ops.ActiveStatus()) (eval) is an awkward way to say self.unit.status = ops.ActiveStatus() (exec). If we had ops-exec instead of ops-eval, then you'd just have to wrap expressions in print(...), but the other way around is harder.

What expressions/commands do you (or others) typically evaluate/run with jhack eval?

benhoyt avatar Nov 07 '24 03:11 benhoyt

The whole point of this exercise is to allow people to 'eval' without needing jhack since the environment could be locked down. so jhack install-ops-eval is a no-go; and also the only thing that does is juju scp a python script into the unit, so at that point we could also simply instruct to do that manually.

As to including it in the packed charms by default, that's not a bad idea but also it's not ideal that it wouldn't work with existing deployments.

Common workflows:

  • jhack eval foo/0 json.dumps(self._generate_some_config(), indent=2) and inspect the resulting json
  • jhack eval foo/0 self._refresh_all_relations() and watch the charm update its databags
  • jhack eval foo/0 self.workload_manager.restart() to restart the services
  • jhack eval foo/0 self._some_private_object.foo to inspect the values of variables

PietroPasotti avatar Nov 07 '24 08:11 PietroPasotti

As to including it in the packed charms by default, that's not a bad idea but also it's not ideal that it wouldn't work with existing deployments.

But isn't that true of any update to ops? Any changes to ops will require a newly-packed charm, right?

Thanks for the list of common workflows.

benhoyt avatar Nov 07 '24 22:11 benhoyt

To the point of the ops-eval script, I'm not sure we'll get buy-in for adding it in Charmcraft. However, I don't see any reason why any one of a charm's dependencies can't include a module that presents an ops-eval entrypoint in the venv's bin directory.

lengau avatar Dec 13 '24 00:12 lengau

We think this area is worth exploring. However, there are a number of UX and technical complexities, so we'd rather this started with a more formal proposal and spec. If you're incentivized to do this, go for it. I'll leave this issue open in the meantime.

Another approach would be an always-present "_eval" or "debug-eval" action that's implemented by CharmBase.

benhoyt avatar Mar 21 '25 00:03 benhoyt

I was unclear who "you're" referred to. @PietroPasotti :-), if you're incentivized to write a spec for this, go ahead and we'll consider it.

benhoyt avatar Mar 25 '25 01:03 benhoyt

We're going to leave this type of functionality to jhack, and close this issue. However, if you'd like to reopen this, please write a more formal spec and we'll go from there.

benhoyt avatar Sep 09 '25 02:09 benhoyt