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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .spellcheck-en-custom.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ kickstarts
koji
livemediacreator
untrusted
executables
subtree
4 changes: 2 additions & 2 deletions doc/03-omnifest/01-directive.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,8 @@ otk.meta.kiwi:
## `otk.external`

External directives. Directives starting with `otk.external` are redirected
to `/usr/libexec/otk/`-binaries. For example the directive
`otk.external.osbuild.depsolve_dnf4` will execute `otk-osbuild depsolve_dnf4`
to `/usr/libexec/otk/external`-binaries. For example the directive
`otk.external.osbuild_depsolve_dnf4` will execute `osbuild_depsolve_dnf4`
with the tree under the directive on stdin and expect a new tree to replace
the directive with on stdout.

Expand Down
163 changes: 157 additions & 6 deletions doc/03-omnifest/02-external.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,173 @@
# External

## osbuild
External directives are directives that are implemented externally from `otk`.
They are meant to be used to provide target-specific behavior and can be used
to express things where `otk` is not expressive enough.

## Protocol

Directives are small executables that receive JSON and are expected to output
JSON again in a specific format. Additional keys are not allowed.

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": {
mvo5 marked this conversation as resolved.
Show resolved Hide resolved
"child": [1],
"options": "are here"
}
}
}
```
Comment on lines +12 to +32
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 :)


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:

```json
{
"tree": {
mvo5 marked this conversation as resolved.
Show resolved Hide resolved
"what": {
"you": "want"
}
}
}
```

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

```yaml
tree:
mvo5 marked this conversation as resolved.
Show resolved Hide resolved
what:
you: "want"
```

If the external returns `{}`, an empty object, `otk` will assume that there is
mvo5 marked this conversation as resolved.
Show resolved Hide resolved
no tree to replace and remove the node instead. This will turn the following
YAML:

```yaml
dummy:
otk.external.name:
options:
```

Into this YAML structure:

```yaml
dummy:
```

mvo5 marked this conversation as resolved.
Show resolved Hide resolved
## Example

The following example demonstrates how an external can be used to implement string
concatenation.

Given the following script called `concat` in `/usr/local/libexec/otk/concat`:

```bash
#!/usr/bin/env bash

output="$(jq -jr '.tree."otk.external.concat".parts[]' <<< "${1}")"
echo "{\"tree\":{\"output\":\"$output\"}}"
```

and the following directive:

```yaml
examplestring:
otk.external.concat:
parts:
- list
- of
- strings
```

the script is called with the following data on stdin:

```json
{
"tree": {
"otk.external.concat": {
"parts": [
"list",
"of",
"strings"
]
}
}
}
```

which results in the following output:

```json
{
"tree": {
"string": "listofstrings"
}
}
```

and the final omnifest will be:

```yaml
examplestring:
listofstrings
```

## Paths

`otk` will look for external directives in the following paths, stopping when
it finds the first match:

- A path defined by the `OTK_EXTERNAL_PATH` environment variable.
- `/usr/local/libexec/otk/external`
- `/usr/libexec/otk/external`
- `/usr/local/lib/otk/external`
- `/usr/lib/otk/external`

The filename for an external executable is based on the external name. When the
following directive is encountered: `otk.external.<name>` then
`otk` will try to find an executable called `<name>` in the previously
mentioned search paths.

Examples:

- `otk.external.foo` -> `foo`
- `otk.external.osbuild_bar` -> `osbuild_bar`

## Implementations

`otk` currently ships together with an external implementation for `osbuild`.
These directives can be used as children under an `otk.target.osbuild` tree.

### osbuild

These directives are only allowed within a [`otk.target.osbuild.<name>`](./01-directive.md#otktargetconsumername).

### `otk.external.osbuild.depsolve_dnf4`
#### `otk.external.osbuild_depsolve_dnf4`

Solves a list of package specifications to RPMs and specifies them in the
osbuild manifest as sources.

### `otk.external.osbuild.depsolve_dnf5`
#### `otk.external.osbuild_depsolve_dnf5`

Expects a `map` as its value.

`osbuild` directives to write files. **If a stage exists for the type of file
you want to write: use it.** See the [best practices](../04-best-practices.md).

### `otk.external.osbuild.file_from_text`
#### `otk.external.osbuild_file_from_text`

Write inline text to a file. Creates a source in the manifest and copies that
source to the destination in the tree.
Expand All @@ -30,15 +181,15 @@ otk.external.osbuild.file_from_text:
Hello, World!
```

### `otk.external.osbuild.file_from_path`
#### `otk.external.osbuild_file_from_path`

Copy a file. Source is relative to the path of the entrypoint omnifest. Creates
a source in the manifest and copies that source to the destination in tree.

Path components up to the destination must be pre-existing in the tree.

```yaml
otk.external.osbuild.file_from_path:
otk.external.osbuild_file_from_path:
source: README.md
destination: /path/to
```
2 changes: 1 addition & 1 deletion example/centos/pipeline/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: build
source-epoch: ${source_epoch}
runner: "org.osbuild.centos9"
stages:
- otk.external.osbuild.depsolve_dnf4:
- otk.external.osbuild_depsolve_dnf4:
architecture: ${architecture}
releasever: ${version}
module_platform_id: platform:el${version}
Expand Down
2 changes: 1 addition & 1 deletion example/centos/pipeline/os-ostree.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ build: "name:build"

stages:
# Install RPMs
- otk.external.osbuild.depsolve_dnf4:
- otk.external.osbuild_depsolve_dnf4:
architecture: ${architecture}
releasever: ${version}
module_platform_id: platform:el${version}
Expand Down
2 changes: 1 addition & 1 deletion example/centos/pipeline/os.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ build: "name:build"

stages:
# Install RPMs
- otk.external.osbuild.depsolve_dnf4:
- otk.external.osbuild_depsolve_dnf4:
architecture: ${architecture}
releasever: ${version}
module_platform_id: platform:el${version}
Expand Down
2 changes: 1 addition & 1 deletion example/fedora/osbuild/buildroot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: buildroot
source-epoch: 1659397331
stages:
- otk.external.osbuild.depsolve_dnf4:
- otk.external.osbuild_depsolve_dnf4:
architecture: ${architecture}
releasever: "40"
module_platform_id: f${version}
Expand Down
2 changes: 1 addition & 1 deletion example/fedora/osbuild/pipeline/raw.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: raw
source-epoch: 1659397331
stages:
# - otk.external.osbuild.depsolve_dnf4:
# - otk.external.osbuild_depsolve_dnf4:
# architecture: ${architecture}
# module_platform_id: f${version}
# repositories: ${repositories}
Expand Down
2 changes: 1 addition & 1 deletion example/fedora/osbuild/pipeline/tree.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: tree
build: name:root
stages:
- otk.external.osbuild.depsolve_dnf4:
- otk.external.osbuild_depsolve_dnf4:
architecture: ${architecture}
releasever: "40"
module_platform_id: f${version}
Expand Down
2 changes: 1 addition & 1 deletion otk.spec
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ cp -a example/* %{buildroot}%{_datadir}/otk/example/
%doc doc
%{_datadir}/otk
%{_bindir}/otk
%{_bindir}/otk-osbuild
%{_bindir}/otk_external_osbuild
%{python3_sitelib}/otk
%{python3_sitelib}/otk_osbuild
%{python3_sitelib}/otk-%{version}.dist-info
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dependencies = [

[project.scripts]
otk = "otk.command:root"
otk-osbuild = "otk_osbuild.command:root"
otk_external_osbuild = "otk_external_osbuild.command:root"

[project.optional-dependencies]
dev = [
Expand Down
2 changes: 2 additions & 0 deletions src/otk/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
PREFIX_INCLUDE = f"{PREFIX}include"
PREFIX_DEFINE = f"{PREFIX}define"

PREFIX_EXTERNAL = f"{PREFIX}external."

NAME_VERSION = f"{PREFIX}version"
Loading
Loading