Skip to content

Commit

Permalink
Merge pull request #26 from sudo-bmitch/pr-limit-apis
Browse files Browse the repository at this point in the history
Add top level API limits on push and delete
  • Loading branch information
sudo-bmitch committed Dec 20, 2023
2 parents d4e08f6 + 9a8d991 commit f57132f
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 30 deletions.
18 changes: 13 additions & 5 deletions cmd/olareg/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type serveOpts struct {
port int
storeType string
storeDir string
apiPush bool
apiDelete bool
apiBlobDel bool
apiReferrer bool
}
Expand All @@ -36,6 +38,8 @@ func newServeCmd(root *rootOpts) *cobra.Command {
newCmd.Flags().IntVar(&opts.port, "port", 80, "listener port")
newCmd.Flags().StringVar(&opts.storeDir, "dir", ".", "root directory for storage")
newCmd.Flags().StringVar(&opts.storeType, "store-type", "dir", "storage type (dir, mem)")
newCmd.Flags().BoolVar(&opts.apiPush, "api-push", true, "enable push APIs")
newCmd.Flags().BoolVar(&opts.apiDelete, "api-delete", true, "enable delete APIs")
newCmd.Flags().BoolVar(&opts.apiBlobDel, "api-blob-delete", false, "enable blob delete API")
newCmd.Flags().BoolVar(&opts.apiReferrer, "api-referrer", true, "enable referrer API")
return newCmd
Expand All @@ -48,12 +52,16 @@ func (opts *serveOpts) run(cmd *cobra.Command, args []string) error {
return fmt.Errorf("unable to parse store type %s: %w", opts.storeType, err)
}
conf := config.Config{
StoreType: storeType,
RootDir: opts.storeDir,
Log: opts.root.log,
Storage: config.ConfigStorage{
StoreType: storeType,
RootDir: opts.storeDir,
},
Log: opts.root.log,
API: config.ConfigAPI{
BlobDelete: config.ConfigAPIBlobDelete{Enabled: &opts.apiBlobDel},
Referrer: config.ConfigAPIReferrer{Enabled: &opts.apiReferrer},
PushEnabled: &opts.apiPush,
DeleteEnabled: &opts.apiDelete,
Blob: config.ConfigAPIBlob{DeleteEnabled: &opts.apiBlobDel},
Referrer: config.ConfigAPIReferrer{Enabled: &opts.apiReferrer},
},
}
handler := olareg.New(conf)
Expand Down
31 changes: 21 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@ import (
type Store int

const (
StoreMem Store = iota // StoreMem only uses memory for an ephemeral registry
StoreUndef Store = iota // undefined backend storage is the invalid zero value
StoreMem // StoreMem only uses memory for an ephemeral registry
StoreDir // StoreDir tracks each repository with a separate blob store
StoreShared // StoreShared tracks the blobs in a single store for less disk usage
)

type Config struct {
StoreType Store
RootDir string
Log slog.Logger
ManifestLimit int64
API ConfigAPI
Storage ConfigStorage
API ConfigAPI
Log slog.Logger
// TODO: TLS and listener options? not needed here if only providing handler
// TODO: GC policy, delete untagged? timeouts for partial blobs?
// TODO: proxy settings, pull only, or push+pull cache
Expand All @@ -30,13 +29,25 @@ type Config struct {
// TODO: allowed actions: get/head, put, delete, catalog
}

type ConfigStorage struct {
StoreType Store
RootDir string
}

type ConfigAPI struct {
BlobDelete ConfigAPIBlobDelete
Referrer ConfigAPIReferrer
PushEnabled *bool
DeleteEnabled *bool
Manifest ConfigAPIManifest
Blob ConfigAPIBlob
Referrer ConfigAPIReferrer
}

type ConfigAPIBlobDelete struct {
Enabled *bool
type ConfigAPIManifest struct {
Limit int64
}

type ConfigAPIBlob struct {
DeleteEnabled *bool
}

type ConfigAPIReferrer struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/store/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func NewDir(conf config.Config, opts ...Opts) Store {
opt(&sc)
}
d := &dir{
root: conf.RootDir,
root: conf.Storage.RootDir,
repos: map[string]*dirRepo{},
log: sc.log,
conf: conf,
Expand Down
6 changes: 3 additions & 3 deletions manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,9 @@ func (s *server) manifestPut(repoStr, arg string) http.HandlerFunc {
s.log.Debug("unsupported media type", "repo", repoStr, "arg", arg, "mediaType", mt)
return
}
if r.ContentLength > s.conf.ManifestLimit {
if r.ContentLength > s.conf.API.Manifest.Limit {
w.WriteHeader(http.StatusRequestEntityTooLarge)
_ = types.ErrRespJSON(w, types.ErrInfoManifestInvalid(fmt.Sprintf("manifest too large, limited to %d bytes", s.conf.ManifestLimit)))
_ = types.ErrRespJSON(w, types.ErrInfoManifestInvalid(fmt.Sprintf("manifest too large, limited to %d bytes", s.conf.API.Manifest.Limit)))
return
}
// parse arg
Expand All @@ -228,7 +228,7 @@ func (s *server) manifestPut(repoStr, arg string) http.HandlerFunc {
}
}
// read manifest
rLimit := io.LimitReader(r.Body, s.conf.ManifestLimit)
rLimit := io.LimitReader(r.Body, s.conf.API.Manifest.Limit)
mRaw, err := io.ReadAll(rLimit)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Expand Down
22 changes: 11 additions & 11 deletions olareg.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ func New(conf config.Config) http.Handler {
if s.log == nil {
s.log = slog.Null{}
}
if s.conf.ManifestLimit <= 0 {
s.conf.ManifestLimit = manifestLimitDefault
if s.conf.API.Manifest.Limit <= 0 {
s.conf.API.Manifest.Limit = manifestLimitDefault
}
switch conf.StoreType {
switch conf.Storage.StoreType {
case config.StoreMem:
s.store = store.NewMem(s.conf, store.WithLog(s.log))
case config.StoreDir:
if s.conf.RootDir == "" {
s.conf.RootDir = "."
if s.conf.Storage.RootDir == "" {
s.conf.Storage.RootDir = "."
}
s.store = store.NewDir(s.conf, store.WithLog(s.log))
}
Expand All @@ -50,7 +50,7 @@ type server struct {
store store.Store
log slog.Logger
// TODO: add context?
// TODO: implement memory store, disk cache, GC handling, etc for the non-disk and non-config data
// TODO: implement disk cache, GC handling, etc for the non-disk and non-config data
}

func (s *server) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
Expand All @@ -66,10 +66,10 @@ func (s *server) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodGet || req.Method == http.MethodHead {
s.manifestGet(matches[0], matches[1]).ServeHTTP(resp, req)
return
} else if req.Method == http.MethodPut {
} else if req.Method == http.MethodPut && boolDefault(s.conf.API.PushEnabled, true) {
s.manifestPut(matches[0], matches[1]).ServeHTTP(resp, req)
return
} else if req.Method == http.MethodDelete {
} else if req.Method == http.MethodDelete && boolDefault(s.conf.API.DeleteEnabled, true) {
s.manifestDelete(matches[0], matches[1]).ServeHTTP(resp, req)
return
} else {
Expand All @@ -81,11 +81,11 @@ func (s *server) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
// handle blob get
s.blobGet(matches[0], matches[1]).ServeHTTP(resp, req)
return
} else if req.Method == http.MethodDelete && boolDefault(s.conf.API.BlobDelete.Enabled, false) {
} else if req.Method == http.MethodDelete && boolDefault(s.conf.API.DeleteEnabled, true) && boolDefault(s.conf.API.Blob.DeleteEnabled, false) {
// handle blob delete
s.blobDelete(matches[0], matches[1]).ServeHTTP(resp, req)
return
} else if matches[1] == "uploads" && req.Method == http.MethodPost {
} else if matches[1] == "uploads" && req.Method == http.MethodPost && boolDefault(s.conf.API.PushEnabled, true) {
// handle blob post
s.blobUploadPost(matches[0]).ServeHTTP(resp, req)
return
Expand All @@ -108,7 +108,7 @@ func (s *server) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
// handle tag listing
s.tagList(matches[0]).ServeHTTP(resp, req)
return
} else if matches, ok := matchV2(pathEl, "...", "blobs", "uploads", "*"); ok {
} else if matches, ok := matchV2(pathEl, "...", "blobs", "uploads", "*"); ok && boolDefault(s.conf.API.PushEnabled, true) {
// handle blob upload methods
if req.Method == http.MethodPatch {
s.blobUploadPatch(matches[0], matches[1]).ServeHTTP(resp, req)
Expand Down

0 comments on commit f57132f

Please sign in to comment.