1
0
mirror of https://github.com/openshift/image-registry.git synced 2026-02-05 09:45:55 +01:00

Add v2 registry

This commit is contained in:
Andy Goldstein
2015-03-19 11:41:00 -04:00
commit aea2afb210
3 changed files with 338 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
// Package main contains the main executable for the integrated OpenShift Docker registry 2.0.
package main

View File

@@ -0,0 +1,33 @@
package main
import (
"flag"
"fmt"
"os"
log "github.com/Sirupsen/logrus"
"github.com/openshift/origin/pkg/cmd/dockerregistry"
)
func main() {
flag.Parse()
// TODO convert to flags instead of a config file?
configurationPath := ""
if flag.NArg() > 0 {
configurationPath = flag.Arg(0)
} else if os.Getenv("REGISTRY_CONFIGURATION_PATH") != "" {
configurationPath = os.Getenv("REGISTRY_CONFIGURATION_PATH")
}
if configurationPath == "" {
fmt.Println("configuration path unspecified")
os.Exit(1)
}
configFile, err := os.Open(configurationPath)
if err != nil {
log.Fatalf("Unable to open configuration file: %s", err)
}
dockerregistry.Execute(configFile)
}

View File

@@ -0,0 +1,303 @@
package repository
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"strings"
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
log "github.com/Sirupsen/logrus"
"github.com/docker/distribution"
"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"
)
func init() {
repomw.Register("openshift", repomw.InitFunc(newRepository))
}
type repository struct {
distribution.Repository
// TODO cache this at the app level
registryClient *client.Client
registryAddr string
namespace string
name string
}
// newRepository returns a new repository middleware.
func newRepository(repo distribution.Repository, options map[string]interface{}) (distribution.Repository, error) {
openshiftAddr := os.Getenv("OPENSHIFT_MASTER")
if len(openshiftAddr) == 0 {
return nil, errors.New("OPENSHIFT_MASTER is required")
}
registryAddr := os.Getenv("REGISTRY_URL")
if len(registryAddr) == 0 {
return nil, errors.New("REGISTRY_URL is required")
}
insecure := len(os.Getenv("OPENSHIFT_INSECURE")) > 0
var tlsClientConfig kclient.TLSClientConfig
if !insecure {
caData := os.Getenv("OPENSHIFT_CA_DATA")
if len(caData) == 0 {
return nil, errors.New("OPENSHIFT_CA_DATA is required")
}
certData := os.Getenv("OPENSHIFT_CERT_DATA")
if len(certData) == 0 {
return nil, errors.New("OPENSHIFT_CERT_DATA is required")
}
certKeyData := os.Getenv("OPENSHIFT_KEY_DATA")
if len(certKeyData) == 0 {
return nil, errors.New("OPENSHIFT_KEY_DATA is required")
}
tlsClientConfig = kclient.TLSClientConfig{
CAData: []byte(caData),
CertData: []byte(certData),
KeyData: []byte(certKeyData),
}
}
registryClientConfig := kclient.Config{
Host: openshiftAddr,
TLSClientConfig: tlsClientConfig,
Insecure: insecure,
}
registryClient, err := client.New(&registryClientConfig)
if err != nil {
return nil, fmt.Errorf("Error creating OpenShift client: %s", err)
}
nameParts := strings.SplitN(repo.Name(), "/", 2)
return &repository{
Repository: repo,
registryClient: 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
}
// Tags lists the tags under the named repository.
func (r *repository) Tags() ([]string, error) {
imageRepository, err := r.getImageRepository()
if err != nil {
return []string{}, nil
}
tags := []string{}
for tag := range imageRepository.Status.Tags {
tags = append(tags, tag)
}
return tags, nil
}
// Exists returns true if the manifest specified by dgst exists.
func (r *repository) Exists(dgst digest.Digest) (bool, error) {
image, err := r.getImage(dgst)
if err != nil {
return false, err
}
return image != nil, nil
}
// ExistsByTag returns true if the manifest with tag `tag` exists.
func (r *repository) ExistsByTag(tag string) (bool, error) {
imageRepository, err := r.getImageRepository()
if err != nil {
return false, err
}
_, found := imageRepository.Status.Tags[tag]
return found, nil
}
// Get retrieves the manifest with digest `dgst`.
func (r *repository) Get(dgst digest.Digest) (*manifest.SignedManifest, error) {
_, err := r.getImageStreamImage(dgst)
if err != nil {
return nil, err
}
image, err := r.getImage(dgst)
if err != nil {
return nil, err
}
return r.manifestFromImage(image)
}
// Get retrieves the named manifest, if it exists.
func (r *repository) GetByTag(tag string) (*manifest.SignedManifest, error) {
image, err := r.getImageRepositoryTag(tag)
if err != nil {
// TODO remove when docker 1.6 is out
// Since docker 1.5 doesn't support pull by id, we're simulating pull by id
// against the v2 registry by using a pull spec of the form
// <repo>:<hex portion of digest>, so once we verify we got a 404 from
// getImageRepositoryTag, we construct a digest and attempt to get the
// imageStreamImage using that digest.
// TODO replace with kerrors.IsStatusError when it's rebased in
if err, ok := err.(*kerrors.StatusError); !ok {
log.Errorf("GetByTag: getImageRepositoryTag returned error: %s", err)
return nil, err
} else if err.ErrStatus.Code != http.StatusNotFound {
log.Errorf("GetByTag: getImageRepositoryTag returned non-404: %s", err)
}
// let's try to get by id
dgst, dgstErr := digest.ParseDigest("sha256:" + tag)
if dgstErr != nil {
log.Errorf("GetByTag: unable to parse digest: %s", dgstErr)
return nil, err
}
image, err = r.getImageStreamImage(dgst)
if err != nil {
log.Errorf("GetByTag: getImageStreamImage returned error: %s", err)
return nil, err
}
}
dgst, err := digest.ParseDigest(image.Name)
if err != nil {
return nil, err
}
image, err = r.getImage(dgst)
if err != nil {
return nil, err
}
return r.manifestFromImage(image)
}
// Put creates or updates the named manifest.
func (r *repository) Put(manifest *manifest.SignedManifest) error {
// Resolve the payload in the manifest.
payload, err := manifest.Payload()
if err != nil {
return err
}
// Calculate digest
dgst, err := digest.FromBytes(payload)
if err != nil {
return err
}
// Upload to openshift
irm := imageapi.ImageRepositoryMapping{
TypeMeta: kapi.TypeMeta{
APIVersion: "v1beta1",
Kind: "ImageRepositoryMapping",
},
ObjectMeta: kapi.ObjectMeta{
Namespace: r.namespace,
Name: r.name,
},
Tag: manifest.Tag,
Image: imageapi.Image{
ObjectMeta: kapi.ObjectMeta{
Name: dgst.String(),
},
DockerImageReference: fmt.Sprintf("%s/%s/%s@%s", r.registryAddr, r.namespace, r.name, dgst.String()),
DockerImageManifest: string(payload),
},
}
if err := r.registryClient.ImageRepositoryMappings(r.namespace).Create(&irm); err != nil {
log.Errorf("Error creating ImageRepositoryMapping: %s", err)
return err
}
// Grab each json signature and store them.
signatures, err := manifest.Signatures()
if err != nil {
return err
}
for _, signature := range signatures {
if err := r.Signatures().Put(dgst, signature); err != nil {
log.Errorf("Error storing signature: %s", err)
return err
}
}
return nil
}
// Delete deletes the manifest with digest `dgst`.
func (r *repository) Delete(dgst digest.Digest) error {
return r.registryClient.Images().Delete(dgst.String())
}
// getImageRepository retrieves the ImageRepository for r.
func (r *repository) getImageRepository() (*imageapi.ImageRepository, error) {
return r.registryClient.ImageRepositories(r.namespace).Get(r.name)
}
// getImage retrieves the Image with digest `dgst`. This uses the registry's
// credentials and should ONLY
func (r *repository) getImage(dgst digest.Digest) (*imageapi.Image, error) {
return r.registryClient.Images().Get(dgst.String())
}
// getImageRepositoryTag retrieves the Image with tag `tag` for the ImageRepository
// associated with r.
func (r *repository) getImageRepositoryTag(tag string) (*imageapi.Image, error) {
return r.registryClient.ImageRepositoryTags(r.namespace).Get(r.name, tag)
}
// getImageStreamImage retrieves the Image with digest `dgst` for the ImageRepository
// associated with r. This ensures the user has access to the image.
func (r *repository) getImageStreamImage(dgst digest.Digest) (*imageapi.Image, error) {
// TODO !!! use user credentials, not the registry's !!!
return r.registryClient.ImageStreamImages(r.namespace).Get(r.name, dgst.String())
}
// manifestFromImage converts an Image to a SignedManifest.
func (r *repository) manifestFromImage(image *imageapi.Image) (*manifest.SignedManifest, error) {
dgst, err := digest.ParseDigest(image.Name)
if err != nil {
return nil, err
}
// Fetch the signatures for the manifest
signatures, err := r.Signatures().Get(dgst)
if err != nil {
return nil, err
}
jsig, err := libtrust.NewJSONSignature([]byte(image.DockerImageManifest), signatures...)
if err != nil {
return nil, err
}
// Extract the pretty JWS
raw, err := jsig.PrettySignature("signatures")
if err != nil {
return nil, err
}
var sm manifest.SignedManifest
if err := json.Unmarshal(raw, &sm); err != nil {
return nil, err
}
return &sm, err
}