Skip to content

feat: configurable preference for relative access creation for uploader #1412

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

Open
wants to merge 2 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
18 changes: 18 additions & 0 deletions api/config/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package config

import (
cfgcpi "ocm.software/ocm/api/config/cpi"
)

// GetConfigured applies config objects of a config context
// to a configuration struct of type T.
// A pointer to the configured struct is returned.
// Attention: T must be a struct type.
func GetConfigured[T any](ctxp ContextProvider) (*T, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

There is exactly one usage of this method, so I would like to keep it an internal helper

var c T
err := cfgcpi.NewUpdater(ctxp.ConfigContext(), &c).Update()
if err != nil {
return nil, err
}
return &c, nil
}
2 changes: 2 additions & 0 deletions api/oci/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type (
BlobAccess = internal.BlobAccess
DataAccess = internal.DataAccess
ConsumerIdentityProvider = internal.ConsumerIdentityProvider

UniformRepositorySpecProvider = internal.UniformRepositorySpecProvider
)

func DefaultContext() internal.Context {
Expand Down
6 changes: 5 additions & 1 deletion api/oci/internal/repotypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ type IntermediateRepositorySpecAspect interface {
IsIntermediate() bool
}

type UniformRepositorySpecProvider interface {
UniformRepositorySpec() *UniformRepositorySpec
}

type RepositorySpec interface {
runtime.VersionedTypedObject
UniformRepositorySpecProvider
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 every Repository Spec now a Uniform Spec Provider?


Name() string
UniformRepositorySpec() *UniformRepositorySpec
Repository(Context, credentials.Credentials) (Repository, error)

Validate(Context, credentials.Credentials, ...credentials.UsageContext) error
Expand Down
14 changes: 12 additions & 2 deletions api/ocm/extensions/accessmethods/relativeociref/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func init() {

var _ accspeccpi.HintProvider = (*AccessSpec)(nil)

// New creates a new localFilesystemBlob accessor.
// New creates a new relativeOciReference accessor.
func New(ref string) *AccessSpec {
return &AccessSpec{
ObjectVersionedType: runtime.NewVersionedObjectType(Type),
Expand Down Expand Up @@ -82,7 +82,17 @@ func (a *AccessSpec) GetOCIReference(cv accspeccpi.ComponentVersionAccess) (stri
defer m.Close()

if o, ok := accspeccpi.GetAccessMethodImplementation(m).(ociartifact.OCIArtifactReferenceProvider); ok {
return o.GetOCIReference(nil)
im, err := o.GetOCIReference(cv)
if err == nil {
if s, ok := cv.Repository().GetSpecification().(oci.UniformRepositorySpecProvider); ok {
host := s.UniformRepositorySpec().Host
// not supported for fake repos
if host != "" {
im = host + "/" + im
}
}
}
return im, err
}
return "", nil
}
66 changes: 66 additions & 0 deletions api/ocm/extensions/attrs/preferrelativeattr/attr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package preferrelativeattr

import (
"fmt"

"ocm.software/ocm/api/datacontext"
"ocm.software/ocm/api/utils/runtime"
)

const (
ATTR_KEY = "github.com/mandelsoft/ocm/preferrelativeaccess"
Copy link
Contributor

Choose a reason for hiding this comment

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

This attribute key should be changed.

Suggested change
ATTR_KEY = "github.com/mandelsoft/ocm/preferrelativeaccess"
ATTR_KEY = "ocm.software/ocm/oci/" + ATTR_SHORT

ATTR_SHORT = "preferrelativeaccess"
)

func init() {
datacontext.RegisterAttributeType(ATTR_KEY, AttributeType{}, ATTR_SHORT)
}

type AttributeType struct{}

func (a AttributeType) Name() string {
return ATTR_KEY
}

func (a AttributeType) Description() string {
return `
*bool*
If an artifact blob is uploaded to the technical repository
used as OCM repository, the uploader should prefer to return
a relative access method.
`
}

func (a AttributeType) Encode(v interface{}, marshaller runtime.Marshaler) ([]byte, error) {
if _, ok := v.(bool); !ok {
return nil, fmt.Errorf("boolean required")
}
return marshaller.Marshal(v)
}

func (a AttributeType) Decode(data []byte, unmarshaller runtime.Unmarshaler) (interface{}, error) {
var value bool
err := unmarshaller.Unmarshal(data, &value)
return value, err
}

////////////////////////////////////////////////////////////////////////////////

func Get(ctx datacontext.Context) bool {
a := ctx.GetAttributes().GetAttribute(ATTR_KEY)
if a == nil {
return false
}
return a.(bool)
}

func Set(ctx datacontext.Context, flag bool) error {
return ctx.GetAttributes().SetAttribute(ATTR_KEY, flag)
}

func ApplyTo(ctx datacontext.Context, flag *bool) bool {
if a := ctx.GetAttributes().GetAttribute(ATTR_KEY); a != nil {
*flag = a.(bool)
}
return *flag
}
41 changes: 41 additions & 0 deletions api/ocm/extensions/attrs/preferrelativeattr/attr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package preferrelativeattr_test

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

"ocm.software/ocm/api/config"
"ocm.software/ocm/api/credentials"
"ocm.software/ocm/api/datacontext"
"ocm.software/ocm/api/oci"
"ocm.software/ocm/api/ocm"
me "ocm.software/ocm/api/ocm/extensions/attrs/keepblobattr"
"ocm.software/ocm/api/utils/runtime"
)

var _ = Describe("attribute", func() {
var ctx ocm.Context
var cfgctx config.Context

BeforeEach(func() {
cfgctx = config.WithSharedAttributes(datacontext.New(nil)).New()
credctx := credentials.WithConfigs(cfgctx).New()
ocictx := oci.WithCredentials(credctx).New()
ctx = ocm.WithOCIRepositories(ocictx).New()
})
It("local setting", func() {
Expect(me.Get(ctx)).To(BeFalse())
Expect(me.Set(ctx, true)).To(Succeed())
Expect(me.Get(ctx)).To(BeTrue())
})

It("global setting", func() {
Expect(me.Get(cfgctx)).To(BeFalse())
Expect(me.Set(ctx, true)).To(Succeed())
Expect(me.Get(ctx)).To(BeTrue())
})

It("parses string", func() {
Expect(me.AttributeType{}.Decode([]byte("true"), runtime.DefaultJSONEncoding)).To(BeTrue())
})
})
13 changes: 13 additions & 0 deletions api/ocm/extensions/attrs/preferrelativeattr/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package preferrelativeattr_test

import (
"testing"

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

func TestConfig(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "OCM Prefer Relative Access Attribute")
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/mandelsoft/goutils/sliceutils"
"github.com/opencontainers/go-digest"

cfgctx "ocm.software/ocm/api/config"
"ocm.software/ocm/api/oci"
"ocm.software/ocm/api/oci/artdesc"
"ocm.software/ocm/api/oci/extensions/repositories/artifactset"
Expand All @@ -23,10 +24,13 @@ import (
"ocm.software/ocm/api/ocm/extensions/accessmethods/localociblob"
"ocm.software/ocm/api/ocm/extensions/accessmethods/ociartifact"
"ocm.software/ocm/api/ocm/extensions/accessmethods/ociblob"
"ocm.software/ocm/api/ocm/extensions/accessmethods/relativeociref"
"ocm.software/ocm/api/ocm/extensions/attrs/compatattr"
"ocm.software/ocm/api/ocm/extensions/attrs/keepblobattr"
"ocm.software/ocm/api/ocm/extensions/attrs/mapocirepoattr"
"ocm.software/ocm/api/ocm/extensions/attrs/preferrelativeattr"
storagecontext "ocm.software/ocm/api/ocm/extensions/blobhandler/handlers/oci"
"ocm.software/ocm/api/ocm/extensions/blobhandler/handlers/oci/ocirepo/config"
"ocm.software/ocm/api/utils/accessobj"
"ocm.software/ocm/api/utils/blobaccess/blobaccess"
)
Expand Down Expand Up @@ -226,6 +230,14 @@ func (b *artifactHandler) StoreBlob(blob cpi.BlobAccess, artType, hint string, g

keep := keepblobattr.Get(ctx.GetContext())

opts, _ := cfgctx.GetConfigured[config.UploadOptions](ctx.GetContext())
if opts == nil {
opts = &config.UploadOptions{}
}
// this attribute (only if set) overrides the enabling set in the
// config.
preferrelativeattr.ApplyTo(ctx.GetContext(), &opts.PreferRelativeAccess)

if m, ok := blob.(blobaccess.AnnotatedBlobAccess[accspeccpi.AccessMethodView]); ok {
// prepare for optimized point to point implementation
log.Debug("oci artifact handler with ocm access source",
Expand Down Expand Up @@ -340,6 +352,10 @@ func (b *artifactHandler) StoreBlob(blob cpi.BlobAccess, artType, hint string, g
if tag != "" {
tag = ":" + tag
}
if opts.PreferRelativeAccessFor(base) {
ref := namespace.GetNamespace() + tag + version
return relativeociref.New(ref), nil
}
ref := scheme + path.Join(base, namespace.GetNamespace()) + tag + version
return ociartifact.New(ref), nil
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package config

import (
"net"
)

// UploadOptions is used to configure
// the implicit OCI uploader for a local OCM repository.
// It can be used to request the generation of relative
// OCI access methods, generally or for dedicated targets.
type UploadOptions struct {
// PreferRelativeAccess enables or disables the settings.
PreferRelativeAccess bool `json:"preferRelativeAccess,omitempty"`
// Repositories is list of repository specs, with or without port.
// If no filters are configured all repos are matched.
Repositories []string `json:"repositories,omitempty"`
}

// PreferRelativeAccessFor checks a repo spec for using
// a relative access method instead of an absolute one.
// It checks hostname and optionally a port name.
// The most specific configuration wins.
func (o *UploadOptions) PreferRelativeAccessFor(repo string) bool {
if len(o.Repositories) == 0 || !o.PreferRelativeAccess {
return o.PreferRelativeAccess
}

fallback := false

host, port, err := net.SplitHostPort(repo)
if err != nil {
host = repo
}
for _, r := range o.Repositories {
rhost, rport, err := net.SplitHostPort(r)
if err != nil {
rhost = r
}
if host == rhost {
if rport == "" {
fallback = true
}
if port == rport {
return true
}
}
}
return fallback
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package config_test

import (
"testing"

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

func TestConfig(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "OCI Uploader forOCI OCM Repositories Test Suite")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package config

import (
"ocm.software/ocm/api/config"
cfgcpi "ocm.software/ocm/api/config/cpi"
"ocm.software/ocm/api/ocm/extensions/attrs/preferrelativeattr"
"ocm.software/ocm/api/utils/runtime"
)

const (
ConfigType = "local.oci.uploader" + cfgcpi.OCM_CONFIG_TYPE_SUFFIX
ConfigTypeV1 = ConfigType + runtime.VersionSeparator + "v1"
)

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

// Config describes a memory based config interface.
type Config struct {
runtime.ObjectVersionedType `json:",inline"`
UploadOptions
}

// New creates a new memory ConfigSpec.
func New() *Config {
return &Config{
ObjectVersionedType: runtime.NewVersionedTypedObject(ConfigType),
}
}

func (a *Config) ApplyTo(ctx config.Context, target interface{}) error {
t, ok := target.(*UploadOptions)
if !ok {
return config.ErrNoContext(ConfigType)
}
t.Repositories = append(t.Repositories, a.Repositories...)
t.PreferRelativeAccess = a.PreferRelativeAccess
return nil
}

const usage = `
The config type <code>` + ConfigType + `</code> can be used to set some
configurations for the implicit OCI artifact upload for OCI based OCM repositories.

<pre>
type: ` + ConfigType + `
preferRelativeAccess: true # use relative access methods for given target repositories.
repositories:
- localhost:5000
</pre>

If <code>preferRelativeAccess</code> is set to <code>true</code> the
OCI uploader for OCI based OCM repositories does not use the
OCI repository to create absolute OCI access methods
if the target repository is in the <code>repositories</code> list.
Instead, a relative <code>relativeOciReference</code> access method
is created.
If this list is empty, all uploads are handled this way.

If the global attribute <code>` + preferrelativeattr.ATTR_SHORT + `</code>
is configured, it overrides the <code>preferRelativeAccess</code> setting.
`
Loading
Loading