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

Add RC1 Support #12

Merged
merged 13 commits into from
May 19, 2022
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ require (
github.com/ncw/swift v1.0.47
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.1
github.com/oras-project/artifacts-spec v1.0.0-draft.1
github.com/oras-project/artifacts-spec v1.0.0-rc.1
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.0.0
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/oras-project/artifacts-spec v1.0.0-draft.1 h1:AyD++MU8sif5eyyvPbT5qskkJA9VZynYVoroVFTXPLM=
github.com/oras-project/artifacts-spec v1.0.0-draft.1/go.mod h1:Xch2aLzSwtkhbFFN6LUzTfLtukYvMMdXJ4oZ8O7BOdc=
github.com/oras-project/artifacts-spec v1.0.0-rc.1 h1:bCHf9mPbrgiNwQFyVzBX79BYZVAl0OUrmvICZOCOwts=
github.com/oras-project/artifacts-spec v1.0.0-rc.1/go.mod h1:Xch2aLzSwtkhbFFN6LUzTfLtukYvMMdXJ4oZ8O7BOdc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
10 changes: 10 additions & 0 deletions registry/api/v2/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,14 @@ var (
to return) is not an integer, or "n" is negative.`,
HTTPStatusCode: http.StatusBadRequest,
})

// ErrorCodeMalformedNextToken is returned when uploading a blob if the
// provided digest does not match the blob contents.
ErrorCodeMalformedNextToken = errcode.Register(errGroup, errcode.ErrorDescriptor{

Choose a reason for hiding this comment

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

I've been giving this some thought myself, but we may want to introduce some way to register errors from the extension API itself.

Value: "MALFORMED_NEXTTOKEN",
Message: "provided nextToken is invalid",
Description: `Returned if a client provides a non-empty nextToken value that
cannot be properly parsed`,
HTTPStatusCode: http.StatusBadRequest,
})
)
4 changes: 2 additions & 2 deletions registry/api/v2/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ func ExtendRoute(ns, ext, component string, template RouteDescriptor, nameRequir
path := routeDescriptorsMap[RouteNameBase].Path
if nameRequired {
name = RouteNameExtensionsRepository
path += "{name:" + reference.NameRegexp.String() + "}"
path += "{name:" + reference.NameRegexp.String() + "}/"
akashsinghal marked this conversation as resolved.
Show resolved Hide resolved
}
name = fmt.Sprintf("%s-%s-%s-%s", name, ns, ext, component)
path = fmt.Sprintf("%s/_%s/%s/%s", path, ns, ext, component)
path = fmt.Sprintf("%s_%s/%s/%s", path, ns, ext, component)

desc := template
desc.Name = name
Expand Down
23 changes: 13 additions & 10 deletions registry/extension/extension.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package extension

import (
"context"
c "context"
"fmt"
"net/http"
Expand Down Expand Up @@ -72,27 +71,31 @@ type EnumerateExtension struct {
var extensions map[string]InitExtensionNamespace
var extensionsNamespaces map[string]Namespace

func EnumerateRegistered(ctx context.Context) (enumeratedExtensions []EnumerateExtension) {
func EnumerateRegistered(ctx Context) (enumeratedExtensions []EnumerateExtension) {
for _, namespace := range extensionsNamespaces {
enumerateExtension := EnumerateExtension{
Name: namespace.GetNamespaceName(),
Url: namespace.GetNamespaceUrl(),
Description: namespace.GetNamespaceDescription(),
Endpoints: []string{},
}

registryScoped := namespace.GetRegistryRoutes()
for _, regScoped := range registryScoped {
path := fmt.Sprintf("_%s/%s/%s", regScoped.Namespace, regScoped.Extension, regScoped.Component)
enumerateExtension.Endpoints = append(enumerateExtension.Endpoints, path)
scopedRoutes := namespace.GetRepositoryRoutes()

// if the repository is not set in the context, scope is registry wide
if ctx.Repository == nil {
scopedRoutes = namespace.GetRegistryRoutes()
}

repositoryScoped := namespace.GetRepositoryRoutes()
for _, repScoped := range repositoryScoped {
path := fmt.Sprintf("_%s/%s/%s", repScoped.Namespace, repScoped.Extension, repScoped.Component)
for _, route := range scopedRoutes {
path := fmt.Sprintf("_%s/%s/%s", route.Namespace, route.Extension, route.Component)
enumerateExtension.Endpoints = append(enumerateExtension.Endpoints, path)
}

enumeratedExtensions = append(enumeratedExtensions, enumerateExtension)
// add extension to list if endpoints exist
if len(enumerateExtension.Endpoints) > 0 {

Choose a reason for hiding this comment

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

Just looking for some context on the changes to this file.

  1. Previously, both repository and registry routes were returned but now it seems to be one of the two?
  2. Why are extensions added only if endpoints exist - when would that not be the case?

Copy link
Author

Choose a reason for hiding this comment

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

  1. Yes that's correct. So previously, both routes were returned but the subset of routes we want to return depends on if the discover request is at the registry or repository level. The distinction is now being made at the EnumerateRegistered level. If the Repository field in the context is nil, then we know that we are operating at the registry level and only want to return registry level routes for that extension namespace.
  2. It's possible that for an extension namespace, that there doesn't exist any routes at the scoped registry or repository level. In that case, we wouldn't want to add the enumerateExtension object to the list since no endpoints were added. The referrers extension would be an example: referrers only operates at the repository level but if we are enumerating at the registry level, then the scopedRoutes would be empty and we wouldn't want to add to the enumeratedExtensions list.

enumeratedExtensions = append(enumeratedExtensions, enumerateExtension)
}
}

return enumeratedExtensions
Expand Down
8 changes: 3 additions & 5 deletions registry/extension/oci/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
)

type discoverGetAPIResponse struct {
Name string `json:"name"`
Extensions []extension.EnumerateExtension `json:"extensions"`
}

Expand All @@ -20,13 +19,13 @@ type extensionHandler struct {
storageDriver driver.StorageDriver
}

func (th *extensionHandler) getExtensions(w http.ResponseWriter, r *http.Request) {
func (eh *extensionHandler) getExtensions(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()

w.Header().Set("Content-Type", "application/json")

// get list of extension information seperated at the namespace level
enumeratedExtensions := extension.EnumerateRegistered(r.Context())
enumeratedExtensions := extension.EnumerateRegistered(*eh.Context)

// remove the oci extension so it's not returned by discover
for i, e := range enumeratedExtensions {
Expand All @@ -37,10 +36,9 @@ func (th *extensionHandler) getExtensions(w http.ResponseWriter, r *http.Request

enc := json.NewEncoder(w)
if err := enc.Encode(discoverGetAPIResponse{
Name: th.Repository.Named().Name(),
Extensions: enumeratedExtensions,
}); err != nil {
th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
eh.Errors = append(eh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
return
}
}
41 changes: 30 additions & 11 deletions registry/extension/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ func (o *ociNamespace) GetManifestHandlers(repo distribution.Repository, blobSto
}

// GetRepositoryRoutes returns a list of extension routes scoped at a repository level
func (d *ociNamespace) GetRepositoryRoutes() []extension.Route {
func (o *ociNamespace) GetRepositoryRoutes() []extension.Route {
var routes []extension.Route

if d.discoverEnabled {
if o.discoverEnabled {
routes = append(routes, extension.Route{
Namespace: namespaceName,
Extension: extensionName,
Expand All @@ -87,38 +87,57 @@ func (d *ociNamespace) GetRepositoryRoutes() []extension.Route {
},
},
},
Dispatcher: d.discoverDispatcher,
Dispatcher: o.discoverDispatcher,
})
}

return routes
}

// GetRegistryRoutes returns a list of extension routes scoped at a registry level
// There are no registry scoped routes exposed by this namespace
func (d *ociNamespace) GetRegistryRoutes() []extension.Route {
return nil
func (o *ociNamespace) GetRegistryRoutes() []extension.Route {
var routes []extension.Route

if o.discoverEnabled {
routes = append(routes, extension.Route{
Namespace: namespaceName,
Extension: extensionName,
Component: discoverComponentName,
Descriptor: v2.RouteDescriptor{
Entity: "Extension",
Methods: []v2.MethodDescriptor{
{
Method: "GET",
Description: "Get all extensions enabled for a registry.",
},
},
},
Dispatcher: o.discoverDispatcher,
})
}

return routes
}

// GetNamespaceName returns the name associated with the namespace
func (d *ociNamespace) GetNamespaceName() string {
func (o *ociNamespace) GetNamespaceName() string {
return namespaceName
}

// GetNamespaceUrl returns the url link to the documentation where the namespace's extension and endpoints are defined
func (d *ociNamespace) GetNamespaceUrl() string {
func (o *ociNamespace) GetNamespaceUrl() string {
return namespaceUrl
}

// GetNamespaceDescription returns the description associated with the namespace
func (d *ociNamespace) GetNamespaceDescription() string {
func (o *ociNamespace) GetNamespaceDescription() string {
return namespaceDescription
}

func (d *ociNamespace) discoverDispatcher(ctx *extension.Context, r *http.Request) http.Handler {
func (o *ociNamespace) discoverDispatcher(ctx *extension.Context, r *http.Request) http.Handler {
extensionHandler := &extensionHandler{
Context: ctx,
storageDriver: d.storageDriver,
storageDriver: o.storageDriver,
}

return handlers.MethodHandler{
Expand Down
14 changes: 14 additions & 0 deletions registry/extension/oras/artifactmanifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ func (a Manifest) ArtifactType() string {
return a.inner.ArtifactType
}

// Annotations returns the annotations of this ORAS artifact.
func (a Manifest) Annotations() map[string]string {
return a.inner.Annotations
}

// MediaType returns the media type of this ORAS artifact.
func (a Manifest) MediaType() string {
return a.inner.MediaType
}

// References returns the distribution descriptors for the referenced blobs.
func (a Manifest) References() []distribution.Descriptor {
blobs := make([]distribution.Descriptor, len(a.inner.Blobs))
Expand Down Expand Up @@ -79,6 +89,10 @@ func (d *DeserializedManifest) UnmarshalJSON(b []byte) error {
if man.ArtifactType == "" {
return errors.New("artifactType cannot be empty")
}
if man.MediaType != v1.MediaTypeArtifactManifest {
return errors.New("mediaType is invalid")
}

d.inner = man

return nil
Expand Down
21 changes: 20 additions & 1 deletion registry/extension/oras/artifactmanifesthandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ import (
"encoding/json"
"errors"
"path"
"time"

"github.com/distribution/distribution/v3"
dcontext "github.com/distribution/distribution/v3/context"
"github.com/distribution/distribution/v3/registry/storage/driver"
"github.com/opencontainers/go-digest"
v1 "github.com/oras-project/artifacts-spec/specs-go/v1"
)

var (
errInvalidArtifactType = errors.New("artifactType invalid")
errInvalidMediaType = errors.New("mediaType invalid")
errInvalidCreatedAnnotation = errors.New("failed to parse created time")
akashsinghal marked this conversation as resolved.
Show resolved Hide resolved
)

// artifactManifestHandler is a ManifestHandler that covers ORAS Artifacts.
Expand Down Expand Up @@ -75,7 +83,18 @@ func (amh *artifactManifestHandler) verifyManifest(ctx context.Context, dm Deser
var errs distribution.ErrManifestVerification

if dm.ArtifactType() == "" {
errs = append(errs, distribution.ErrManifestVerification{errors.New("artifactType invalid")})
errs = append(errs, errInvalidArtifactType)
}

if dm.MediaType() != v1.MediaTypeArtifactManifest {
errs = append(errs, errInvalidMediaType)
}

if createdAt, ok := dm.Annotations()[createAnnotationName]; ok {
_, err := time.Parse(time.RFC3339, createdAt)
if err != nil {
errs = append(errs, errInvalidCreatedAnnotation)
}
}

if !skipDependencyVerification {
Expand Down
Loading