diff --git a/cache/register_for_beta.go b/cache/register_for_beta.go index 80bdb492..5f045f74 100644 --- a/cache/register_for_beta.go +++ b/cache/register_for_beta.go @@ -46,9 +46,9 @@ func (ds *dataStore) RegisterForBeta(ctx echo.Context) error { err := validateEmail(body["email"]) if err != nil { - return ctx.JSON(http.StatusBadRequest, echo.Map{ - "error": "invalid email format, please try again", - }) + return ctx.JSON(http.StatusBadRequest, echo.Map{ + "error": "invalid email format, please try again", + }) } key := []byte("email") diff --git a/cache/store.go b/cache/store.go index d8fc1d04..9d7f128d 100644 --- a/cache/store.go +++ b/cache/store.go @@ -296,7 +296,7 @@ func (ds *dataStore) ListAll() ([]byte, error) { } func (ds *dataStore) ListWithPrefix(prefix []byte) ([]byte, error) { - var buf []*types.LayerRef + var buf []byte err := ds.db.View(func(txn *badger.Txn) error { it := txn.NewIterator(badger.DefaultIteratorOptions) @@ -304,12 +304,8 @@ func (ds *dataStore) ListWithPrefix(prefix []byte) ([]byte, error) { for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { item := it.Item() err := item.Value(func(v []byte) error { - var layerRef types.LayerRef - if err := json.Unmarshal(v, &layerRef); err != nil { - return err - } - - buf = append(buf, &layerRef) + buf = make([]byte, len(v)) + copy(buf, v) return nil }) if err != nil { @@ -318,11 +314,7 @@ func (ds *dataStore) ListWithPrefix(prefix []byte) ([]byte, error) { } return nil }) - if err != nil { - return nil, err - } - - return json.Marshal(buf) + return buf, err } func (ds *dataStore) Delete(key []byte) error { diff --git a/config/config.go b/config/config.go index 494efbed..b08d8b2d 100644 --- a/config/config.go +++ b/config/config.go @@ -9,13 +9,13 @@ import ( type ( RegistryConfig struct { - Debug bool `mapstructure:"debug"` - Environment string `mapstructure:"environment"` - Host string `mapstructure:"host"` - Port uint `mapstructure:"port"` - SkynetPortalURL string `mapstructure:"skynet_portal_url"` - SigningSecret string `mapstructure:"signing_secret"` - SkynetConfig SkynetConfig `mapstructure:"skynet_config"` + Debug bool `mapstructure:"debug"` + Environment string `mapstructure:"environment"` + Host string `mapstructure:"host"` + Port uint `mapstructure:"port"` + SkynetPortalURL string `mapstructure:"skynet_portal_url"` + SigningSecret string `mapstructure:"signing_secret"` + SkynetConfig SkynetConfig `mapstructure:"skynet_config"` } SkynetConfig struct { diff --git a/main.go b/main.go index 0688a4b1..6b924bda 100644 --- a/main.go +++ b/main.go @@ -116,6 +116,9 @@ func main() { e.Add(http.MethodGet, "/v2/", reg.ApiVersion, BasicAuth(authSvc.BasicAuth)) + ///GET /v2//tags/list + router.Add(http.MethodGet, "/tags/list", reg.ListTags) + router.Add(http.MethodDelete, "/blobs/:digest", reg.DeleteLayer) router.Add(http.MethodDelete, "/manifests/:digest", reg.DeleteImage) diff --git a/registry/v2/registry.go b/registry/v2/registry.go index cefd97f2..bcbfe14a 100644 --- a/registry/v2/registry.go +++ b/registry/v2/registry.go @@ -7,6 +7,8 @@ import ( "io" "net/http" "path" + "sort" + "strconv" "strings" "sync" @@ -367,7 +369,52 @@ func (r *registry) DeleteImage(ctx echo.Context) error { return ctx.NoContent(http.StatusAccepted) } +// Content discovery GET /v2//tags/list + +func (r *registry) ListTags(ctx echo.Context) error { + namespace := ctx.Param("username") + "/" + ctx.Param("imagename") + limit := ctx.QueryParam("n") + + l, err := r.localCache.ListWithPrefix([]byte(namespace)) + if err != nil { + errMsg := r.errorResponse(RegistryErrorCodeTagInvalid, err.Error(), nil) + return ctx.JSONBlob(http.StatusNotFound, errMsg) + } + var md types.Metadata + err = json.Unmarshal(l, &md) + if err != nil { + errMsg := r.errorResponse(RegistryErrorCodeTagInvalid, err.Error(), nil) + return ctx.JSONBlob(http.StatusNotFound, errMsg) + } + var tags []string + for _, v := range md.Manifest.Config { + tags = append(tags, v.Reference) + } + if limit != "" { + n, err := strconv.ParseInt(limit, 10, 32) + if err != nil { + errMsg := r.errorResponse(RegistryErrorCodeTagInvalid, err.Error(), nil) + return ctx.JSONBlob(http.StatusNotFound, errMsg) + } + if n > 0 { + tags = tags[0:n] + } + if n == 0 { + tags = []string{} + } + } + sort.Strings(tags) + return ctx.JSON(http.StatusOK, echo.Map{ + "name": namespace, + "tags": tags, + }) +} +func (r *registry) List(ctx echo.Context) error { + return fmt.Errorf("error") +} + // GET /v2//blobs/ + func (r *registry) PullLayer(ctx echo.Context) error { namespace := ctx.Param("username") + "/" + ctx.Param("imagename") clientDigest := ctx.Param("digest") @@ -536,13 +583,6 @@ func (r *registry) PushLayer(ctx echo.Context) error { return ctx.NoContent(http.StatusAccepted) } -func (r *registry) ListTags(ctx echo.Context) error { - return nil -} -func (r *registry) List(ctx echo.Context) error { - return nil -} - // Should also look into 401 Code // https://docs.docker.com/registry/spec/api/ func (r *registry) ApiVersion(ctx echo.Context) error { diff --git a/registry/v2/types.go b/registry/v2/types.go index 7b789416..7387975b 100644 --- a/registry/v2/types.go +++ b/registry/v2/types.go @@ -153,9 +153,11 @@ type Registry interface { LayerExists(ctx echo.Context) error // GET /v2//manifests/ + PullManifest(ctx echo.Context) error // PUT /v2//manifests/ + PushManifest(ctx echo.Context) error // Push individual layers first, then upload a signed manifest @@ -173,6 +175,7 @@ type Registry interface { // Range: bytes=0- // Content-Length: 0 // Docker-Upload-UUID: + PushImage(ctx echo.Context) error StartUpload(ctx echo.Context) error