Skip to content

Commit

Permalink
[WIP] Add App struct for application-scoped data
Browse files Browse the repository at this point in the history
Signed-off-by: Oleg Bulatov <[email protected]>
  • Loading branch information
Oleg Bulatov committed Aug 30, 2017
1 parent 7999046 commit 1fdaada
Show file tree
Hide file tree
Showing 16 changed files with 293 additions and 227 deletions.
73 changes: 1 addition & 72 deletions pkg/cmd/dockerregistry/dockerregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (
"github.com/docker/distribution/configuration"
"github.com/docker/distribution/context"
"github.com/docker/distribution/health"
"github.com/docker/distribution/registry/auth"
"github.com/docker/distribution/registry/handlers"
"github.com/docker/distribution/registry/storage"
"github.com/docker/distribution/registry/storage/driver/factory"
"github.com/docker/distribution/uuid"
Expand All @@ -45,7 +43,6 @@ import (
"github.com/openshift/origin/pkg/cmd/server/crypto"
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
"github.com/openshift/origin/pkg/dockerregistry/server"
"github.com/openshift/origin/pkg/dockerregistry/server/api"
"github.com/openshift/origin/pkg/dockerregistry/server/audit"
"github.com/openshift/origin/pkg/dockerregistry/server/client"
registryconfig "github.com/openshift/origin/pkg/dockerregistry/server/configuration"
Expand Down Expand Up @@ -148,14 +145,12 @@ func Execute(configFile io.Reader) {
setDefaultLogParameters(dockerConfig)

ctx := context.Background()
ctx = server.WithConfiguration(ctx, extraConfig)
ctx, err = configureLogging(ctx, dockerConfig)
if err != nil {
log.Fatalf("error configuring logger: %v", err)
}

registryClient := client.NewRegistryClient(clientcmd.NewConfig().BindToFile())
ctx = server.WithRegistryClient(ctx, registryClient)

readLimiter := newLimiter(extraConfig.Requests.Read)
writeLimiter := newLimiter(extraConfig.Requests.Write)
Expand All @@ -166,73 +161,7 @@ func Execute(configFile io.Reader) {
// with uuid generation under low entropy.
uuid.Loggerf = context.GetLogger(ctx).Warnf

// add parameters for the auth middleware
if dockerConfig.Auth.Type() == server.OpenShiftAuth {
if dockerConfig.Auth[server.OpenShiftAuth] == nil {
dockerConfig.Auth[server.OpenShiftAuth] = make(configuration.Parameters)
}
dockerConfig.Auth[server.OpenShiftAuth][server.AccessControllerOptionParams] = server.AccessControllerParams{
Logger: context.GetLogger(ctx),
RegistryClient: registryClient,
}
}

app := handlers.NewApp(ctx, dockerConfig)

// Add a token handling endpoint
if options, usingOpenShiftAuth := dockerConfig.Auth[server.OpenShiftAuth]; usingOpenShiftAuth {
tokenRealm, err := server.TokenRealm(options)
if err != nil {
context.GetLogger(app).Fatalf("error setting up token auth: %s", err)
}
err = app.NewRoute().Methods("GET").PathPrefix(tokenRealm.Path).Handler(server.NewTokenHandler(ctx, registryClient)).GetError()
if err != nil {
context.GetLogger(app).Fatalf("error setting up token endpoint at %q: %v", tokenRealm.Path, err)
}
context.GetLogger(app).Debugf("configured token endpoint at %q", tokenRealm.String())
}

// TODO add https scheme
adminRouter := app.NewRoute().PathPrefix(api.AdminPrefix).Subrouter()
pruneAccessRecords := func(*http.Request) []auth.Access {
return []auth.Access{
{
Resource: auth.Resource{
Type: "admin",
},
Action: "prune",
},
}
}

app.RegisterRoute(
// DELETE /admin/blobs/<digest>
adminRouter.Path(api.AdminPath).Methods("DELETE"),
// handler
server.BlobDispatcher,
// repo name not required in url
handlers.NameNotRequired,
// custom access records
pruneAccessRecords,
)

// Registry extensions endpoint provides extra functionality to handle the image
// signatures.
server.RegisterSignatureHandler(app)

// Registry extensions endpoint provides prometheus metrics.
if extraConfig.Metrics.Enabled {
if len(extraConfig.Metrics.Secret) == 0 {
context.GetLogger(app).Fatalf("openshift.metrics.secret field cannot be empty when metrics are enabled")
}
server.RegisterMetricHandler(app)
}

// Advertise features supported by OpenShift
if app.Config.HTTP.Headers == nil {
app.Config.HTTP.Headers = http.Header{}
}
app.Config.HTTP.Headers.Set("X-Registry-Supports-Signatures", "1")
app, _ := server.NewApp(ctx, registryClient, dockerConfig, extraConfig)

app.RegisterHealthChecks()
handler := http.Handler(app)
Expand Down
9 changes: 8 additions & 1 deletion pkg/dockerregistry/server/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ func BlobDispatcher(ctx *handlers.Context, r *http.Request) http.Handler {
reference := context.GetStringValue(ctx, "vars.digest")
dgst, _ := digest.ParseDigest(reference)

app := appFrom(ctx)
if app == nil {
panic("no app in context")
}

blobHandler := &blobHandler{
Context: ctx,
app: app,
Digest: dgst,
}

Expand All @@ -35,6 +41,7 @@ func BlobDispatcher(ctx *handlers.Context, r *http.Request) http.Handler {
type blobHandler struct {
*handlers.Context

app *App
Digest digest.Digest
}

Expand All @@ -47,7 +54,7 @@ func (bh *blobHandler) Delete(w http.ResponseWriter, req *http.Request) {
return
}

vacuum := storage.NewVacuum(bh.Context, dockerStorageDriver)
vacuum := storage.NewVacuum(bh.Context, bh.app.driver)

err := vacuum.RemoveBlob(bh.Digest.String())
if err != nil {
Expand Down
132 changes: 132 additions & 0 deletions pkg/dockerregistry/server/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package server

import (
"fmt"
"net/http"
"os"

"github.com/docker/distribution"
"github.com/docker/distribution/configuration"
"github.com/docker/distribution/context"
"github.com/docker/distribution/registry/auth"
"github.com/docker/distribution/registry/handlers"
storagedriver "github.com/docker/distribution/registry/storage/driver"

"github.com/openshift/origin/pkg/dockerregistry/server/api"
"github.com/openshift/origin/pkg/dockerregistry/server/client"
registryconfig "github.com/openshift/origin/pkg/dockerregistry/server/configuration"
)

var (
// quotaEnforcing contains shared caches of quota objects keyed by project
// name. Will be initialized only if the quota is enforced.
// See EnforceQuotaEnvVar.
quotaEnforcing *quotaEnforcingConfig
)

type App struct {
registryClient client.RegistryClient
extraConfig *registryconfig.Configuration

// driver gives access to the blob store.
// This variable holds the object created by docker/distribution. We
// import it into our namespace because there are no other ways to access
// it. In other cases it is hidden from us.
driver storagedriver.StorageDriver

// registry represents a collection of repositories, addressable by name.
// This variable holds the object created by docker/distribution. We
// import it into our namespace because there are no other ways to access
// it. In other cases it is hidden from us.
registry distribution.Namespace
}

func NewApp(ctx context.Context, registryClient client.RegistryClient, dockerConfig *configuration.Configuration, extraConfig *registryconfig.Configuration) (*handlers.App, *App) {
app := &App{
registryClient: registryClient,
extraConfig: extraConfig,
}
ctx = withApp(ctx, app)

patchConfig(dockerConfig, app)

dockerApp := handlers.NewApp(ctx, dockerConfig)

// Add a token handling endpoint
if dockerConfig.Auth.Type() == authOpenShift {
tokenRealm, err := TokenRealm(dockerConfig.Auth[authOpenShift])
if err != nil {
context.GetLogger(dockerApp).Fatalf("error setting up token auth: %s", err)
}
err = dockerApp.NewRoute().Methods("GET").PathPrefix(tokenRealm.Path).Handler(NewTokenHandler(ctx, registryClient)).GetError()
if err != nil {
context.GetLogger(dockerApp).Fatalf("error setting up token endpoint at %q: %v", tokenRealm.Path, err)
}
context.GetLogger(dockerApp).Debugf("configured token endpoint at %q", tokenRealm.String())
}

// TODO add https scheme
adminRouter := dockerApp.NewRoute().PathPrefix(api.AdminPrefix).Subrouter()
pruneAccessRecords := func(*http.Request) []auth.Access {
return []auth.Access{
{
Resource: auth.Resource{
Type: "admin",
},
Action: "prune",
},
}
}

dockerApp.RegisterRoute(
// DELETE /admin/blobs/<digest>
adminRouter.Path(api.AdminPath).Methods("DELETE"),
// handler
BlobDispatcher,
// repo name not required in url
handlers.NameNotRequired,
// custom access records
pruneAccessRecords,
)

// Registry extensions endpoint provides extra functionality to handle the image
// signatures.
RegisterSignatureHandler(dockerApp)

// Registry extensions endpoint provides prometheus metrics.
if extraConfig.Metrics.Enabled {
if len(extraConfig.Metrics.Secret) == 0 {
context.GetLogger(dockerApp).Fatalf("openshift.metrics.secret field cannot be empty when metrics are enabled")
}
RegisterMetricHandler(dockerApp)
}

// Advertise features supported by OpenShift
if dockerApp.Config.HTTP.Headers == nil {
dockerApp.Config.HTTP.Headers = http.Header{}
}
dockerApp.Config.HTTP.Headers.Set("X-Registry-Supports-Signatures", "1")

return dockerApp, app
}

func (app *App) Repository(ctx context.Context, repo distribution.Repository, options map[string]interface{}) (distribution.Repository, error) {
if app.driver == nil {
return nil, fmt.Errorf("configuration error: the OpenShift storage driver middleware is not activated")
}

if app.registry == nil {
return nil, fmt.Errorf("configuration error: the OpenShift registry middleware is not activated")
}

registryOSClient, err := app.registryClient.Client()
if err != nil {
return nil, err
}

if quotaEnforcing == nil {
quotaEnforcing = newQuotaEnforcingConfig(ctx, os.Getenv(EnforceQuotaEnvVar), os.Getenv(ProjectCacheTTLEnvVar), options)
}

return newRepositoryWithClient(app, ctx, registryOSClient, repo, options)
}
25 changes: 11 additions & 14 deletions pkg/dockerregistry/server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/openshift/origin/pkg/dockerregistry/server/audit"
"github.com/openshift/origin/pkg/dockerregistry/server/client"
"github.com/openshift/origin/pkg/dockerregistry/server/configuration"
)

type deferredErrors map[string]error
Expand All @@ -35,8 +36,6 @@ func (d deferredErrors) Empty() bool {
}

const (
OpenShiftAuth = "openshift"

defaultTokenPath = "/openshift/token"
defaultUserName = "anonymous"

Expand All @@ -48,10 +47,6 @@ const (
AccessControllerOptionParams = "_params"
)

func init() {
registryauth.Register(OpenShiftAuth, registryauth.InitFunc(newAccessController))
}

// WithUserInfoLogger creates a new context with provided user infomation.
func WithUserInfoLogger(ctx context.Context, username, userid string) context.Context {
ctx = context.WithValue(ctx, audit.AuditUserEntry, username)
Expand All @@ -69,6 +64,7 @@ type AccessController struct {
tokenRealm *url.URL
registryClient client.RegistryClient
auditLog bool
metricsConfig configuration.Metrics
}

var _ registryauth.AccessController = &AccessController{}
Expand Down Expand Up @@ -135,6 +131,7 @@ func TokenRealm(options map[string]interface{}) (*url.URL, error) {
type AccessControllerParams struct {
Logger context.Logger
RegistryClient client.RegistryClient
MetricsConfig configuration.Metrics
}

func newAccessController(options map[string]interface{}) (registryauth.AccessController, error) {
Expand All @@ -159,6 +156,7 @@ func newAccessController(options map[string]interface{}) (registryauth.AccessCon
realm: realm,
tokenRealm: tokenRealm,
registryClient: params.RegistryClient,
metricsConfig: params.MetricsConfig,
}

if audit, ok := options["audit"]; ok {
Expand Down Expand Up @@ -263,7 +261,7 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg
return nil, ac.wrapErr(ctx, err)
}

bearerToken, err := getOpenShiftAPIToken(ctx, req)
bearerToken, err := getOpenShiftAPIToken(req)
if err != nil {
return nil, ac.wrapErr(ctx, err)
}
Expand All @@ -274,7 +272,7 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg
}

// In case of docker login, hits endpoint /v2
if len(bearerToken) > 0 && !isMetricsBearerToken(ctx, bearerToken) {
if len(bearerToken) > 0 && !isMetricsBearerToken(ac.metricsConfig, bearerToken) {
user, userid, err := verifyOpenShiftUser(ctx, osClient)
if err != nil {
return nil, ac.wrapErr(ctx, err)
Expand Down Expand Up @@ -358,7 +356,7 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg
case "metrics":
switch access.Action {
case "get":
if !isMetricsBearerToken(ctx, bearerToken) {
if !isMetricsBearerToken(ac.metricsConfig, bearerToken) {
return nil, ac.wrapErr(ctx, ErrOpenShiftAccessDenied)
}
default:
Expand Down Expand Up @@ -406,7 +404,7 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg
return withUserClient(ctx, osClient), nil
}

func getOpenShiftAPIToken(ctx context.Context, req *http.Request) (string, error) {
func getOpenShiftAPIToken(req *http.Request) (string, error) {
token := ""

authParts := strings.SplitN(req.Header.Get("Authorization"), " ", 2)
Expand Down Expand Up @@ -512,10 +510,9 @@ func verifyPruneAccess(ctx context.Context, c client.SelfSubjectAccessReviewsNam
return nil
}

func isMetricsBearerToken(ctx context.Context, token string) bool {
config := ConfigurationFrom(ctx)
if config.Metrics.Enabled {
return config.Metrics.Secret == token
func isMetricsBearerToken(metrics configuration.Metrics, token string) bool {
if metrics.Enabled {
return metrics.Secret == token
}
return false
}
2 changes: 0 additions & 2 deletions pkg/dockerregistry/server/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
_ "github.com/openshift/origin/pkg/api/install"

"github.com/openshift/origin/pkg/dockerregistry/server/client"
"github.com/openshift/origin/pkg/dockerregistry/server/configuration"
)

func sarResponse(ns string, allowed bool, reason string) *authorizationapi.SelfSubjectAccessReview {
Expand Down Expand Up @@ -421,7 +420,6 @@ func TestAccessController(t *testing.T) {
}
ctx := context.Background()
ctx = context.WithRequest(ctx, req)
ctx = WithConfiguration(ctx, &configuration.Configuration{})
authCtx, err := accessController.Authorized(ctx, test.access...)
server.Close()

Expand Down
2 changes: 1 addition & 1 deletion pkg/dockerregistry/server/blobdescriptorservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (bs *blobDescriptorService) Stat(ctx context.Context, dgst digest.Digest) (
context.GetLogger(ctx).Debugf("(*blobDescriptorService).Stat: could not stat layer link %s in repository %s: %v", dgst.String(), repo.Named().Name(), err)

// First attempt: looking for the blob locally
desc, err = dockerRegistry.BlobStatter().Stat(ctx, dgst)
desc, err = repo.app.registry.BlobStatter().Stat(ctx, dgst)
if err == nil {
context.GetLogger(ctx).Debugf("(*blobDescriptorService).Stat: blob %s exists in the global blob store", dgst.String())
// only non-empty layers is wise to check for existence in the image stream.
Expand Down
Loading

0 comments on commit 1fdaada

Please sign in to comment.