Skip to content
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

improve ocm ref parsing #337

Merged
merged 2 commits into from
Apr 16, 2023
Merged
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
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
}