open-component-model / ocm

Open Component Model (Software Bill of Delivery) Toolset
https://ocm.software
Apache License 2.0
32 stars 23 forks source link

Support for CLI Extensions by OCM Plugins #815

Closed mandelsoft closed 3 months ago

mandelsoft commented 3 months ago

What this PR does / why we need it:

The OCM Plugin framework now supports two new features:

Additionally it is possible to consume logging configuration from the OCM CLI for all plugin feature commands.

Examples see coding in cmds/cliplugin

Config Types

Config types are just registered at the Plugin Object;

    p := ppi.NewPlugin("cliplugin", version.Get().String())
        ...
    p.RegisterConfigType(configType)

The argument is just the config type as registered at the ocm library, for example:

const ConfigType = "rhabarber.config.acme.org"

type Config struct {
    runtime.ObjectVersionedType `json:",inline"`
    ...
}

func (a *Config) ApplyTo(ctx cfgcpi.Context, target interface{}) error {
        ...
}

func init() {
    configType = cfgcpi.NewConfigType[*Config](ConfigType, usage)
    cfgcpi.RegisterConfigType(configType)
}

CLI Commands

CLI commands are simple configured cobra.Command objects. They are registered at the plugin object with

        cmd, err := clicmd.NewCLICommand(NewCommand(), clicmd.WithCLIConfig(), clicmd.WithVerb("check"))
    if err != nil {
        os.Exit(1)
    }
    p.RegisterCommand(NewCommand())

with coding similar to


type command struct {
    date string
}

func NewCommand() *cobra.Command {
    cmd := &command{}
    c := &cobra.Command{
        Use:   Name + " <options>",
        Short: "determine whether we are in rhubarb season",
        Long:  "The rhubarb season is between march and april.",
        RunE:  cmd.Run,
    }

    c.Flags().StringVarP(&cmd.date, "date", "d", "", "the date to ask for (MM/DD)")
    return c
}

func (c *command) Run(cmd *cobra.Command, args []string) error {
   ...
}

The plugin programming interface supports the generation of an extension command directly from a cobra command object using the method NewCLICommand from the ppi.clicmd package. Otherwise the ppi.Command interface can be implemented without requiring a cobra command..

If the code wants to use the config framework, for example to

the appropriate command feature must be set. For the cobra support this is implemented by the option WithCLIConfig. If set to true, the OCM CLI configuration is available for the config context used in the CLI code.

The command can be a top-level command or attached to a dedicated verb (and optionally a realm like ocmor oci). For the cobra support this can be requested by the option WithVerb(...).

If the config framework is used just add the following anonymoud import for an automated configuration:

import (
        // enable mandelsoft plugin logging configuration.
    _ "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/ppi/config"
)

The plugin code is then configured with the configuration of the OCM CLI and the config framework can be used. If the configuration should be handled by explicit plugin code a handler can be registered with

func init() {
    command.RegisterCommandConfigHandler(yourHandler)
}

It gets a config yaml according to the config objects used by the OCM library.

Logging

To get the logging configuration from the OCM CLI the plugin has be configured with

    p.ForwardLogging()

If the standard mandelsoft logging from the OCM library is used the configuration can be implemented directly with an anonymous import of

import (
        // enable mandelsoft plugin logging configuration.
    _ "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/ppi/logging"
)

The plugin code is then configured with the logging configuration of the OCM CLI and the mandelsoft logging frame work can be used. If the logging configuration should be handled by explicit plugin code a handler can be registered with

func init() {
    cmds.RegisterLoggingConfigHandler(yourHandler)
}

It gets a logging configuration yaml according to the logging config used by the OCM library (github.com/mandelsoft/logging/config).

Using Plugin command extensions from the OCM library.

The plugin command extensions can also be called without the OCM CLI directly from the OCM library. Therefore the plugin objects provided by the library can be used.

Logging information and config information must explicitly be configured to be passed to the plugin.

Therefore the context attribute clicfgattr.Get(ctx) is used. It can be set via clicfgattr.Set(...). The logging configuration is extracted from the configured configuration object with target type *logging.LoggingConfiguration.

If the code uses an OCM context configured with a (ocm)utils.ConfigureXXX function, the cli config attribute is set accordingly.

Which issue(s) this PR fixes:

mandelsoft commented 3 months ago

Just rebased and fixed the linter issues