Skip to content

Commit

Permalink
make input scheme extendable
Browse files Browse the repository at this point in the history
  • Loading branch information
mandelsoft committed Dec 9, 2024
1 parent 5dd5512 commit f42bff7
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 36 deletions.
104 changes: 104 additions & 0 deletions cmds/ocm/commands/ocmcmds/common/inputs/extend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package inputs_test

import (
"fmt"

. "github.com/mandelsoft/goutils/testutils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/validation/field"

"ocm.software/ocm/api/utils/blobaccess/blobaccess"
"ocm.software/ocm/api/utils/cobrautils/flagsets"
"ocm.software/ocm/api/utils/mime"
"ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs"
"ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/cpi"
"ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/options"
"ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/ociartifact"
)

var _ = Describe("Input Type Extension Test Environment", func() {
var (
scheme = inputs.NewInputTypeScheme(nil, inputs.DefaultInputTypeScheme)
itype = inputs.NewInputType(TYPE, &Spec{}, "", ConfigHandler())
flags *pflag.FlagSet
opts flagsets.ConfigOptions
)

BeforeEach(func() {
scheme.Register(itype)
flags = &pflag.FlagSet{}
opts = scheme.CreateConfigTypeSetConfigProvider().CreateOptions()
opts.AddFlags(flags)
})

It("derives base input type", func() {
prov := scheme.CreateConfigTypeSetConfigProvider()
MustBeSuccessful(flagsets.ParseOptionsFor(flags,
flagsets.OptionSpec(prov.GetTypeOptionType(), ociartifact.TYPE),
flagsets.OptionSpec(options.PathOption, "ghcr.io/open-component-model/image:v1.0"),
flagsets.OptionSpec(options.PlatformsOption, "linux/amd64"),
flagsets.OptionSpec(options.PlatformsOption, "/arm64"),
))
cfg := Must(prov.GetConfigFor(opts))
fmt.Printf("selected input options: %+v\n", cfg)

spec := Must(scheme.GetInputSpecFor(cfg))
Expect(spec).To(Equal(ociartifact.New("ghcr.io/open-component-model/image:v1.0", "linux/amd64", "/arm64")))
})

It("uses extended input type", func() {
prov := scheme.CreateConfigTypeSetConfigProvider()
MustBeSuccessful(flagsets.ParseOptionsFor(flags,
flagsets.OptionSpec(prov.GetTypeOptionType(), TYPE),
flagsets.OptionSpec(options.PathOption, "ghcr.io/open-component-model/image:v1.0"),
))
cfg := Must(prov.GetConfigFor(opts))
fmt.Printf("selected input options: %+v\n", cfg)

spec := Must(scheme.GetInputSpecFor(cfg))
Expect(spec).To(Equal(New("ghcr.io/open-component-model/image:v1.0")))
})
})

////////////////////////////////////////////////////////////////////////////////
// test input

const TYPE = "testinput"

type Spec struct {
// PathSpec holds the repository path and tag of the image in the docker daemon
cpi.PathSpec
}

var _ inputs.InputSpec = (*Spec)(nil)

func New(pathtag string) *Spec {
return &Spec{
PathSpec: cpi.NewPathSpec(TYPE, pathtag),
}
}

func (s *Spec) Validate(fldPath *field.Path, ctx inputs.Context, inputFilePath string) field.ErrorList {
allErrs := s.PathSpec.Validate(fldPath, ctx, inputFilePath)
return allErrs
}

func (s *Spec) GetBlob(ctx inputs.Context, info inputs.InputResourceInfo) (blobaccess.BlobAccess, string, error) {
blob := blobaccess.ForString(mime.MIME_TEXT, s.Path)
return blob, "", nil
}

func ConfigHandler() flagsets.ConfigOptionTypeSetHandler {
return cpi.NewMediaFileSpecOptionType(TYPE, AddConfig,
options.PathOption, options.HintOption, options.PlatformsOption)
}

func AddConfig(opts flagsets.ConfigOptions, config flagsets.Config) error {
if err := cpi.AddPathSpecConfig(opts, config); err != nil {
return err
}
return nil
}
42 changes: 18 additions & 24 deletions cmds/ocm/commands/ocmcmds/common/inputs/inputtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/mandelsoft/goutils/errors"
"github.com/modern-go/reflect2"
"k8s.io/apimachinery/pkg/util/validation/field"
ocmlog "ocm.software/ocm/api/utils/logging"

clictx "ocm.software/ocm/api/cli"
"ocm.software/ocm/api/datacontext"
Expand Down Expand Up @@ -157,53 +158,47 @@ func (t *DefaultInputType) ApplyConfig(opts flagsets.ConfigOptions, config flags
type InputTypeScheme interface {
runtime.Scheme[InputSpec, InputType]

ConfigTypeSetConfigProvider() flagsets.ConfigTypeOptionSetConfigProvider
flagsets.ConfigProvider
CreateConfigTypeSetConfigProvider() flagsets.ConfigTypeOptionSetConfigProvider

GetInputType(name string) InputType
Register(atype InputType)

GetInputSpecFor(opts flagsets.ConfigOptions) (InputSpec, error)
GetInputSpecFor(flagsets.Config) (InputSpec, error)
DecodeInputSpec(data []byte, unmarshaler runtime.Unmarshaler) (InputSpec, error)
CreateInputSpec(obj runtime.TypedObject) (InputSpec, error)
}

type inputTypeScheme struct {
runtime.Scheme[InputSpec, InputType]
optionTypes flagsets.ConfigTypeOptionSetConfigProvider
}

func NewInputTypeScheme(defaultRepoDecoder runtime.TypedObjectDecoder[InputSpec]) InputTypeScheme {
scheme := runtime.MustNewDefaultScheme[InputSpec, InputType](&UnknownInputSpec{}, false, defaultRepoDecoder)
prov := flagsets.NewTypedConfigProvider("input", "blob input specification", "inputType")
prov.AddGroups("Input Specification Options")
return &inputTypeScheme{scheme, prov}
}
func NewInputTypeScheme(defaultRepoDecoder runtime.TypedObjectDecoder[InputSpec], base ...InputTypeScheme) InputTypeScheme {
var b runtime.Scheme[InputSpec, InputType] = utils.Optional(base...)

func (t *inputTypeScheme) ConfigTypeSetConfigProvider() flagsets.ConfigTypeOptionSetConfigProvider {
return t.optionTypes
scheme := runtime.MustNewDefaultScheme[InputSpec, InputType](&UnknownInputSpec{}, false, defaultRepoDecoder, b)
return &inputTypeScheme{scheme}
}

func (t *inputTypeScheme) CreateOptions() flagsets.ConfigOptions {
return t.optionTypes.CreateOptions()
func (t *inputTypeScheme) CreateConfigTypeSetConfigProvider() flagsets.ConfigTypeOptionSetConfigProvider {
prov := flagsets.NewTypedConfigProvider("input", "blob input specification", "inputType")
prov.AddGroups("Input Specification Options")
for _, p := range t.KnownTypes() {
err := prov.AddTypeSet(p.ConfigOptionTypeSetHandler())
if err != nil {
ocmlog.Logger(REALM).LogError(err, "cannot compose type CLI options", "type", p.GetType())
}
}
return prov
}

func (t *inputTypeScheme) GetInputSpecFor(opts flagsets.ConfigOptions) (InputSpec, error) {
cfg, err := t.GetConfigFor(opts)
if err != nil {
return nil, err
}
func (t *inputTypeScheme) GetInputSpecFor(cfg flagsets.Config) (InputSpec, error) {
data, err := json.Marshal(cfg)
if err != nil {
return nil, err
}
return t.DecodeInputSpec(data, runtime.DefaultJSONEncoding)
}

func (t *inputTypeScheme) GetConfigFor(opts flagsets.ConfigOptions) (flagsets.Config, error) {
return t.optionTypes.GetConfigFor(opts)
}

func (t *inputTypeScheme) GetInputType(name string) InputType {
d := t.GetDecoder(name)
if d == nil {
Expand All @@ -217,7 +212,6 @@ func (t *inputTypeScheme) Register(rtype InputType) {
return
}
t.RegisterByDecoder(rtype.GetType(), rtype)
t.optionTypes.AddTypeSet(rtype.ConfigOptionTypeSetHandler())
}

func (t *inputTypeScheme) DecodeInputSpec(data []byte, unmarshaler runtime.Unmarshaler) (InputSpec, error) {
Expand Down
1 change: 0 additions & 1 deletion cmds/ocm/commands/ocmcmds/common/inputs/inputtype_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"ocm.software/ocm/api/utils/mime"
"ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs"
"ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/file"
Expand Down
7 changes: 7 additions & 0 deletions cmds/ocm/commands/ocmcmds/common/inputs/logging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package inputs

import (
ocmlog "ocm.software/ocm/api/utils/logging"
)

var REALM = ocmlog.DefineSubRealm("OCM Input Handling", "inputs")
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/options"
)

func ConfigHandler() flagsets.ConfigOptionTypeSetHandler {
return cpi.NewMediaFileSpecOptionType(TYPE, AddConfig,
func ConfigHandler(t string) flagsets.ConfigOptionTypeSetHandler {
return cpi.NewMediaFileSpecOptionType(t, AddConfig,
options.PathOption, options.HintOption, options.PlatformsOption)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,23 +120,24 @@ var _ = Describe("Test Environment", func() {
Context("inputs", func() {
BeforeEach(func() {
flags = &pflag.FlagSet{}
opts = inputs.DefaultInputTypeScheme.CreateOptions()
opts = inputs.DefaultInputTypeScheme.CreateConfigTypeSetConfigProvider().CreateOptions()
opts.AddFlags(flags)
cfg = flagsets.Config{}
})

It("input type", func() {
fmt.Printf("input option names: %+v\n", opts.Names())
prov := inputs.DefaultInputTypeScheme.CreateConfigTypeSetConfigProvider()
MustBeSuccessful(flagsets.ParseOptionsFor(flags,
flagsets.OptionSpec(inputs.DefaultInputTypeScheme.ConfigTypeSetConfigProvider().GetTypeOptionType(), me.TYPE),
flagsets.OptionSpec(prov.GetTypeOptionType(), me.TYPE),
flagsets.OptionSpec(options.PathOption, "ghcr.io/open-component-model/image:v1.0"),
flagsets.OptionSpec(options.PlatformsOption, "linux/amd64"),
flagsets.OptionSpec(options.PlatformsOption, "/arm64"),
))
cfg := Must(inputs.DefaultInputTypeScheme.GetConfigFor(opts))
cfg := Must(prov.GetConfigFor(opts))
fmt.Printf("selected input options: %+v\n", cfg)

spec := Must(inputs.DefaultInputTypeScheme.GetInputSpecFor(opts))
spec := Must(inputs.DefaultInputTypeScheme.GetInputSpecFor(cfg))
Expect(spec).To(Equal(me.New("ghcr.io/open-component-model/image:v1.0", "linux/amd64", "/arm64")))
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const (
)

func init() {
inputs.DefaultInputTypeScheme.Register(inputs.NewInputType(TYPE, &Spec{}, usage, ConfigHandler()))
inputs.DefaultInputTypeScheme.Register(inputs.NewInputType(LEGACY_TYPE, &Spec{}, legacy_usage, ConfigHandler()))
inputs.DefaultInputTypeScheme.Register(inputs.NewInputType(TYPE, &Spec{}, usage, ConfigHandler(TYPE)))
inputs.DefaultInputTypeScheme.Register(inputs.NewInputType(LEGACY_TYPE, &Spec{}, legacy_usage, ConfigHandler(LEGACY_TYPE)))
}

const legacy_usage = `
Expand Down
6 changes: 3 additions & 3 deletions cmds/ocm/commands/ocmcmds/common/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func (a *ContentResourceSpecificationsProvider) AddFlags(fs *pflag.FlagSet) {
a.ElementMetaDataSpecificationsProvider.AddFlags(fs)

a.accprov = a.ctx.OCMContext().AccessMethods().CreateConfigTypeSetConfigProvider()
inptypes := inputs.For(a.ctx).ConfigTypeSetConfigProvider()
inptypes := inputs.For(a.ctx).CreateConfigTypeSetConfigProvider()

set := flagsets.NewConfigOptionTypeSet("resources")
set.AddAll(a.accprov)
Expand Down Expand Up @@ -301,7 +301,7 @@ func (a *ContentResourceSpecificationsProvider) Complete() error {

unique := a.options.FilterBy(flagsets.Not(a.shared.HasOptionType))
aopts := unique.FilterBy(a.accprov.HasOptionType)
iopts := unique.FilterBy(inputs.For(a.ctx).ConfigTypeSetConfigProvider().HasOptionType)
iopts := unique.FilterBy(inputs.For(a.ctx).CreateConfigTypeSetConfigProvider().HasOptionType)

if !a.options.Changed(a.contentFlags...) {
return fmt.Errorf("one of %v is required", flagsets.AddPrefix("--", a.contentFlags...))
Expand Down Expand Up @@ -350,7 +350,7 @@ func (a *ContentResourceSpecificationsProvider) Get() (string, error) {
if err != nil {
return "", err
}
err = a.apply(inputs.For(a.ctx).ConfigTypeSetConfigProvider(), data)
err = a.apply(inputs.For(a.ctx).CreateConfigTypeSetConfigProvider(), data)
if err != nil {
return "", err
}
Expand Down

0 comments on commit f42bff7

Please sign in to comment.