Skip to content

Commit

Permalink
improve ocm ref parsing (#337)
Browse files Browse the repository at this point in the history
* improve ocm ref parsing

* improve OCIRegistry Ref Mapping
  • Loading branch information
mandelsoft authored and robertwolu committed Sep 25, 2023
1 parent 2a55b23 commit dce58d1
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 23 deletions.
5 changes: 5 additions & 0 deletions pkg/contexts/oci/grammar/grammar.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ var (
DomainRegexp,
Optional(Literal(`:`), Match(`[0-9]+`)))

// HostPortRegexp describes a non-DNS simple hostname like localhost.
HostPortRegexp = Sequence(
DomainComponentRegexp,
Optional(Literal(`:`), Match(`[0-9]+`)))

PathRegexp = Sequence(
Optional(Literal("/")),
Match(`[a-zA-Z0-9-_.]+(?:/[a-zA-Z0-9-_.]+)+`))
Expand Down
51 changes: 51 additions & 0 deletions pkg/contexts/ocm/cpi/ref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
//
// SPDX-License-Identifier: Apache-2.0

package cpi

import (
"sync"
)

type ParseHandler func(u *UniformRepositorySpec) error

type registry struct {
lock sync.RWMutex
handlers map[string]ParseHandler
}

func (r *registry) Register(ty string, h ParseHandler) {
r.lock.Lock()
defer r.lock.Unlock()
r.handlers[ty] = h
}

func (r *registry) Get(ty string) ParseHandler {
r.lock.RLock()
defer r.lock.RUnlock()
return r.handlers[ty]
}

func (r *registry) Handle(u UniformRepositorySpec) (UniformRepositorySpec, error) {
h := r.Get(u.Type)
if h != nil {
err := h(&u)
return u, err
}
return u, nil
}

var parseregistry = &registry{handlers: map[string]ParseHandler{}}

func RegisterRefParseHandler(ty string, h ParseHandler) {
parseregistry.Register(ty, h)
}

func GetRefParseHandler(ty string, h ParseHandler) {
parseregistry.Get(ty)
}

func HandleRef(u UniformRepositorySpec) (UniformRepositorySpec, error) {
return parseregistry.Handle(u)
}
3 changes: 1 addition & 2 deletions pkg/contexts/ocm/internal/uniform.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"fmt"
"sync"

"github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ocireg"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/runtime"
"github.com/open-component-model/ocm/pkg/utils"
Expand Down Expand Up @@ -48,7 +47,7 @@ func (r *UniformRepositorySpec) CredHost() string {

func (u *UniformRepositorySpec) String() string {
t := u.Type
if t != "" && !ocireg.IsKind(t) {
if t != "" {
t += "::"
}
if u.Info != "" {
Expand Down
17 changes: 10 additions & 7 deletions pkg/contexts/ocm/ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/open-component-model/ocm/pkg/common"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
"github.com/open-component-model/ocm/pkg/contexts/ocm/grammar"
"github.com/open-component-model/ocm/pkg/errors"
)
Expand All @@ -25,29 +26,29 @@ func ParseRepo(ref string) (UniformRepositorySpec, error) {
ref = ref[1:]
}
if strings.HasPrefix(ref, ".") || strings.HasPrefix(ref, "/") {
return UniformRepositorySpec{
return cpi.HandleRef(UniformRepositorySpec{
Info: ref,
CreateIfMissing: create,
}, nil
})
}
match := grammar.AnchoredRepositoryRegexp.FindSubmatch([]byte(ref))
if match == nil {
match = grammar.AnchoredGenericRepositoryRegexp.FindSubmatch([]byte(ref))
if match == nil {
return UniformRepositorySpec{}, errors.ErrInvalid(KIND_OCM_REFERENCE, ref)
}
return UniformRepositorySpec{
return cpi.HandleRef(UniformRepositorySpec{
Type: string(match[1]),
Info: string(match[2]),
CreateIfMissing: create,
}, nil
})
}
return UniformRepositorySpec{
return cpi.HandleRef(UniformRepositorySpec{
Type: string(match[1]),
Host: string(match[2]),
SubPath: string(match[3]),
CreateIfMissing: create,
}, nil
})
}

// RefSpec is a go internal representation of a oci reference.
Expand Down Expand Up @@ -102,7 +103,9 @@ func ParseRef(ref string) (RefSpec, error) {
if v != "" {
spec.Version = &v
}
return spec, nil
var err error
spec.UniformRepositorySpec, err = cpi.HandleRef(spec.UniformRepositorySpec)
return spec, err
}

func (r *RefSpec) Name() string {
Expand Down
38 changes: 31 additions & 7 deletions pkg/contexts/ocm/ref_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
package ocm_test

import (
"strings"

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

"github.com/open-component-model/ocm/pkg/contexts/ocm"
"github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ocireg"
. "github.com/open-component-model/ocm/pkg/testutils"
)

func Type(t string) string {
Expand Down Expand Up @@ -54,19 +58,29 @@ func CheckRef(ref, ut, h, us, c, uv, i string) {

var _ = Describe("ref parsing", func() {
Context("complete refs", func() {
t := "OCIRepository"
t := ocireg.Type
s := "mandelsoft/cnudie"
v := "v1"

h := "ghcr.io"
c := "github.com/mandelsoft/ocm"

It("without info", func() {
for _, ut := range []string{"", t} {
for _, us := range []string{"", s} {
for _, uv := range []string{"", v} {
ref := Type(ut) + h + Sub(us) + "//" + c + Vers(uv)
CheckRef(ref, ut, h, us, c, uv, "")
Context("without info", func() {
for _, ut := range []string{t, ""} {
for _, uh := range []string{h, h + ":3030", "localhost", "localhost:3030"} {
for _, us := range []string{"", s} {
for _, uv := range []string{"", v} {
ref := Type(ut) + uh + Sub(us) + "//" + c + Vers(uv)
ut, uh, us, uv := ut, uh, us, uv

It("parses ref "+ref, func() {
if ut == "" && strings.HasPrefix(uh, "localhost") {
CheckRef(ref, ut, "", "", c, uv, uh+Sub(us))
} else {
CheckRef(ref, ut, uh, us, c, uv, "")
}
})
}
}
}
}
Expand Down Expand Up @@ -95,4 +109,14 @@ var _ = Describe("ref parsing", func() {
CheckRef("any::file.io//bla.blob/comp", "any", "file.io", "", "bla.blob/comp", "", "")
})
})

Context("map to spec", func() {
It("handles localhost", func() {
ctx := ocm.New()

ref := Must(ocm.ParseRef("OCIRegistry::localhost:80/test//github.vom/mandelsoft/test"))
spec := Must(ctx.MapUniformRepositorySpec(&ref.UniformRepositorySpec))
Expect(spec).To(Equal(ocireg.NewRepositorySpec("localhost:80", ocireg.NewComponentRepositoryMeta("test", ""))))
})
})
})
57 changes: 50 additions & 7 deletions pkg/contexts/ocm/repositories/genericocireg/uniform.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,70 @@
package genericocireg

import (
"fmt"
"strings"

"github.com/open-component-model/ocm/pkg/contexts/oci/grammar"
"github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ocireg"
"github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compatattr"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
)

func init() {
cpi.RegisterRepositorySpecHandler(&repospechandler{}, "*")
cpi.RegisterRefParseHandler(Type, HandleRef)
}

type repospechandler struct{}

func (h *repospechandler) MapReference(ctx cpi.Context, u *cpi.UniformRepositorySpec) (cpi.RepositorySpec, error) {
if u.Info != "" || u.Host == "" {
return nil, nil
}
var meta *ComponentRepositoryMeta
if u.SubPath != "" {
meta = NewComponentRepositoryMeta(u.SubPath, "")
host := u.Host
subp := u.SubPath

if u.Type == Type {
if u.Info != "" && u.SubPath == "" {
idx := strings.Index(u.Info, grammar.RepositorySeparator)
if idx > 0 {
host = u.Info[:idx]
subp = u.Info[idx+1:]
} else {
host = u.Info
}
} else if u.Host == "" {
return nil, fmt.Errorf("host required for OCI based OCM reference")
}
} else {
if u.Type != "" || u.Info != "" || u.Host == "" {
return nil, nil
}
host = u.Host
}
if subp != "" {
meta = NewComponentRepositoryMeta(subp, "")
}
if compatattr.Get(ctx) {
return NewRepositorySpec(ocireg.NewLegacyRepositorySpec(u.Host), meta), nil
return NewRepositorySpec(ocireg.NewLegacyRepositorySpec(host), meta), nil
}
return NewRepositorySpec(ocireg.NewRepositorySpec(host), meta), nil
}

func HandleRef(u *cpi.UniformRepositorySpec) error {
if u.Host == "" && u.Info != "" && u.SubPath == "" {
host := ""
subp := ""
idx := strings.Index(u.Info, grammar.RepositorySeparator)
if idx > 0 {
host = u.Info[:idx]
subp = u.Info[idx+1:]
} else {
host = u.Info
}
if grammar.HostPortRegexp.MatchString(host) || grammar.DomainPortRegexp.MatchString(host) {
u.Host = host
u.SubPath = subp
u.Info = ""
}
}
return NewRepositorySpec(ocireg.NewRepositorySpec(u.Host), meta), nil
return nil
}

0 comments on commit dce58d1

Please sign in to comment.