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

docs: plugin concept docu #1122

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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 .github/config/markdownlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ MD013: false #Line length - https://github.com/DavidAnson/markdownlint/blob/HEAD
MD033: false #Inline HTML - https://github.com/DavidAnson/markdownlint/blob/HEAD/doc/Rules.md#md033
MD024:
siblings_only: true #Multiple headers with the same content
MD010:
code_blocks: false
2 changes: 2 additions & 0 deletions .github/config/wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ ociregistry
ocm
ocmbot
ocmcli
ocmcmds
ocmconfig
oncefunc
oo
Expand Down Expand Up @@ -228,6 +229,7 @@ pubsub
readme
readonly
refcount
registerextensions
relativeocireference
releasenotes
releaser
Expand Down
151 changes: 151 additions & 0 deletions api/ocm/plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# The OCM Library Plugin Concept

The ocm library supports a plugin mechanism to provide further variants
for OCM and library extension points without the need of extending and recompiling
the OCM CLI (and other applications using this library).
Comment on lines +3 to +5
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The ocm library supports a plugin mechanism to provide further variants
for OCM and library extension points without the need of extending and recompiling
the OCM CLI (and other applications using this library).
The OCM library supports a plugin mechanism to provide further library extension points without the need of extending and recompiling OCM (and other applications using this library).


This plugin concept is not part of the OCM specification, because it is
a feature of this library implementation.

The following extension points are supported:

- Access methods
- Uploaders
- Downloaders
- Actions
- Value sets (for example for routing slip entries)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we have routing slips in OCM?

- Config types
- Value Merge Handler (for example for label values in delta transports)
Copy link
Contributor

Choose a reason for hiding this comment

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

What does this mean? I am missing explanation on what these points should be able to do. Is that available somewhere else? if so we should link it here I think

- CLI Commands (for OCM CLI)
Copy link
Contributor

Choose a reason for hiding this comment

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

I dont understand why we can add CLI Commands to a plugin infrastructure that is supposedly an interface of the library. To me that means the plugin system is not confined to the library, but to the entire codebase.
For me that means either that

  • the whole plugin system is across the entirety of the OCM codebase
  • we have 2 separate plugin systems that need to be documented separately and be detached from each other

- (transfer handlers)
- (signing tools)
- (input types for component version composition)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- (input types for component version composition)
- (new input types to compose component versions)


## Plugin Technology

A plugin is a simple executable, which might be written in any program language.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
A plugin is a simple executable, which might be written in any program language.
A plugin is an executable written in any programming language.

The plugin has to provide a set of CLI commands for every extension point it provides new variations for.

The data transfer between the library and the plugin is done via

- command options (for inbound information)
Copy link
Contributor

Choose a reason for hiding this comment

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

What is a command option? something like -myoption ?

- standard input (for potentially large inbound content (streaming))
Copy link
Contributor

Choose a reason for hiding this comment

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

What if a plugin needs multiple data streams? in that case STDIN would not be feasible for both.

- standard output (for structured outbound information)
- standard output (for unstructured data (streaming)

The commands and their interface are described in the [plugin reference](../../../docs/pluginreference/plugin.md).

Every plugin must provide the [`info`](../../../docs/pluginreference/plugin_info.md) command. It has to provide information
about the supported features as JSOn document on standard output.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
about the supported features as JSOn document on standard output.
about the supported features as JSON document on standard output.

The structure of this document is described by the descriptor type in package [`api/ocm/plugin/descriptor`](descriptor/descriptor.go)

Plugin are searched in a plugin folder (typically `.ocm/plugins`), This default location can be changed by the `plugincachedir` attribute.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Plugin are searched in a plugin folder (typically `.ocm/plugins`), This default location can be changed by the `plugincachedir` attribute.
Plugins are searched in a plugin folder (typically `.ocm/plugins`), This default location can be changed by the `plugincachedir` attribute.

Could you add an example on how this attribute can be changed?


## Plugin-related CLI Commands

The OCM CLI provides commands to

- [install](../../../docs/reference/ocm_install_plugins.md)
- [update](../../../docs/reference/ocm_install_plugins.md)
- [list](../../../docs/reference/ocm_get_plugins.md)
- [examine](../../../docs/reference/ocm_describe_plugins.md)
- [remove](../../../docs/reference/ocm_install_plugins.md)

plugins.

Plugins can either be installed manually, just by copying the plugin executable to the plugin directory, or by using the CLI commands. They use OCM component versions as installation source. Plugins must have the artifact type `ocmPlugin` and follow the rules for providing multi-platform executables by using separate
Copy link
Contributor

Choose a reason for hiding this comment

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

To me it does not make sense that plugins can only be installed as as a component version as an installation source, shouldnt the source be able to come from anything?

If I read this it sounds like if I want to use the cli install command and I have a file on the FS my options are

  1. Dont use the CLI and move it manually
  2. Create a ComponentVersion, add the ocmPlugin resource and then reference that during the install command. That to me sounds very weird

Another naming thing: wouldnt the plugin have a "resource type" instead of an "artifact type"? maybe im confusing things here though

resources with the same name by different platform attributes as extended identity. (see Go platform and architecture names).

The commands extract the correct variant for the platform the command is running.
If the given reference does not include a resource identity, the first resource with the correct artifact type is used.

## Support for writing Plugins

To write an OCM plugin in Go, the provided [support library](ppi) can be used.
Copy link
Contributor

@jakobmoellerdev jakobmoellerdev Nov 22, 2024

Choose a reason for hiding this comment

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

is this link to ppi working?

checked the relative link, thats working

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this library inside the OCM core? That means that every go plugin has to compile the entirety of OCM itself while building, making the Plugins quite big.

Copy link
Contributor

Choose a reason for hiding this comment

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

This go section deserves its own README.md just about the ppi and is not really part of a concept imo. Happy to hear other opinions

It provides a complete set of commands for all extension points and a main
function to run the plugin.

It can be used by a `main` function to run the plugin:

```Go
package main

import (
"os"

"ocm.software/ocm/api/ocm/plugin/ppi"
"ocm.software/ocm/api/ocm/plugin/ppi/cmds"
"ocm.software/ocm/api/version"
"ocm.software/ocm/cmds/demoplugin/accessmethods"
"ocm.software/ocm/cmds/demoplugin/config"
"ocm.software/ocm/cmds/demoplugin/uploaders"
"ocm.software/ocm/cmds/demoplugin/valuesets"
)

func main() {
p := ppi.NewPlugin("demo", version.Get().String())

p.SetShort("demo plugin")
p.SetLong("plugin providing access to temp files and a check routing slip entry.")
p.SetConfigParser(config.GetConfig)

p.RegisterAccessMethod(accessmethods.New())
u := uploaders.New()
p.RegisterUploader("testArtifact", "", u)
p.RegisterValueSet(valuesets.New())
err := cmds.NewPluginCommand(p).Execute(os.Args[1:])
if err != nil {
os.Exit(1)
}
}
```

The library provides an interface type [Plugin](ppi/interface.go) and a standard [implementation](ppi/plugin.go) for this interface.

It is used to configure the desired extension point implementations. Every extension point provides an extension interface and supporting types.
By implementing and registering such an interface the plugin object gets
informed about the implemented extension point variants and how they are implemented.
Comment on lines +105 to +107
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
It is used to configure the desired extension point implementations. Every extension point provides an extension interface and supporting types.
By implementing and registering such an interface the plugin object gets
informed about the implemented extension point variants and how they are implemented.
It is used to configure which extension points to implement.
Every extension point provides
- an extension interface
- supporting types required to interact with the interface.
Implementing & registering an extension point interface variant causes it to be usable as a plugin.


This information is then used by the standard implementation of the `info` command
to provide an appropriate plugin descriptor.
Comment on lines +109 to +110
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
This information is then used by the standard implementation of the `info` command
to provide an appropriate plugin descriptor.
The registration is then used by the ppi library implementation of the `info` command and results in an automatically filled descriptor.


The support provides a complete command set. Therefore, the extension point implementation have never to deal with the command line interface, everything is described by the provided extension point interfaces.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The support provides a complete command set. Therefore, the extension point implementation have never to deal with the command line interface, everything is described by the provided extension point interfaces.
Because the `ppi` library provides all command implementations, plugin authors do not need to write their own CLI. Instead they can focus solely on implementing the extension point interfaces.


The implemented standard commands have access to the plugin object and therefore
determine whether there is an implementation for an extension point variant requested via the command execution.
Comment on lines +114 to +115
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The implemented standard commands have access to the plugin object and therefore
determine whether there is an implementation for an extension point variant requested via the command execution.
The implemented standard commands have access to the plugin object and direct to the correct variant implementation by introspecting the command input.

I think examples would help here.


If there is an implementation, the CLI interface is just mapped to calls to interface functions of the particular extension point functionality.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
If there is an implementation, the CLI interface is just mapped to calls to interface functions of the particular extension point functionality.

This is quite obvious from the 2 paragraphs above and I vote to remove it


The standard implementation od the commands can be found below the package
[`api/plugin/ppi/cmds](ppi/cmds), structured by the extension point name and its
interface operation. (for example [upload/put](ppi/cmds/upload/put/cmd.go))
Comment on lines +119 to +121
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The standard implementation od the commands can be found below the package
[`api/plugin/ppi/cmds](ppi/cmds), structured by the extension point name and its
interface operation. (for example [upload/put](ppi/cmds/upload/put/cmd.go))
The standard implementation of the commands can be found below the package
[`api/plugin/ppi/cmds](ppi/cmds), structured by the extension point name and its
interface operation. (for example [upload/put](ppi/cmds/upload/put/cmd.go))


## How plugins are used in the library

The library includes the counterpart of the plugin-side support. It is found directly in package [`api/ocm/plugin](.). It shared the descriptor package for the plugin descriptor and provides a separate [Plugin](plugin.go) object type.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is it part of the api/ocm package when api/ocm contains the OCM Specification? I thought that the library API and the OCM API are separate.

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The library includes the counterpart of the plugin-side support. It is found directly in package [`api/ocm/plugin](.). It shared the descriptor package for the plugin descriptor and provides a separate [Plugin](plugin.go) object type.
The library includes the counterpart of the plugin-side support. It is found directly in package [`api/ocm/plugin](.). It shares the descriptor package for the plugin descriptor and provides a separate [Plugin](plugin.go) object type.


Such an object provides methods for all extension point functions, which can the appropriate CLI command for its plugin executable. Again, it shields the CLI interface by providing an appropriate Go interface.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does the OCM Side of the Plugin need a CLI interface at all?


To enable the usage of plugins in an [api/ocm/Context](../internal/context.go),
the plugins must be registered at this context by calling [api/ocm/plugin/registration/RegisterExtensions](registration/registration.go).
It used the plugin cache die attribute to setup a plugin cache by reading all the plugin descriptors of the configured plugins and providing appropriate plugin objects.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
It used the plugin cache die attribute to setup a plugin cache by reading all the plugin descriptors of the configured plugins and providing appropriate plugin objects.
It uses the plugin cache attribute to setup a plugin cache by reading all the plugin descriptors of the configured plugins and providing appropriate plugin objects.

whats a die attribute?

Copy link
Contributor

Choose a reason for hiding this comment

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

What is It?


For extension points requiring a static registration (like access method) it uses
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the difference between a static and dynamic registration? this is not covered in this document. And when do you require what?
Also: How does a CLI maintainer write a new extension point?

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
For extension points requiring a static registration (like access method) it uses
For extension points requiring a static registration (like access methods) it uses

Copy link
Contributor

Choose a reason for hiding this comment

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

What is it in this case? The library? The context?

the information from the descriptors to determine the provided variants and registers appropriate proxy type implementations at the context.

For extension points supporting an on-demand registration it use
Copy link
Contributor

Choose a reason for hiding this comment

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

it is not specified

the registration handler registration feature of those extension points
to register an appropriate registration handler using the namespace prefix `plugin`. This handler evaluate sub names to determine the plugin name and extension name in this plugin to provide an extension handler implementation proxy to for ward the handler functionality to the appropriate plugin.
Copy link
Contributor

Choose a reason for hiding this comment

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

I am missing examples here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
to register an appropriate registration handler using the namespace prefix `plugin`. This handler evaluate sub names to determine the plugin name and extension name in this plugin to provide an extension handler implementation proxy to for ward the handler functionality to the appropriate plugin.
to register an appropriate registration handler using the namespace prefix `plugin`. This handler evaluates sub names into the plugin name and extension name in this plugin to provide an extension handler implementation proxy to forward the handler functionality to the appropriate plugin.

This sentence is hard to understand because its large. it would be good to break it up


### Extension Proxies
Copy link
Contributor

Choose a reason for hiding this comment

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

I dont understand the need for an extension proxy because that mapping should be resolvable without it imo. It isnt clear to me from the concept


Regardless, whether it is a static type proxy or handler proxy, the proxy implementation keeps track of it plugin and extension name and maps
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Regardless, whether it is a static type proxy or handler proxy, the proxy implementation keeps track of it plugin and extension name and maps
Regardless, whether it is a static type proxy or handler proxy, the proxy implementation keeps track of its plugin and extension name

The combination of plugin+extension name does not become clear in this concept doc and I am missing the contract description

the extension point functionality to the Go interface of the plugin representation, which the calls the appropriate CLI command of the plugin executable.
Copy link
Contributor

Choose a reason for hiding this comment

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

This sentence is convoluted and basically only says:

"An Extension Proxy uses the Plugin & Extension name to call the go interface for the plugin that in turn calls the CLI command on the Plugin Executable"

Also im missing flow diagrams for this (and many other) interactions here.


The proxy implementation are found in `plugin` packages below the extension point package (for example the [access method plugin proxy](../extensions/accessmethods/plugin)).

The registration handler registries are typically found in the extension point packages in (for example)) [registrations.go](../extensions/download/registration.go). The general registration handler registration handling is implemented in package [api/utils/registrations](../../utils/registrations).
Copy link
Contributor

Choose a reason for hiding this comment

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

registration handler registries and dynamic registrations are not covered well in this document imo and need additional explanation.

It is called registration handler registry, because it is a registry which provides a namespace to name the types of handlers. It is possible to register registration handlers for a sub namespace, which then handle the registration (and creation) of handlers for a particular extension point (for example download handler).
Copy link
Contributor

Choose a reason for hiding this comment

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

If that is really why it is called, then this is a very strange concept.

Why cant the registration be done natively on the underlying registry?


The `command` extension point is implemented by the CLI package [cmds/ocm/commands/ocmcmds/plugins](../../../cmds/ocm/commands/ocmcmds/plugins).
Command extensions can be registered for any verb and object type name (even those not yet existing) and are automatically added to the command tree of the CLI.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this?

Loading