Prepare urls.URLBuilder package for export

The URLBuilder is now exported with documentation for its behavior. Its a light
wrapper around gorilla mux that avoids one having to remember exact arguments
take by each route.
This commit is contained in:
Stephen J Day 2014-12-11 21:57:14 -08:00
parent e5b6da80d0
commit 9b872ca150

View file

@ -1,52 +1,61 @@
package registry package urls
import ( import (
"net/http" "net/http"
"net/url" "net/url"
"github.com/docker/docker-registry/api/urls"
"github.com/docker/docker-registry/digest" "github.com/docker/docker-registry/digest"
"github.com/docker/docker-registry/storage"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
type urlBuilder struct { // URLBuilder creates registry API urls from a single base endpoint. It can be
url *url.URL // url root (ie http://localhost/) // used to create urls for use in a registry client or server.
//
// All urls will be created from the given base, including the api version.
// For example, if a root of "/foo/" is provided, urls generated will be fall
// under "/foo/v2/...". Most application will only provide a schema, host and
// port, such as "https://localhost:5000/".
type URLBuilder struct {
root *url.URL // url root (ie http://localhost/)
router *mux.Router router *mux.Router
} }
func newURLBuilder(root *url.URL) *urlBuilder { // NewURLBuilder creates a URLBuilder with provided root url object.
return &urlBuilder{ func NewURLBuilder(root *url.URL) *URLBuilder {
url: root, return &URLBuilder{
router: urls.Router(), root: root,
router: Router(),
} }
} }
func newURLBuilderFromRequest(r *http.Request) *urlBuilder { // NewURLBuilderFromString workes identically to NewURLBuilder except it takes
u := &url.URL{ // a string argument for the root, returning an error if it is not a valid
Scheme: r.URL.Scheme, // url.
Host: r.Host, func NewURLBuilderFromString(root string) (*URLBuilder, error) {
}
return newURLBuilder(u)
}
func newURLBuilderFromString(root string) (*urlBuilder, error) {
u, err := url.Parse(root) u, err := url.Parse(root)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newURLBuilder(u), nil return NewURLBuilder(u), nil
} }
func (ub *urlBuilder) buildBaseURL() (string, error) { // NewURLBuilderFromRequest uses information from an *http.Request to
route := clonedRoute(ub.router, urls.RouteNameBase) // construct the root url.
func NewURLBuilderFromRequest(r *http.Request) *URLBuilder {
u := &url.URL{
Scheme: r.URL.Scheme,
Host: r.Host,
}
baseURL, err := route. return NewURLBuilder(u)
Schemes(ub.url.Scheme). }
Host(ub.url.Host).
URL() // BuildBaseURL constructs a base url for the API, typically just "/v2/".
func (ub *URLBuilder) BuildBaseURL() (string, error) {
route := ub.cloneRoute(RouteNameBase)
baseURL, err := route.URL()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -54,13 +63,11 @@ func (ub *urlBuilder) buildBaseURL() (string, error) {
return baseURL.String(), nil return baseURL.String(), nil
} }
func (ub *urlBuilder) buildTagsURL(name string) (string, error) { // BuildTagsURL constructs a url to list the tags in the named repository.
route := clonedRoute(ub.router, urls.RouteNameTags) func (ub *URLBuilder) BuildTagsURL(name string) (string, error) {
route := ub.cloneRoute(RouteNameTags)
tagsURL, err := route. tagsURL, err := route.URL("name", name)
Schemes(ub.url.Scheme).
Host(ub.url.Host).
URL("name", name)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -68,17 +75,11 @@ func (ub *urlBuilder) buildTagsURL(name string) (string, error) {
return tagsURL.String(), nil return tagsURL.String(), nil
} }
func (ub *urlBuilder) forManifest(m *storage.Manifest) (string, error) { // BuildManifestURL constructs a url for the manifest identified by name and tag.
return ub.buildManifestURL(m.Name, m.Tag) func (ub *URLBuilder) BuildManifestURL(name, tag string) (string, error) {
} route := ub.cloneRoute(RouteNameManifest)
func (ub *urlBuilder) buildManifestURL(name, tag string) (string, error) { manifestURL, err := route.URL("name", name, "tag", tag)
route := clonedRoute(ub.router, urls.RouteNameManifest)
manifestURL, err := route.
Schemes(ub.url.Scheme).
Host(ub.url.Host).
URL("name", name, "tag", tag)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -86,17 +87,11 @@ func (ub *urlBuilder) buildManifestURL(name, tag string) (string, error) {
return manifestURL.String(), nil return manifestURL.String(), nil
} }
func (ub *urlBuilder) forLayer(l storage.Layer) (string, error) { // BuildLayerURL constructs the url for the blob identified by name and dgst.
return ub.buildLayerURL(l.Name(), l.Digest()) func (ub *URLBuilder) BuildBlobURL(name string, dgst digest.Digest) (string, error) {
} route := ub.cloneRoute(RouteNameBlob)
func (ub *urlBuilder) buildLayerURL(name string, dgst digest.Digest) (string, error) { layerURL, err := route.URL("name", name, "digest", dgst.String())
route := clonedRoute(ub.router, urls.RouteNameBlob)
layerURL, err := route.
Schemes(ub.url.Scheme).
Host(ub.url.Host).
URL("name", name, "digest", dgst.String())
if err != nil { if err != nil {
return "", err return "", err
} }
@ -104,13 +99,12 @@ func (ub *urlBuilder) buildLayerURL(name string, dgst digest.Digest) (string, er
return layerURL.String(), nil return layerURL.String(), nil
} }
func (ub *urlBuilder) buildLayerUploadURL(name string) (string, error) { // BuildBlobURL constructs a url to begin a blob upload in the repository
route := clonedRoute(ub.router, urls.RouteNameBlobUpload) // identified by name.
func (ub *URLBuilder) BuildBlobUploadURL(name string) (string, error) {
route := ub.cloneRoute(RouteNameBlobUpload)
uploadURL, err := route. uploadURL, err := route.URL("name", name)
Schemes(ub.url.Scheme).
Host(ub.url.Host).
URL("name", name)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -118,17 +112,14 @@ func (ub *urlBuilder) buildLayerUploadURL(name string) (string, error) {
return uploadURL.String(), nil return uploadURL.String(), nil
} }
func (ub *urlBuilder) forLayerUpload(layerUpload storage.LayerUpload) (string, error) { // BuildBlobUploadChunkURL constructs a url for the upload identified by uuid,
return ub.buildLayerUploadResumeURL(layerUpload.Name(), layerUpload.UUID()) // including any url values. This should generally not be used by clients, as
} // this url is provided by server implementations during the blob upload
// process.
func (ub *URLBuilder) BuildBlobUploadChunkURL(name, uuid string, values ...url.Values) (string, error) {
route := ub.cloneRoute(RouteNameBlobUploadChunk)
func (ub *urlBuilder) buildLayerUploadResumeURL(name, uuid string, values ...url.Values) (string, error) { uploadURL, err := route.URL("name", name, "uuid", uuid)
route := clonedRoute(ub.router, urls.RouteNameBlobUploadChunk)
uploadURL, err := route.
Schemes(ub.url.Scheme).
Host(ub.url.Host).
URL("name", name, "uuid", uuid)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -136,6 +127,17 @@ func (ub *urlBuilder) buildLayerUploadResumeURL(name, uuid string, values ...url
return appendValuesURL(uploadURL, values...).String(), nil return appendValuesURL(uploadURL, values...).String(), nil
} }
// clondedRoute returns a clone of the named route from the router. Routes
// must be cloned to avoid modifying them during url generation.
func (ub *URLBuilder) cloneRoute(name string) *mux.Route {
route := new(mux.Route)
*route = *ub.router.GetRoute(name) // clone the route
return route.
Schemes(ub.root.Scheme).
Host(ub.root.Host)
}
// appendValuesURL appends the parameters to the url. // appendValuesURL appends the parameters to the url.
func appendValuesURL(u *url.URL, values ...url.Values) *url.URL { func appendValuesURL(u *url.URL, values ...url.Values) *url.URL {
merged := u.Query() merged := u.Query()
@ -161,10 +163,3 @@ func appendValues(u string, values ...url.Values) string {
return appendValuesURL(up, values...).String() return appendValuesURL(up, values...).String()
} }
// clondedRoute returns a clone of the named route from the router.
func clonedRoute(router *mux.Router, name string) *mux.Route {
route := new(mux.Route)
*route = *router.GetRoute(name) // clone the route
return route
}