Skip to content

Commit

Permalink
Refactored OSO's code depending on docker/distribution
Browse files Browse the repository at this point in the history
- Repository components 1 character long allowed
- Use new docker/distribution/registry/api/errcode package
- Repository middleware now implements ManifestService.

Signed-off-by: Michal Minar <[email protected]>
  • Loading branch information
Michal Minar committed Oct 26, 2015
1 parent e92eaa1 commit dc0cce0
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 79 deletions.
35 changes: 20 additions & 15 deletions pkg/dockerregistry/server/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (

ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/distribution/registry/api/v2"
"github.com/docker/distribution/registry/handlers"
"github.com/docker/distribution/registry/storage"
storagedriver "github.com/docker/distribution/registry/storage/driver"
gorillahandlers "github.com/gorilla/handlers"
)
Expand Down Expand Up @@ -40,17 +42,20 @@ func (bh *blobHandler) Delete(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()

if len(bh.Digest) == 0 {
bh.Errors.Push(v2.ErrorCodeBlobUnknown)
w.WriteHeader(http.StatusNotFound)
bh.Errors = append(bh.Errors, v2.ErrorCodeBlobUnknown)
return
}

err := bh.Registry().Blobs().Delete(bh.Digest)
bd, err := storage.RegistryBlobDeleter(bh.Namespace())
if err != nil {
bh.Errors = append(bh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
return
}
err = bd.Delete(bh, bh.Digest)
if err != nil {
// Ignore PathNotFoundError
if _, ok := err.(storagedriver.PathNotFoundError); !ok {
bh.Errors.PushErr(fmt.Errorf("error deleting blob %q: %v", bh.Digest, err))
w.WriteHeader(http.StatusBadRequest)
bh.Errors = append(bh.Errors, errcode.ErrorCodeUnknown.WithDetail(fmt.Errorf("error deleting blob %q: %v", bh.Digest, err)))
return
}
}
Expand Down Expand Up @@ -86,17 +91,15 @@ func (lh *layerHandler) Delete(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()

if len(lh.Digest) == 0 {
lh.Errors.Push(v2.ErrorCodeBlobUnknown)
w.WriteHeader(http.StatusNotFound)
lh.Errors = append(lh.Errors, v2.ErrorCodeBlobUnknown)
return
}

err := lh.Repository.Layers().Delete(lh.Digest)
err := lh.Repository.Blobs(lh).Delete(lh, lh.Digest)
if err != nil {
// Ignore PathNotFoundError
if _, ok := err.(storagedriver.PathNotFoundError); !ok {
lh.Errors.PushErr(fmt.Errorf("error unlinking layer %q from repo %q: %v", lh.Digest, lh.Repository.Name(), err))
w.WriteHeader(http.StatusBadRequest)
lh.Errors = append(lh.Errors, errcode.ErrorCodeUnknown.WithDetail(fmt.Errorf("error unlinking layer %q from repo %q: %v", lh.Digest, lh.Repository.Name(), err)))
return
}
}
Expand Down Expand Up @@ -133,17 +136,19 @@ func (mh *manifestHandler) Delete(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()

if len(mh.Digest) == 0 {
mh.Errors.Push(v2.ErrorCodeManifestUnknown)
w.WriteHeader(http.StatusNotFound)
mh.Errors = append(mh.Errors, v2.ErrorCodeManifestUnknown)
return
}

err := mh.Repository.Manifests().Delete(mh.Context, mh.Digest)
manService, err := mh.Repository.Manifests(mh)
if err != nil {
mh.Errors = append(mh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
}
err = manService.Delete(mh.Digest)
if err != nil {
// Ignore PathNotFoundError
if _, ok := err.(storagedriver.PathNotFoundError); !ok {
mh.Errors.PushErr(fmt.Errorf("error deleting repo %q, manifest %q: %v", mh.Repository.Name(), mh.Digest, err))
w.WriteHeader(http.StatusBadRequest)
mh.Errors = append(mh.Errors, errcode.ErrorCodeUnknown.WithDetail(fmt.Errorf("error deleting repo %q, manifest %q: %v", mh.Repository.Name(), mh.Digest, err)))
return
}
}
Expand Down
17 changes: 7 additions & 10 deletions pkg/dockerregistry/server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import (
"net/http"
"strings"

kerrors "k8s.io/kubernetes/pkg/api/errors"

log "github.com/Sirupsen/logrus"
ctxu "github.com/docker/distribution/context"
context "github.com/docker/distribution/context"
registryauth "github.com/docker/distribution/registry/auth"
kerrors "k8s.io/kubernetes/pkg/api/errors"

authorizationapi "github.com/openshift/origin/pkg/authorization/api"
"github.com/openshift/origin/pkg/client"
"golang.org/x/net/context"
)

func init() {
Expand Down Expand Up @@ -76,17 +75,15 @@ func (ac *authChallenge) Error() string {
return ac.err.Error()
}

// ServeHTTP handles writing the challenge response
// by setting the challenge header and status code.
func (ac *authChallenge) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// SetHeaders sets the basic challenge header on the response.
func (ac *authChallenge) SetHeaders(w http.ResponseWriter) {
// WWW-Authenticate response challenge header.
// See https://tools.ietf.org/html/rfc6750#section-3
str := fmt.Sprintf("Basic realm=%s", ac.realm)
if ac.err != nil {
str = fmt.Sprintf("%s,error=%q", str, ac.Error())
}
w.Header().Add("WWW-Authenticate", str)
w.WriteHeader(http.StatusUnauthorized)
w.Header().Set("WWW-Authenticate", str)
}

// wrapErr wraps errors related to authorization in an authChallenge error that will present a WWW-Authenticate challenge response
Expand All @@ -110,7 +107,7 @@ func (ac *AccessController) wrapErr(err error) error {
// origin/pkg/cmd/dockerregistry/dockerregistry.go#Execute
// docker/distribution/registry/handlers/app.go#appendAccessRecords
func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...registryauth.Access) (context.Context, error) {
req, err := ctxu.GetRequest(ctx)
req, err := context.GetRequest(ctx)
if err != nil {
return nil, ac.wrapErr(err)
}
Expand Down
146 changes: 109 additions & 37 deletions pkg/dockerregistry/server/repositorymiddleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ import (

log "github.com/Sirupsen/logrus"
"github.com/docker/distribution"
"github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest"
repomw "github.com/docker/distribution/registry/middleware/repository"
"github.com/docker/libtrust"
"github.com/openshift/origin/pkg/client"
imageapi "github.com/openshift/origin/pkg/image/api"
"golang.org/x/net/context"
kapi "k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"

"github.com/openshift/origin/pkg/client"
imageapi "github.com/openshift/origin/pkg/image/api"
)

func init() {
Expand All @@ -29,14 +32,17 @@ func init() {
type repository struct {
distribution.Repository

registryClient *client.Client
registryAddr string
namespace string
name string
ctx context.Context
registryInterface client.Interface
registryAddr string
namespace string
name string
}

var _ distribution.ManifestService = &repository{}

// newRepository returns a new repository middleware.
func newRepository(repo distribution.Repository, options map[string]interface{}) (distribution.Repository, error) {
func newRepository(ctx context.Context, repo distribution.Repository, options map[string]interface{}) (distribution.Repository, error) {
registryAddr := os.Getenv("REGISTRY_URL")
if len(registryAddr) == 0 {
return nil, errors.New("REGISTRY_URL is required")
Expand All @@ -53,22 +59,27 @@ func newRepository(repo distribution.Repository, options map[string]interface{})
}

return &repository{
Repository: repo,
registryClient: registryClient,
registryAddr: registryAddr,
namespace: nameParts[0],
name: nameParts[1],
Repository: repo,
registryInterface: registryClient,
registryAddr: registryAddr,
namespace: nameParts[0],
name: nameParts[1],
}, nil
}

// Manifests returns r, which implements distribution.ManifestService.
func (r *repository) Manifests() distribution.ManifestService {
return r
func (r *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) {
if r.ctx != ctx {
return r, nil
}
repo := repository(*r)
repo.ctx = ctx
return &repo, nil
}

// Tags lists the tags under the named repository.
func (r *repository) Tags(ctx context.Context) ([]string, error) {
imageStream, err := r.getImageStream(ctx)
func (r *repository) Tags() ([]string, error) {
imageStream, err := r.getImageStream()
if err != nil {
return []string{}, nil
}
Expand All @@ -81,7 +92,7 @@ func (r *repository) Tags(ctx context.Context) ([]string, error) {
}

// Exists returns true if the manifest specified by dgst exists.
func (r *repository) Exists(ctx context.Context, dgst digest.Digest) (bool, error) {
func (r *repository) Exists(dgst digest.Digest) (bool, error) {
image, err := r.getImage(dgst)
if err != nil {
return false, err
Expand All @@ -90,8 +101,8 @@ func (r *repository) Exists(ctx context.Context, dgst digest.Digest) (bool, erro
}

// ExistsByTag returns true if the manifest with tag `tag` exists.
func (r *repository) ExistsByTag(ctx context.Context, tag string) (bool, error) {
imageStream, err := r.getImageStream(ctx)
func (r *repository) ExistsByTag(tag string) (bool, error) {
imageStream, err := r.getImageStream()
if err != nil {
return false, err
}
Expand All @@ -100,8 +111,8 @@ func (r *repository) ExistsByTag(ctx context.Context, tag string) (bool, error)
}

// Get retrieves the manifest with digest `dgst`.
func (r *repository) Get(ctx context.Context, dgst digest.Digest) (*manifest.SignedManifest, error) {
if _, err := r.getImageStreamImage(ctx, dgst); err != nil {
func (r *repository) Get(dgst digest.Digest) (*manifest.SignedManifest, error) {
if _, err := r.getImageStreamImage(dgst); err != nil {
log.Errorf("Error retrieving ImageStreamImage %s/%s@%s: %v", r.namespace, r.name, dgst.String(), err)
return nil, err
}
Expand All @@ -115,9 +126,42 @@ func (r *repository) Get(ctx context.Context, dgst digest.Digest) (*manifest.Sig
return r.manifestFromImage(image)
}

// Enumerate retrieves digests of manifest revisions in particular repository
func (r *repository) Enumerate() ([]digest.Digest, error) {
if _, err := r.getImageStream(); err != nil {
if kerrors.IsNotFound(err) {
err = distribution.ErrRepositoryUnknown{fmt.Sprintf("%s/%s", r.namespace, r.name)}
} else {
err = fmt.Errorf("Failed to get image stream %s/%s: %v", r.namespace, r.name, err)
}
return nil, err
}
images, err := r.getImages()
if err != nil {
return nil, err
}

res := make([]digest.Digest, 0, len(images.Items))
for _, img := range images.Items {
dgst, err := digest.ParseDigest(img.Name)
if err != nil {
log.Warnf("Failed to parse image name %q into digest: %v", img.Name, err)
} else {
res = append(res, dgst)
}
}

return res, nil
}

// GetByTag retrieves the named manifest with the provided tag
func (r *repository) GetByTag(ctx context.Context, tag string) (*manifest.SignedManifest, error) {
imageStreamTag, err := r.getImageStreamTag(ctx, tag)
func (r *repository) GetByTag(tag string, options ...distribution.ManifestServiceOption) (*manifest.SignedManifest, error) {
for _, opt := range options {
if err := opt(r); err != nil {
return nil, err
}
}
imageStreamTag, err := r.getImageStreamTag(tag)
if err != nil {
log.Errorf("Error getting ImageStreamTag %q: %v", tag, err)
return nil, err
Expand All @@ -140,7 +184,7 @@ func (r *repository) GetByTag(ctx context.Context, tag string) (*manifest.Signed
}

// Put creates or updates the named manifest.
func (r *repository) Put(ctx context.Context, manifest *manifest.SignedManifest) error {
func (r *repository) Put(manifest *manifest.SignedManifest) error {
// Resolve the payload in the manifest.
payload, err := manifest.Payload()
if err != nil {
Expand Down Expand Up @@ -172,7 +216,7 @@ func (r *repository) Put(ctx context.Context, manifest *manifest.SignedManifest)
},
}

if err := r.registryClient.ImageStreamMappings(r.namespace).Create(&ism); err != nil {
if err := r.registryInterface.ImageStreamMappings(r.namespace).Create(&ism); err != nil {
// if the error was that the image stream wasn't found, try to auto provision it
statusErr, ok := err.(*kerrors.StatusError)
if !ok {
Expand All @@ -192,7 +236,7 @@ func (r *repository) Put(ctx context.Context, manifest *manifest.SignedManifest)
},
}

client, ok := UserClientFrom(ctx)
client, ok := UserClientFrom(r.ctx)
if !ok {
log.Errorf("Error creating user client to auto provision image stream: Origin user client unavailable")
return statusErr
Expand All @@ -205,7 +249,7 @@ func (r *repository) Put(ctx context.Context, manifest *manifest.SignedManifest)

// try to create the ISM again
if err := unversioned.RetryOnConflict(unversioned.DefaultRetry, func() error {
return r.registryClient.ImageStreamMappings(r.namespace).Create(&ism)
return r.registryInterface.ImageStreamMappings(r.namespace).Create(&ism)
}); err != nil {
log.Errorf("Error creating image stream mapping: %s", err)
return err
Expand All @@ -231,30 +275,58 @@ func (r *repository) Put(ctx context.Context, manifest *manifest.SignedManifest)
// Delete deletes the manifest with digest `dgst`. Note: Image resources
// in OpenShift are deleted via 'oadm prune images'. This function deletes
// the content related to the manifest in the registry's storage (signatures).
func (r *repository) Delete(ctx context.Context, dgst digest.Digest) error {
return r.Repository.Manifests().Delete(ctx, dgst)
func (r *repository) Delete(dgst digest.Digest) error {
manServ, err := r.Repository.Manifests(r.ctx)
if err != nil {
return err
}
return manServ.Delete(dgst)
}

// getImageStream retrieves the ImageStream for r.
func (r *repository) getImageStream(ctx context.Context) (*imageapi.ImageStream, error) {
return r.registryClient.ImageStreams(r.namespace).Get(r.name)
func (r *repository) getImageStream() (*imageapi.ImageStream, error) {
return r.registryInterface.ImageStreams(r.namespace).Get(r.name)
}

// getImage retrieves the Image with digest `dgst`.
func (r *repository) getImage(dgst digest.Digest) (*imageapi.Image, error) {
return r.registryClient.Images().Get(dgst.String())
return r.registryInterface.Images().Get(dgst.String())
}

// getImages retrieves repository's ImageList.
func (r *repository) getImages() (*imageapi.ImageList, error) {
imgList, err := r.registryInterface.Images().List(labels.Everything(), fields.Everything())
if err != nil {
return nil, err
}

res := imageapi.ImageList{}
for _, img := range imgList.Items {
if img.Annotations == nil || img.Annotations[imageapi.ManagedByOpenShiftAnnotation] != "true" {
continue
}
ref, err := imageapi.ParseDockerImageReference(img.DockerImageReference)
if err != nil {
continue
}
if ref.Namespace != r.namespace || ref.Name != r.name {
continue
}
res.Items = append(res.Items, img)
}
return &res, nil
}

// getImageStreamTag retrieves the Image with tag `tag` for the ImageStream
// associated with r.
func (r *repository) getImageStreamTag(ctx context.Context, tag string) (*imageapi.ImageStreamTag, error) {
return r.registryClient.ImageStreamTags(r.namespace).Get(r.name, tag)
func (r *repository) getImageStreamTag(tag string) (*imageapi.ImageStreamTag, error) {
return r.registryInterface.ImageStreamTags(r.namespace).Get(r.name, tag)
}

// getImageStreamImage retrieves the Image with digest `dgst` for the ImageStream
// associated with r. This ensures the image belongs to the image stream.
func (r *repository) getImageStreamImage(ctx context.Context, dgst digest.Digest) (*imageapi.ImageStreamImage, error) {
return r.registryClient.ImageStreamImages(r.namespace).Get(r.name, dgst.String())
func (r *repository) getImageStreamImage(dgst digest.Digest) (*imageapi.ImageStreamImage, error) {
return r.registryInterface.ImageStreamImages(r.namespace).Get(r.name, dgst.String())
}

// manifestFromImage converts an Image to a SignedManifest.
Expand Down
Loading

0 comments on commit dc0cce0

Please sign in to comment.