Skip to content

docs: grab workload version in k8s tutorial#2559

Open
tromai wants to merge 33 commits into
canonical:mainfrom
tromai:grab-workload-version-in-k8s-tutorial
Open

docs: grab workload version in k8s tutorial#2559
tromai wants to merge 33 commits into
canonical:mainfrom
tromai:grab-workload-version-in-k8s-tutorial

Conversation

@tromai

@tromai tromai commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Closes #2282.

This PR modify the From zero to hero: Write your first Kubernetes charm tutorial:

  • Added a section in Chapter 1: creating a helper module for the charm to interact with the workload application. This is inspired by what we have in the machine charm tutorial. The helper module contains the function to get the version of the FastAPI web server.
  • Added another section in Chapter 1: exposing this version as the workload version to Juju (following this guide).
  • Added unit tests and integration tests to cover the above logic.
  • Propagate the changes to the other chapters.

The PR reviewer can focus on the Markdown docs. We can leave the code changes in examples/ at the end, when we have finalized the tutorial, as the changes in examples/ are plenty and mostly reflect the tutorial.

@tromai tromai self-assigned this Jun 10, 2026
tromai added 3 commits June 11, 2026 17:02
port the grab workfload version code to tutorial 2->5 and use auto fixture for patching get_version in unit tests

and also change localhost -> 0.0.0.0 in the fastapi_demo.py modules in examples
Comment thread examples/k8s-1-minimal/tests/unit/test_charm.py Outdated
@tromai

tromai commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

While working on this task, I noticed that in Chapter 5, we introduced requests for the integration tests. However, we didn't mention adding this dependency using uv add or modifying pyproject.toml. Please let me know if this is a real gap in the doc, and I can fix it in this PR too.

@tromai tromai added the rainy day Small items done in ~10% of each week's time label Jun 15, 2026
@tromai tromai marked this pull request as ready for review June 15, 2026 06:49
@dwilding

Copy link
Copy Markdown
Contributor

While working on this task, I noticed that in Chapter 5, we introduced requests for the integration tests. However, we didn't mention adding this dependency using uv add or modifying pyproject.toml. Please let me know if this is a real gap in the doc, and I can fix it in this PR too.

Oops, you're quite right. Well spotted! In the tutorial we say "Then import pytest, pytest_jubilant, and requests" - but only the first two are predefined dependencies in the Charmcraft profile.

@tromai

tromai commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

While working on this task, I noticed that in Chapter 5, we introduced requests for the integration tests. However, we didn't mention adding this dependency using uv add or modifying pyproject.toml. Please let me know if this is a real gap in the doc, and I can fix it in this PR too.

Oops, you're quite right. Well spotted! In the tutorial we say "Then import pytest, pytest_jubilant, and requests" - but only the first two are predefined dependencies in the Charmcraft profile.

Thanks for the confirmation David. I added the step to run uv add requests in the tutorial 80bbc83

@dwilding dwilding left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a serious chunk of work - thanks for working through it so thoroughly! It's looking great. I'm sending a first batch of comments and will hold off reviewing further until you've had a chance to think about them.

@james-garner-canonical james-garner-canonical left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good. I agree with David's suggestions.

@tromai

tromai commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Thanks everyone for the reviews. I really appreciate it !

I will address the feedback

@dwilding dwilding left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm holding off approving until the collect-status question is settled, but the latest changes look good 🙂

I haven't cross-checked the doc pages against the charm source. I'd be very happy to do that as a final review step.

@dwilding dwilding left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice indeed! I cross-checked the details and found a couple of small adjustments needed.

Comment thread examples/k8s-5-observe/tests/unit/test_charm.py

@james-garner-canonical james-garner-canonical left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good, it's nice to add this feature to the K8s tutorial (and more complicated to do so than in the machine tutorial both due to the container architecture and the tutorial structure).

I have a few questions and suggestions. @dwilding could you please take a look at my suggestions too in case your opinion differs.

Comment on lines +239 to 251
return
try:
version = fastapi_demo.get_version(config.server_port)
except RuntimeError as version_e:
logger.error("Failed to get workload version: %s", version_e)
self.unit.status = ops.BlockedStatus(
"Failed to get version from server: check port config"
)
return
self.unit.set_workload_version(version)
```

We removed three `self.unit.status = ` lines from this version of the method. We'll handle replacing those shortly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following text for this code block is now kind of contradictory -- it's explaining that we removed self.unit.status = lines (presumably switching to the collect status pattern), but we're conspicuously leaving a self.unit.state = ops.BlockedStatus without further explanation.

If this is the right pattern, then I think it could use some explanation as to why it's not replaced.

But is it the right pattern? It looks like our collect status handler will happily clobber this blocked status with active status.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But is it the right pattern? It looks like our collect status handler will happily clobber this blocked status with active status.

Is that true? I had assumed that the collect-status handler wouldn't override higher priority status with a lower priority one - but I actually don't know.

In tutorial text we say:

We also want to clean up the code to remove the places where we're setting the status outside of this method, other than anywhere we're wanting a status to show up during the event execution (such as MaintenanceStatus).

Which suggested to me that a maintenance status would survive collect-status. And so presumably blocked status would too.

I think it would be good to test this to confirm.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh but I suppose the point is that maintenance status gets overridden after the work in the event execution has completed. Hmm

Comment on lines +142 to +143
self.unit.status = ops.ActiveStatus()
self.unit.set_workload_version(version)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor, but perhaps self.unit.status = ops.ActiveStatus() should be the very last line?

@tromai tromai Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, initially self.unit.status = ops.ActiveStatus() is the last line.

However, we decided that putting self.unit.set_workload_version(version) last would simplify the tutorial, and went with it.

In chapter 1, section Set the workload version, we can simply write

In `src/charm.py`, append the following lines to the `_on_demo_server_pebble_ready` function:

```python
# Set the workload version of this charm.
version = fastapi_demo.get_version(port=8000)
self.unit.set_workload_version(version)
```

And this code propagated into Chapter 2 as you see here, and I kept it like that.

Comment thread examples/k8s-1-minimal/src/fastapi_demo.py Outdated
Comment thread examples/k8s-1-minimal/src/fastapi_demo.py Outdated
Comment thread examples/k8s-2-configurable/src/charm.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rainy day Small items done in ~10% of each week's time

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Grab workload version in K8s tutorial

4 participants