Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

describe and implement the external protocol #94

Merged
merged 5 commits into from
Jun 7, 2024

Conversation

supakeen
Copy link
Member

Describes an initial external protocol.

@supakeen supakeen linked an issue May 28, 2024 that may be closed by this pull request
Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

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

Thank you for documenting this! Some inline questions/suggestions for your consideration. Hope they are useful.

doc/03-omnifest/02-external.md Outdated Show resolved Hide resolved
Directives are small executables that receive JSON and are expected to output
JSON again in a specific format. `otk` will look for these directives in
`/usr/libexec/otk`. Externals are explicitly mapped to executables in the
registry that is defined in `src/otk/external.py`.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure that this description of the "mapping" should be here. I think it's an implementation detail and I personally wold love to discuss if we cannot just auto-discover them. osbuild for example does not have a registry of stages and I'm not sure what it gives us that we have a registry. More control for sure but also less options for external people to develop their own externals. If we want to keep the registry in code maybe something like: Externals need official registration the otk source code to work or something (i.e. maybe more emphasis on the intend).

Copy link
Member Author

Choose a reason for hiding this comment

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

I've removed this section and we can do lookups as described in the new Paths section.

## Protocol

Directives are small executables that receive JSON and are expected to output
JSON again in a specific format. `otk` will look for these directives in
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure that the path should be under "protocol", maybe worth adding a ## Files section or something? And it might be nice to describe how to run them from a git checkout or via an env var. Maybe OTK_EXTERNAL_PATH?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added a Paths section that includes this information.


Directives are small executables that receive JSON and are expected to output
JSON again in a specific format. `otk` will look for these directives in
`/usr/libexec/otk`. Externals are explicitly mapped to executables in the
Copy link
Contributor

Choose a reason for hiding this comment

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

Do all distros nowdays have /usr/libexec (honest question, idk)

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good question. I don't know, what can we call it that would make sense across distributions?

Copy link
Member Author

Choose a reason for hiding this comment

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

Dove into this a bit; from a quick test it seems Debian, Ubuntu, RHEL (UBI), Fedora, CentOS, Gentoo all have /usr/libexec. Alpine and Arch do not.

We can either do our own search path (/usr/libexec and then /usr/lib, perhaps with /usr/local-variants for each) or let eventual packages for Alpine or Arch add a patch.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, I've updated the docs to search all relevant paths starting with an environment-defined one.

{
"tree": {
"otk.external.name": {
"child": [],
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand if "child" and "options" are examples or have a special meaning. Maybe above the json a yaml fragment could be added that has the yaml source from the otk file and then here we see the json that is generated from that yaml. Does that make sense?

Copy link
Member Author

Choose a reason for hiding this comment

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

They are indeed examples without special meaning. I'll add a YAML blurb!

Copy link
Member Author

Choose a reason for hiding this comment

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

YAML blurb has been added.

doc/03-omnifest/02-external.md Show resolved Hide resolved
}
```

*If the external returns `{}`, an empty object, `otk` will assume that there is
Copy link
Contributor

Choose a reason for hiding this comment

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

Does that mean {} or {"tree": {}} ? I assume the former, what will happen if otk gets the later?

Copy link
Member Author

Choose a reason for hiding this comment

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

It means the former. If it gets the latter then it will put a {} (empty object) into the output, I'll add an example of that.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added YAML examples for this.

@supakeen supakeen force-pushed the describe-external-protocol branch from 2091935 to 00cbe3a Compare May 28, 2024 10:37
@supakeen supakeen changed the title doc: describe external protocol describe and implement the external protocol May 28, 2024
@supakeen supakeen force-pushed the describe-external-protocol branch from d9333c8 to d3ca93d Compare May 28, 2024 10:52
Comment on lines +12 to +32
When we have an [omnifest](./index.md) that looks like this:

```yaml
otk.external.name:
child:
- 1
options: "are here"
```

The external gets called with the following JSON:

```json
{
"tree": {
"otk.external.name": {
"child": [1],
"options": "are here"
}
}
}
```
Copy link
Member

Choose a reason for hiding this comment

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

Bit of a rephrase to avoid "you", "we", etc:


Given the following omnifest:

otk.external.custom-command:
  child:
    - 1
  options: "are here"

The external with name custom-command gets called with the following JSON as input:

{
  "tree": {
    "otk.external.custom-command": {
      "child": [1],
      "options": "are here"
    }
  }
}

The reference to the name custom-command isn't the point here but I think repeating rules in various sections helps reinforce them. In other words, it's a reminder that the <something> part of otk.external.<something> is both significant and arbitrary (as far as the internals are concerned) and only needs to match the name of an external executable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Super nitpick - the initial part of the suggestion to change When we have an [omnifest](./index.md) that looks like this: -> Given the following [omnifest](https://github.com/osbuild/otk/pull/94/index.md): seems to be gotten lost (which is not a big deal but I wanted to mention it :)

doc/03-omnifest/02-external.md Show resolved Hide resolved
Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

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

Thank you! I like it, still some ideas/questions/suggestions inline, hope they are useful :)

}
```

And is expected to return the following JSON:
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe And is expected to return a JSON dict with a single top-level "tree" object that can contain arbitrary values. Additional top-level keys are not allowed. Example:

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, this says "Done" but GH shows me the original version, is that intentional?

Copy link
Member Author

Choose a reason for hiding this comment

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

Some commit seems to have gone missing, really done now :)

doc/03-omnifest/02-external.md Show resolved Hide resolved
}
```

Which will lead to the following YAML structure:
Copy link
Contributor

@mvo5 mvo5 May 28, 2024

Choose a reason for hiding this comment

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

Maybe: Which will replace the previous otk.external.name subtree with the output of new subtree from the external comand. Example:

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to above, this says "Done" but AFAICT the text is unchanged, is it "done" in an indirect way?

Copy link
Member Author

Choose a reason for hiding this comment

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

Some commit seems to have gone missing, really done now :)

doc/03-omnifest/02-external.md Show resolved Hide resolved
doc/03-omnifest/02-external.md Show resolved Hide resolved
- `/usr/lib/otk`

The filename for an external executable is based on the external name. When the
following directive is encountered: `otk.external.<name>.<argument>` then
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have an example for <argument> ? I'm not sure I follow why we need this mapping. As a stawman, maybe something very simple. The search path is the same as above but contains "/external/" as the last component (so /usr/libexec/otk/external" and then just `osbuild_dnf4" ? Or are we limiting ourselfs here is do something very simple like this?

Copy link
Member

@achilleas-k achilleas-k May 28, 2024

Choose a reason for hiding this comment

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

I think in the interest of keeping things simple for the initial implementation, we can avoid adding features that can be expressed easily with other features, especially if the alternative isn't too complicated. A subcommand here can easily be expressed using an option for the external.

...
  otk.external.myscript.subcommand_1:
    option: "value"

is not that much cleaner than:

...
  otk.external.myscript:
    command: "subcommand_1"
    option: "value"

I think the fact that the first form requires an extra paragraph in the specification and docs is a sign that it's not necessary given how the alternative isn't really that complicated.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't have a direct example except that it might help people with code organisation and reuse. I'll go with the simple implementation (no subcommands).

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.


- `otk.external.foo` -> `otk_external_foo`
- `otk.external.foo.bar_baz` -> `otk_external_foo bar_baz`
- `otk.external.foo.bar.baz` -> `otk_external_foo bar baz`
Copy link
Member

Choose a reason for hiding this comment

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

Already I'm looking at this and thinking "is bar baz a single argument with a space or two args?"
If it's the former, how do I do two args?
If it's the latter, how do I do args with spaces?

Neither of these questions exist if the specification doesn't allow arguments in the otk.external directive's name and subcommands/arguments just become part of the input.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, I'm going to drop it, it's confusing.

Copy link
Member Author

Choose a reason for hiding this comment

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

Dropped.

Copy link
Member

Choose a reason for hiding this comment

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

Still there?

Copy link
Member Author

Choose a reason for hiding this comment

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

Dropped, I think I only dropped it form the code implementation initially.

@supakeen supakeen force-pushed the describe-external-protocol branch 3 times, most recently from 8b148cc to 65e7d5c Compare June 3, 2024 11:00
@supakeen supakeen requested review from mvo5 and achilleas-k June 3, 2024 11:00
Copy link
Member

@achilleas-k achilleas-k left a comment

Choose a reason for hiding this comment

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

LGTM generally. Tiny correction for a duplicate line.
Also, the subcommand/argument stuff and rephrase from my previous comments seem unchanged?

and the following directive:

```yaml
and the following directive:
Copy link
Member

Choose a reason for hiding this comment

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

Duplicate line snuck into the yaml code block.

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed.


- `otk.external.foo` -> `otk_external_foo`
- `otk.external.foo.bar_baz` -> `otk_external_foo bar_baz`
- `otk.external.foo.bar.baz` -> `otk_external_foo bar baz`
Copy link
Member

Choose a reason for hiding this comment

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

Still there?

@supakeen supakeen force-pushed the describe-external-protocol branch from 65e7d5c to bded701 Compare June 3, 2024 14:34
achilleas-k
achilleas-k previously approved these changes Jun 3, 2024
Copy link
Member

@achilleas-k achilleas-k left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

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

Thanks for the changes, the README looks very nice now! Fine to merge IMHO.

I have some questions in the implementation though, maybe I'm misunderstanding something but it seems like the parsing of external names to an executable and args for the executable was removed from the spec but the code seems to be still doing it? Is that because the otk_external_osbuild is easier to implement this way? i.e. instead of looking at "os.argv[0]" how it got called it would be called via the argument magic from "convert()"? If so we should reconsider if we want it back in the spec or if we want to change the code to look at argv[0] but it seems right now there is a bit of a mismatch between spec and code.

Comment on lines +12 to +32
When we have an [omnifest](./index.md) that looks like this:

```yaml
otk.external.name:
child:
- 1
options: "are here"
```

The external gets called with the following JSON:

```json
{
"tree": {
"otk.external.name": {
"child": [1],
"options": "are here"
}
}
}
```
Copy link
Contributor

Choose a reason for hiding this comment

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

Super nitpick - the initial part of the suggestion to change When we have an [omnifest](./index.md) that looks like this: -> Given the following [omnifest](https://github.com/osbuild/otk/pull/94/index.md): seems to be gotten lost (which is not a big deal but I wanted to mention it :)

}
```

And is expected to return the following JSON:
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, this says "Done" but GH shows me the original version, is that intentional?

}
```

Which will lead to the following YAML structure:
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to above, this says "Done" but AFAICT the text is unchanged, is it "done" in an indirect way?

# TODO exception
sys.exit(1)
return
exe, args = convert(directive)
Copy link
Contributor

Choose a reason for hiding this comment

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

AFAICT we do not use "args" currently, maybe worth removing them until we need them? then this could become something like exe = exe_from_directive(directive) or exe = exe_from(directive)

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

src/otk/external.py Outdated Show resolved Hide resolved
src/otk/external.py Show resolved Hide resolved
src/otk/external.py Outdated Show resolved Hide resolved
test/test_external.py Outdated Show resolved Hide resolved
Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

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

Thanks for the update! I did a more careful review of the python code this time and noticed what appears to be a small oversight in the way the data from the external helper is loaded in call (hence the change request). My other comments are just ideas or suggestions :)

src/otk/external.py Outdated Show resolved Hide resolved
src/otk/external.py Outdated Show resolved Hide resolved
src/otk/external.py Outdated Show resolved Hide resolved
src/otk/external.py Show resolved Hide resolved
test/test_external.py Outdated Show resolved Hide resolved
src/otk/external.py Show resolved Hide resolved
@supakeen supakeen force-pushed the describe-external-protocol branch from 90b7c3e to cd31fcd Compare June 4, 2024 11:42
Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

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

Thanks for the updates! I went over it again and also updated the small integration test for the externals, I added checks in inputs too (sorry, forgot to do that yesterday). It's attached here:
0001-external-write-integration-test-for-the-happy-case.patch.txt

src/otk/external.py Show resolved Hide resolved
doc/03-omnifest/02-external.md Show resolved Hide resolved
@supakeen supakeen force-pushed the describe-external-protocol branch from cd31fcd to 32212f8 Compare June 6, 2024 09:17
@achilleas-k
Copy link
Member

So this PR is getting a bit hard to re-re-review after all the comments and follow-up commits. Would it be difficult to have the commit history cleaned up a bit? For example, 062ed90 could be squashed into 8e172c9 (the commit that drops the usage of the OSBuildContext). Smaller commits like the 4 that follow the first can be squashed into the first one that's the bulk of the doc writing. Stuff like that.

@supakeen supakeen force-pushed the describe-external-protocol branch from 4442ecb to ef0ef83 Compare June 6, 2024 09:58
Describes an initial external protocol.

Signed-off-by: Simon de Vlieger <[email protected]>
Updates the externals to use a search path for executables instead of a
registry of explicit conversions.

Signed-off-by: Simon de Vlieger <[email protected]>
Rename `otk-osbuild` to fit in with the new specification.

Signed-off-by: Simon de Vlieger <[email protected]>
@supakeen supakeen force-pushed the describe-external-protocol branch from ef0ef83 to 1863855 Compare June 6, 2024 10:08
Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

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

Thank you for the history cleanup! This looks pretty ready now, I have a few inline questions but those are no blockers so I'm fine approving if we want to do the open questions in a followup (happy to helper here of course :)

doc/03-omnifest/01-directive.md Outdated Show resolved Hide resolved
src/otk/external.py Outdated Show resolved Hide resolved
src/otk/external.py Outdated Show resolved Hide resolved
src/otk_external_osbuild/command.py Show resolved Hide resolved
Remove unused arguments from the external calls, rename search to
path_for to be more explicit about what the function does. Remove the
contexts as they are documented to not be available.

Also creates testcases for these functions.

Signed-off-by: Simon de Vlieger <[email protected]>
The repetitive names with `otk_external` can instead be encoded by
including an `external` in the search paths.

Signed-off-by: Simon de Vlieger <[email protected]>
@supakeen supakeen force-pushed the describe-external-protocol branch from 1863855 to 875aeb1 Compare June 7, 2024 09:27
Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

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

Thank you!

@supakeen supakeen added this pull request to the merge queue Jun 7, 2024
Merged via the queue into osbuild:main with commit 66da377 Jun 7, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Define the otk external protocol in documentation
3 participants