Repository navigation
fix!: ops.hookcmds returns all the data that the hook commands output - #2805
Open
tonyandrewmeyer wants to merge 6 commits into
Open
tonyandrewmeyer wants to merge 6 commits into
tonyandrewmeyer wants to merge 6 commits into
Conversation
Compare every hook command's output with Juju 2.9, 3.6, and 4 (from the Juju source and live runs), and fix the places where ops.hookcmds dropped or mis-parsed data: - status_get(app=True) always returned no units, because Juju nests the unit statuses inside "application-status" rather than at the top level. - secret_info_get ignored the secret's owner and the access grants. Add SecretInfo.owner and SecretInfo.access, with a new SecretAccess type. - opened_ports(endpoints=True) mis-parsed ports with more than one endpoint, because Juju separates the endpoints with ", ". - SecretInfo.description and label could be None despite being typed str. Also: - Deprecate secret_set's owner argument and stop passing --owner, as Juju ignores it when updating a secret. - Add config_get(all=True) and state_get(strict=True) for the matching Juju flags. - Allow the CRITICAL level in juju_log, include None in the return types of config_get(key) and relation_get(key=...), give secret_revoke's relation_id a default, and correct relation_list's internal annotations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove config_get's all and state_get's strict arguments again: both were deliberately left out, as there's no use-case for them. Add a comment for state_get matching the existing one for config_get, and a comment listing the hook commands that deliberately have no wrapper. When a hook command doesn't exist (for example, the secret commands and relation-model-get on Juju 2.9), ops.hookcmds now raises hookcmds.Error with return code 127 and the FileNotFoundError as the cause, rather than letting the FileNotFoundError escape. This is a breaking change for code that catches FileNotFoundError from ops.hookcmds directly. The model backend converts the error back to the original FileNotFoundError, so ops.Model behaviour is unchanged. Extend the hookcmds integration tests to cover the fixes: the app status units, the secret owner and access grants, the deprecated secret_set owner, unset secret labels and descriptions, ports with more than one endpoint, missing config and relation keys, and logging at the CRITICAL level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use the deprecated directive for secret_set's owner argument, as the rest of ops does, explain why the model backend maps an empty secret description to None, and use the active voice in relation_get's docstring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tonyandrewmeyer
marked this pull request as ready for review
October 8, 2026 04:44
hookcmds gives '' for a missing label, the same as it does for a missing description, but ops.model.SecretInfo has always used None (and both testing backends still do). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ng hook command The command was never run, so the generic message was misleading in juju debug-log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Matches the SecretRotate precedent in the same module. Juju 3.6 has view, rotate and manage; Juju 4 drops rotate. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This fixes the places where
ops.hookcmdsdrops or mis-parses data that Juju's hook commands output. I found them by comparing every hook command in the Juju 2.9, 3.6, and main (4.x) source with what we parse, and then running each command against real controllers (details below).status_get(app=True)always returnedunits={}. Juju nests the unit statuses insideapplication-statusrather than at the top level - our test (and theAppStatusDicttype) had the wrong shape, which is why it passed.secret_info_getignoredowner(Juju 3.1+) andaccess(Juju 3.4+, the list of grants, omitted when there are none). These are nowSecretInfo.ownerandSecretInfo.access, with a newSecretAccessclass for the grants.opened_ports(endpoints=True)mis-parsed ports with more than one endpoint, because Juju separates the endpoints with", ":80/tcp (db, web)came back asPort(protocol='tcp (db,', port=80, endpoints=['web']). The test used(ep1,ep2), which Juju never produces.SecretInfo.descriptionwasNonerather than''when Juju omits it (andlabelwould have been too, although Juju always sends that).ops.model.SecretInfostill getsNonefor a missing description, as before.secret-setaccepts--ownerbut ignores it (in every 3.x release and in 4), so theownerargument tosecret_setcould never do anything. It's now deprecated, and we no longer pass--owner. The flag is only there becausesecret-setshares its flags withsecret-add(since unit-owned secrets arrived in Juju 3.0):secret-setchecks the value and then drops it, so this never worked, rather than being a deprecation we missed.relation_model_getand thesecret_*functions raised a bareFileNotFoundErrorrather thanhookcmds.Error, because the commands don't exist. A missing hook command now raiseshookcmds.Error, with return code 127 (the shell's "command not found" code) and theFileNotFoundErroras the cause. That's a breaking change for anyone catchingFileNotFoundErrorfromops.hookcmds(which I believe is acceptable), butops.modelconverts it back, soops.Modelbehaves as before (I would change this in 4.0).juju_logacceptsCRITICAL(ops.modelalready passed it, with atype: ignore),config_get(key)andrelation_get(key=...)can returnNone(Juju outputsnullfor a missing key),secret_revoke'srelation_idhas a default, andrelation_list's internal annotations were swapped.There's one small
ops.modelchange: the pre-3.6 metadata fix insecret_setno longer passes an empty--label. Juju ignores an empty label, so this isn't a behaviour change, but it keeps the existing tests valid now that hookcmds gives''rather thanNone.Other than that,
ops.Modelbehaves as before, so I haven't done a Hyrum run.How I tested things
For the live testing, I used a Multipass VM per Juju version (2.9.60, 3.6.29, and 4.0.15, all on LXD), and deployed a minimal machine charm: a bash
dispatch, a peer relation, a requires/provides pair related to a second application, an extra binding, filesystem storage, a file resource, config options with and without defaults, and an action. Each application had two units. I then usedjuju exec(juju runon 2.9) to run each hook command raw with--format=jsonalongside the matchingops.hookcmdscall, on both the leader and a non-leader. That covered config-get, credential-get, goal-state, is-leader, network-get (including-r), opened-ports (with and without--endpoints), relation-ids, relation-list, relation-get, relation-model-get, resource-get, state-get, status-get (unit and application), storage-list, storage-get, and the secret commands (application-owned and unit-owned secrets, a relation grant and a single-unit grant,secret-info-getby ID and by label, andsecret-set --owner). On 3.6, I also checked action-get and storage-get without-sfrom within the action and storage-attached hooks.Without this change, the status, opened-ports, and secret problems reproduced on all three versions (other than the secrets ones on 2.9, which doesn't have secrets). With this branch, the same probe gives the expected values everywhere: the units are in
AppStatus.units, the endpoints are['db', 'web'],SecretInfohas the owner and grants (for exampleSecretAccess(target='unit-b-0', scope='relation-a.db#b.web', role='view')) on 3.6 and 4.0, andrelation_get(key=...)returnsNonefor a missing key. On 3.6 and 4.0,secret_set(owner=...)warns and the secret's owner staysunit, and on 4.0 aCRITICALjuju_logshows up injuju debug-logat that level. Everything else already matched Juju's output.I've also extended the
ops.hookcmdsintegration tests to cover each of these fixes, other than the Juju 2.9 one, as CI doesn't test against 2.9. They pass on my fork on machine and Kubernetes, with Juju 3.6, 4.0 (stable and edge), and 4.1 (edge).Follow-up work:
ops.model.SecretInfodoesn't haveowneroraccesseither. Adding those seems like a separate feature PR, since it also needsops.testingsupport.