canonical / ops-scenario

State-transition testing SDK for Operator Framework Juju charms.
Apache License 2.0
10 stars 7 forks source link

Support for container pebble layer defined externally #152

Closed dimaqq closed 2 months ago

dimaqq commented 2 months ago

I have this setup:

rockcraft.yaml

services:
  gubernator:
    override: replace
    startup: enabled
    command: /bin/gubernator
    environment:
      GUBER_TRACING_LEVEL: ...
    on-success: shutdown
    on-failure: shutdown
    on-check-failure:
      online: shutdown

When juju starts a container with the image built from this rock, pebble gets this plan, and is started on hold, then charm does does:

    def _on_gubernator_pebble_ready(self, event: ops.PebbleReadyEvent):
        event.workload.replan()
        self.unit.status = ops.ActiveStatus()

Meanwhile in unit tests, scenario.mocking._MockPebbleClient has a concept of containers, but is not instantiated with the layer data.

Which makes it kinda hard to test things, apart from happy path.

I wonder how to best represent this.

tonyandrewmeyer commented 2 months ago

Can't you provide the plan as a layer in the container? Like:

container = Container("gubernator", layers={"rock": pebble.Layer(...)})

The rockcraft approach is perhaps an argument that _base_plan shouldn't be private. It would be nice if we had some documentation on using the rock's Pebble with charms (I can't find anything at all on juju.is) - that would help be clear on what's expected, and whether a public base_plan makes sense.

dimaqq commented 2 months ago

That may just work, let me wrap up a prototype to share here.

dimaqq commented 2 months ago

Thank you, I can surely instantiate container with pebble layers. \ The next question is though, how to observe the (internal?) mock pebble status update in scenario?

Here's what I have so far:

https://github.com/dimaqq/hexanator/commit/8a4ed2781a7fb799def91253c2d129402cb71333

TL;DR:

tonyandrewmeyer commented 2 months ago

Yeah, that's where #34 comes in. Right now, the Scenario testing Pebble client doesn't initialise the service status dict so even though the methods are inherited they won't work, if I recall correctly.

So the most straightforward solution is to just mock the restart and assert that it's called. If you really want to avoid mocks then you could do it all with the ops class, I think - before running the event do mgr.charm.unit.containers['...'].stop('...') then run it and then do mgr.charm.unit.containers['...'].get_service('...') and assert on the result. However, I'm not sure that will actually run with Scenario not initialising the internal dict. So it may be that it's Harness or mocking.

dimaqq commented 2 months ago

Ugh, I had a silly bug...

It turns out that I can in fact read back the container status:

https://github.com/dimaqq/hexanator/commit/9358ef82cad95e53c557a0ebc53e25ba0a451266

tonyandrewmeyer commented 2 months ago

The rockcraft approach is perhaps an argument that _base_plan shouldn't be private. It would be nice if we had some documentation on using the rock's Pebble with charms (I can't find anything at all on juju.is) - that would help be clear on what's expected, and whether a public base_plan makes sense.

For what it's worth, if this "use the rock plan" approach is documented as a way people should make charms, then I do think _base_plan (maybe under another name?) maybe should be public.