Skip to content

Commit

Permalink
blob limit configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
mandelsoft committed Nov 26, 2024
1 parent 879378a commit 422090f
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 5 deletions.
11 changes: 11 additions & 0 deletions api/credentials/identity/hostpath/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,14 @@ func PathPrefix(id cpi.ConsumerIdentity) string {
}
return strings.TrimPrefix(id[ID_PATHPREFIX], "/")
}

func HostPort(id cpi.ConsumerIdentity) string {
if id == nil {
return ""
}
host := id[ID_HOSTNAME]
if port, ok := id[ID_PORT]; ok {
return host + ":" + port
}
return host
}
128 changes: 128 additions & 0 deletions api/ocm/extensions/repositories/genericocireg/config/type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package config

import (
"strings"

"ocm.software/ocm/api/config"
cfgcpi "ocm.software/ocm/api/config/cpi"
"ocm.software/ocm/api/utils/runtime"
)

const (
ConfigType = "blobLimits.ocireg.ocm" + 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
// for configuring blob limits for underlying OCI manifest layers.
type Config struct {
runtime.ObjectVersionedType `json:",inline"`
// BlobLimits describe the limit setting for host:port
// entries. As a spcial case (for testing) it is possible
// to configure linits for CTF, also, by using "@"+filepath.
BlobLimits BlobLimits `json:"blobLimits"`
}

type BlobLimits map[string]int64

func (b BlobLimits) GetLimit(hostport string) int64 {
if b == nil {
return -1
}
host := hostport
i := strings.Index(hostport, ":")
if i > 0 {
host = hostport[:i]
}

l, ok := b[hostport]
if ok {
return l
}
l, ok = b[host]
if ok {
return l
}
return -1
}

type Configurable interface {
ConfigureBlobLimits(limits BlobLimits)
}

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

func (a *Config) GetType() string {
return ConfigType
}

func (a *Config) AddLimit(hostport string, limit int64) {
if a.BlobLimits == nil {
a.BlobLimits = BlobLimits{}
}
a.BlobLimits[hostport] = limit
}

func (a *Config) ApplyTo(ctx config.Context, target interface{}) error {
t, ok := target.(Configurable)
if !ok {
return config.ErrNoContext(ConfigType)
}
if a.BlobLimits != nil {
t.ConfigureBlobLimits(a.BlobLimits)
}
return nil
}

const usage = `
The config type <code>` + ConfigType + `</code> can be used to set some
configurations for an OCM context;
<pre>
type: ` + ConfigType + `
aliases:
myrepo:
type: &lt;any repository type>
&lt;specification attributes>
...
resolvers:
- repository:
type: &lt;any repository type>
&lt;specification attributes>
...
prefix: ghcr.io/open-component-model/ocm
priority: 10
</pre>
With aliases repository alias names can be mapped to a repository specification.
The alias name can be used in a string notation for an OCM repository.
Resolvers define a list of OCM repository specifications to be used to resolve
dedicated component versions. These settings are used to compose a standard
component version resolver provided for an OCM context. Optionally, a component
name prefix can be given. It limits the usage of the repository to resolve only
components with the given name prefix (always complete name segments).
An optional priority can be used to influence the lookup order. Larger value
means higher priority (default 10).
All matching entries are tried to lookup a component version in the following
order:
- highest priority first
- longest matching sequence of component name segments first.
If resolvers are defined, it is possible to use component version names on the
command line without a repository. The names are resolved with the specified
resolution rule.
They are also used as default lookup repositories to lookup component references
for recursive operations on component versions (<code>--lookup</code> option).
`
62 changes: 62 additions & 0 deletions api/ocm/extensions/repositories/genericocireg/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package genericocireg_test

import (
"reflect"

. "github.com/mandelsoft/goutils/testutils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"ocm.software/ocm/api/datacontext"
"ocm.software/ocm/api/ocm/extensions/repositories/genericocireg/config"

"github.com/mandelsoft/goutils/finalizer"
"github.com/mandelsoft/vfs/pkg/osfs"
"github.com/mandelsoft/vfs/pkg/vfs"
"ocm.software/ocm/api/oci"
"ocm.software/ocm/api/oci/extensions/repositories/ctf"
"ocm.software/ocm/api/ocm"
"ocm.software/ocm/api/ocm/cpi/repocpi"
"ocm.software/ocm/api/ocm/extensions/repositories/genericocireg"
"ocm.software/ocm/api/utils/accessio"
"ocm.software/ocm/api/utils/accessobj"
)

var _ = Describe("component repository mapping", func() {
var tempfs vfs.FileSystem

var ocispec oci.RepositorySpec
var spec *genericocireg.RepositorySpec

BeforeEach(func() {
t, err := osfs.NewTempFileSystem()
Expect(err).To(Succeed())
tempfs = t

// ocmlog.Context().AddRule(logging.NewConditionRule(logging.TraceLevel, accessio.ALLOC_REALM))

ocispec, err = ctf.NewRepositorySpec(accessobj.ACC_CREATE, "test", accessio.PathFileSystem(tempfs), accessobj.FormatDirectory)
Expect(err).To(Succeed())
spec = genericocireg.NewRepositorySpec(ocispec, nil)
})

AfterEach(func() {
vfs.Cleanup(tempfs)
})

It("creates a dummy component with configured chunks", func() {
var finalize finalizer.Finalizer
defer Defer(finalize.Finalize)

ctx := ocm.New(datacontext.MODE_EXTENDED)

cfg := config.New()
cfg.AddLimit("@test", 5)
ctx.ConfigContext().ApplyConfig(cfg, "direct")

repo := finalizer.ClosingWith(&finalize, Must(ctx.RepositoryForSpec(spec)))
impl := Must(repocpi.GetRepositoryImplementation(repo))
Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl"))

Expect(impl.(*genericocireg.RepositoryImpl).GetBlobLimit()).To(Equal(int64(5)))
})
})
2 changes: 1 addition & 1 deletion api/ocm/extensions/repositories/genericocireg/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ var _ = Describe("component repository mapping", func() {
MustBeSuccessful(finalize.Finalize())
})

FIt("creates a dummy component with chunks", func() {
It("creates a dummy component with chunks", func() {
var finalize finalizer.Finalizer
defer Defer(finalize.Finalize)

Expand Down
33 changes: 33 additions & 0 deletions api/ocm/extensions/repositories/genericocireg/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ import (
"github.com/mandelsoft/goutils/general"

"ocm.software/ocm/api/credentials"
"ocm.software/ocm/api/credentials/identity/hostpath"
"ocm.software/ocm/api/datacontext"
"ocm.software/ocm/api/oci"
ocicpi "ocm.software/ocm/api/oci/cpi"
"ocm.software/ocm/api/oci/extensions/repositories/ctf"
"ocm.software/ocm/api/oci/extensions/repositories/ocireg"
"ocm.software/ocm/api/ocm/cpi"
"ocm.software/ocm/api/ocm/cpi/repocpi"
"ocm.software/ocm/api/ocm/extensions/repositories/genericocireg/componentmapping"
"ocm.software/ocm/api/ocm/extensions/repositories/genericocireg/config"
)

type OCIBasedRepository interface {
Expand Down Expand Up @@ -50,23 +54,52 @@ type RepositoryImpl struct {
var (
_ repocpi.RepositoryImpl = (*RepositoryImpl)(nil)
_ credentials.ConsumerIdentityProvider = (*RepositoryImpl)(nil)
_ config.Configurable = (*RepositoryImpl)(nil)
)

func NewRepository(ctxp cpi.ContextProvider, meta *ComponentRepositoryMeta, ocirepo oci.Repository, blobLimit ...int64) cpi.Repository {
ctx := datacontext.InternalContextRef(ctxp.OCMContext())

impl := &RepositoryImpl{
ctx: ctx,
meta: *DefaultComponentRepositoryMeta(meta),
ocirepo: ocirepo,
blobLimit: general.OptionalDefaulted(-1, blobLimit...),
}
if len(blobLimit) == 0 {
ctxp.OCMContext().ConfigContext().ApplyTo(0, impl)
}
return repocpi.NewRepository(impl, "OCM repo[OCI]")
}

func (r *RepositoryImpl) ConfigureBlobLimits(limits config.BlobLimits) {
if len(limits) == 0 {
return
}
if spec, ok := r.ocirepo.GetSpecification().(*ocireg.RepositorySpec); ok {
id := spec.GetConsumerId()
hp := hostpath.HostPort(id)
l := limits.GetLimit(hp)
if l >= 0 {
r.blobLimit = l
}
}
if spec, ok := r.ocirepo.GetSpecification().(*ctf.RepositorySpec); ok {
l := limits.GetLimit("@" + spec.FilePath)
if l >= 0 {
r.blobLimit = l
}
}
}

func (r *RepositoryImpl) SetBlobLimit(s int64) {
r.blobLimit = s
}

func (r *RepositoryImpl) GetBlobLimit() int64 {
return r.blobLimit
}

func (r *RepositoryImpl) Close() error {
return r.ocirepo.Close()
}
Expand Down
13 changes: 9 additions & 4 deletions api/ocm/extensions/repositories/genericocireg/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func NewComponentRepositoryMeta(subPath string, mapping ComponentNameMapping) *C
type RepositorySpec struct {
oci.RepositorySpec
ComponentRepositoryMeta
BlobLimit int64
BlobLimit *int64
}

var (
Expand Down Expand Up @@ -130,7 +130,7 @@ func (a *RepositorySpec) AsUniformSpec(cpi.Context) *cpi.UniformRepositorySpec {

type meta struct {
ComponentRepositoryMeta `json:",inline"`
BlobLimit int64 `json:"blobLimit"`
BlobLimit *int64 `json:"blobLimit,omitempty"`
}

func (u *RepositorySpec) UnmarshalJSON(data []byte) error {
Expand All @@ -147,7 +147,9 @@ func (u *RepositorySpec) UnmarshalJSON(data []byte) error {

u.RepositorySpec = ocispec
u.ComponentRepositoryMeta = m.ComponentRepositoryMeta
u.BlobLimit = m.BlobLimit
if m.BlobLimit != nil {
u.BlobLimit = m.BlobLimit
}

normalizers.Normalize(u)
return nil
Expand Down Expand Up @@ -179,7 +181,10 @@ func (s *RepositorySpec) Repository(ctx cpi.Context, creds credentials.Credentia
if err != nil {
return nil, err
}
return NewRepository(ctx, &s.ComponentRepositoryMeta, r, s.BlobLimit), nil
if s.BlobLimit != nil {
return NewRepository(ctx, &s.ComponentRepositoryMeta, r, *s.BlobLimit), nil
}
return NewRepository(ctx, &s.ComponentRepositoryMeta, r), nil
}

func (s *RepositorySpec) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity {
Expand Down
41 changes: 41 additions & 0 deletions docs/reference/ocm_configfile.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,47 @@ The following configuration types are supported:
&lt;name>: &lt;yaml defining the attribute>
...
</pre>
- <code>blobLimits.ocireg.ocm.config.ocm.software</code>
The config type <code>blobLimits.ocireg.ocm.config.ocm.software</code> can be used to set some
configurations for an OCM context;

<pre>
type: blobLimits.ocireg.ocm.config.ocm.software
aliases:
myrepo:
type: &lt;any repository type>
&lt;specification attributes>
...
resolvers:
- repository:
type: &lt;any repository type>
&lt;specification attributes>
...
prefix: ghcr.io/open-component-model/ocm
priority: 10
</pre>

With aliases repository alias names can be mapped to a repository specification.
The alias name can be used in a string notation for an OCM repository.

Resolvers define a list of OCM repository specifications to be used to resolve
dedicated component versions. These settings are used to compose a standard
component version resolver provided for an OCM context. Optionally, a component
name prefix can be given. It limits the usage of the repository to resolve only
components with the given name prefix (always complete name segments).
An optional priority can be used to influence the lookup order. Larger value
means higher priority (default 10).

All matching entries are tried to lookup a component version in the following
order:
- highest priority first
- longest matching sequence of component name segments first.

If resolvers are defined, it is possible to use component version names on the
command line without a repository. The names are resolved with the specified
resolution rule.
They are also used as default lookup repositories to lookup component references
for recursive operations on component versions (<code>--lookup</code> option).
- <code>cli.ocm.config.ocm.software</code>
The config type <code>cli.ocm.config.ocm.software</code> is used to handle the
main configuration flags of the OCM command line tool.
Expand Down

0 comments on commit 422090f

Please sign in to comment.