Repository navigation
Conversation
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| Security | 12 critical |
| CodeStyle | 37 minor |
🟢 Metrics 229 complexity · 312 duplication
Metric Results Complexity 229 Duplication 312
🟢 Coverage 65.15% diff coverage
Metric Results Coverage variation Report missing for 5a7afe01 Diff coverage ✅ 65.15% diff coverage Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (5a7afe0) Report Missing Report Missing Report Missing Head commit (9eb1bcd) 85287 20874 24.48% Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#3397) 1145 746 65.15% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%1 Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
|
How do audio artwork, geogebra thumbnail and tiff preview extraction fit into this approach? Have you seen that I reworked the tiff extraction pr to use tika? I already upstreamed audio artwork extraction and tiff extraction to tika. Geogebra is still pending, but I expect it to be merged very soon as well. So I would like to see a preprocessing step for extraction of embedded pictures before they are handed over to imagor for resizing (with tika it would be push based and out of process too). I was a bit surprised to see that you are moving so much into the webdav service, my expectation would have been that you just replace the Decoder in the thumbnails service - can you elaborate a little why you chose this way more invasive approach? |
3a7874b to
6aaa4ec
Compare
We can move the workflow into webdav, because webdav already has everything it needs locally (the CS3 gateway client for stat + download, the auth context, the response writer), which lets the generator shrink to a stateless resizer (POST bytes in, resized bytes out) that's swappable with an external imagor via a single URL. The thumbnail generation process is owned, end-to-end, by webdav instead of logic split across two services and two round-trips. And we can use an external container to support HEIC thumbnails, just by changing the URL. And I really want to be able to reuse the thumbnailer like tika, because it now becomes stateless. |
Okay, alright. Works for me.
👍🏻
I'm fine with that, I just wanted you to be aware. No problem at all to port it. It was just important to me that extractors still have a place in the architecture and we don't need a magical one service that can handle all file types.
Fair enough, thanks!
That part was clear :) One more note: It might be the time to at least quickly think about the "honest hasPreview" implementation.
Thoughts? Do you see any problems with your drafted architecture? (I don't see any, just to be sure) |
👀 🤔 I'll comment there...
I would prefer library code. I was thinking of moving it to /pkg, in case other services also call the grpc api.
Yes, they should be cached.
Althought, I plan to add a configurable blobstore for the cache so we can store thumbnails in nats or s3 ... with a checksum based key. But that would be the topic of a future PR. |
|
FYI, all merged:
So we can replace all existing extractors and get this on top: |
78a9428 to
0a9e86a
Compare
|
Some more thoughts on why POST and not GET: With POST the image generator does not need to be granted access to all instances it should GET images from. In a multi instance deployment or at hosters all instances can just POST to the imagor service. New instances do not need to be registered in the firewall or network configuration to let imagor make requests to them. The imagor service itself can just be horizontally scaled. Finally, if malicious bytes manage to take over the container/pod it can still not make any requests to the outside world. So POST really is driven by security and scalability. This also explains why we cannot let clients make signed GET requests directly to the imagor service. It would have to be granted access to all instances. IMO that is too much of an attack surface, even if the requests are signed by the instance itself. At the network level you dont want to inspect every request to ensure it really is a GET / download request for a file ... but if you don't the request might also POST to another endpoint to probe the infrastructure ... nah. Lock it down: no egress whatsoever. As a sidenote: the image cache uses hash based keys, so all instances could share the same objectstorage as well. And the objectstorage can then implement the cache invalidation. |
|
Found a compatibility bug while pointing the generator URL at a real imagor (imagorvideo): Related: for the default fill operation (no operation segment) the router has no route with Fix is building a single segment and matching the routes: --- a/services/webdav/pkg/generator/url.go
+++ b/services/webdav/pkg/generator/url.go
@@
box := fmt.Sprintf("%dx%d", width, height)
- noUpscale := "filters:no_upscale()"
- formatFilter := fmt.Sprintf("filters:format(%s)", outputExt)
+ filterSegment := fmt.Sprintf("filters:no_upscale():format(%s)", outputExt)
segment := operationSegment(operation)
if segment == "" {
- return fmt.Sprintf("%s/unsafe/%s/%s/%s/", base, box, noUpscale, formatFilter)
+ return fmt.Sprintf("%s/unsafe/%s/%s/", base, box, filterSegment)
}
- return fmt.Sprintf("%s/unsafe/%s/%s/%s/%s/", base, segment, box, noUpscale, formatFilter)
+ return fmt.Sprintf("%s/unsafe/%s/%s/%s/", base, segment, box, filterSegment)
--- a/services/thumbnails/pkg/service/http/v0/service.go
+++ b/services/thumbnails/pkg/service/http/v0/service.go
@@
- m.Post("/unsafe/{operation}/{width}x{height}/filters:no_upscale()/filters:format({format})/", handler)
- m.Post("/unsafe/{operation}/{width}x{height}/filters:no_upscale()/filters:format({format})", handler)
- m.Post("/unsafe/{width}x{height}/filters:format({format})/", handler)
- m.Post("/unsafe/{width}x{height}/filters:format({format})", handler)
+ m.Post("/unsafe/{operation}/{width}x{height}/filters:no_upscale():format({format})/", handler)
+ m.Post("/unsafe/{operation}/{width}x{height}/filters:no_upscale():format({format})", handler)
+ m.Post("/unsafe/{width}x{height}/filters:no_upscale():format({format})/", handler)
+ m.Post("/unsafe/{width}x{height}/filters:no_upscale():format({format})", handler)(plus the URL literals in |
026b932 to
07eff1b
Compare
|
The web ui fetches previews with
The tests fetch a 1200x1200 image to test text rendering ... However, the available preview resolutions are configured on the server side and NONE of the resolutions is in the default list (save 32x32): The only real difference is the processor.
THUMBNAILS_RESOLUTIONS was used to limit the number of thumbnail resolutions that will be cached AND returned by the API. So the quare resolutions requested by the web ui with fit (fit-in), produce landscape or portrait previews, depending on the source image dimensions. If the source image happens to be wider or taller than the configured resolution, the largest configured resolution will be generated and cached. This might be smaller than the requested width or height. But since the web ui does not always know the size of the image all it can do is tell the server to fit the image into a rectangular box and the server will fit-in the image. For fit-in the response never is a square, which the web ui already expects. I think we can delete all non square resolutions, because in effect only they are requested. thumbnail and fit are only used to make the server crop the image to the exact resolution (thumbnail), or fill the image to the box without cropping. |
|
Not sure, happy to discuss (if needed at all) - but how do I request previews for ultrawide pictures? I'm requesting non-quadratic previews here: opencloud-eu/web-extensions#529 |
|
Not sure if or how this affects the web client, but if it does in any capacity, please check in with one of us to verify the desired behavior. We've made quite some performance improvements over the last few weeks by optimizing requested and delivered resolutions, it would be a shame if we regressed on that. |
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| Security | 12 critical |
| CodeStyle | 37 minor |
🟢 Metrics 941 complexity
Metric Results Complexity 941
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Internal only: the fields in webdav, graph and frontend now read Thumbnails like the keys and the variables they carry. The generator keeps its released thumbnail block, field and key alike.
CI runs imagor as a service instead of through testcontainers, so the frame limit never reached it and the animation case answered differently there than locally.
# Conflicts: # tests/acceptance/expected-failures-decomposed-storage.md # tests/acceptance/expected-failures-posix-storage.md
A comment that explains a decision by what another branch or the previous service did stops being true the moment this merges.
Environment names keep working, a yaml configuration does not: its keys belong to the section of the service that reads them, and an unknown key is ignored without a word.
| thumbnails_resolutions: ["16", "32", "2048"] | ||
| thumbnails_max_input_image_file_size: 50MB | ||
| thumbnails_txt_fontmap_file: /etc/opencloud/fontmap.json | ||
| thumbnails_cache_dir: /var/lib/opencloud/thumbnails/files |
There was a problem hiding this comment.
I guess we could/should put it in a thumbnail block as well?
png.Decode and gif.Decode hand back the source colors untouched, so a wide gamut image came out oversaturated where a jpeg, read through imaging, was converted. Real imagor passes the profile on to the client instead.
…bnails block Every service that configures thumbnails now carries one thumbnails section whose keys are named like the setting itself, so webdav's thumbnails.max_input_image_file_size and the thumbnails service's thumbnail.max_input_image_file_size read the same, and a key's path matches its env name. Env names are unchanged.
An unquoted 50000000 reaches the container as 5e+07 and imagor panics while parsing its flags, which took both imagor services down and failed test-lint-unit.
OpenCloud never honored it, so clients were tested against an ignored a and honoring it now would change what they get.
They are merged onto the built-in defaults, the way the proxy's additional_policies and OC_ADD_RUN_SERVICES are, so the name says it: env variable, yaml key and field all carry the prefix. The two that take key:value entries are named in the plural like the other map-shaped settings in this repo, and the README says which of the three takes bare mime types.
The pinned commit predates opencloud-eu/opencloud#3397, so the server built for CI ignored WEBDAV_THUMBNAILS_GENERATOR_URL and OC_THUMBNAILS_ADDITIONAL_SOURCE_TYPES and the preview suite found no thumbnails. Pinned to the current main.
The pinned commit predates opencloud-eu/opencloud#3397, so the server built for CI ignored WEBDAV_THUMBNAILS_GENERATOR_URL and OC_THUMBNAILS_ADDITIONAL_SOURCE_TYPES and the preview suite found no thumbnails. Pinned to the current main.
The pinned commit predates opencloud-eu/opencloud#3397, so the server built for CI ignored WEBDAV_THUMBNAILS_GENERATOR_URL and OC_THUMBNAILS_ADDITIONAL_SOURCE_TYPES and the preview suite found no thumbnails. Pinned to the current main.
This PR tries to fix #1128 in a backwards compatible way.
Thumbnail generation used to live entirely in the thumbnails service behind a gRPC API: webdav called GetThumbnail, the service fetched the source from storage, preprocessed and generated the image, stored it on its own filesystem, and returned a JWT-signed URL that webdav had to follow with a second authenticated HTTP call just to get the bytes back. This branch inverts that: the thumbnails service becomes a stateless imagor-compatible resizer (one POST endpoint — image bytes in, resized bytes out; no auth, storage, or gRPC), and webdav owns the whole workflow via a single ThumbnailWorkflow type:
stat via gateway → cache check → download source → preprocess → POST to generator → cache → respondstat via gateway → snap the requested size → cache check → download source → preprocess → POST to generator → cache → respond.Webdav gains what it needs to own that pipeline: the preprocessors (text→image, audio cover art) moved over from the thumbnails service, a new checksum-keyed thumbnail cache with memory/file/S3/noop backends, and a pkg/generator package that builds resizer URLs and posts multipart images. Config shrinks to one generator URL plus timeout and max input file size — the URL can point at the built-in thumbnails service or any imagor instance.
The old architecture is deleted from the thumbnails service: proto files, gRPC handler, JWT transfer tokens, filesystem storage, source fetchers, the /data endpoint, and duplicated utilities (~4,000 lines), plus leftover dead config, no-op metrics wrappers, and opencloud init's now-meaningless transfer-secret generation. Net diff:
102 files, +3,337/−4,336118 files, +6,863/−4,861; thumbnail requests no longer need the second round-trip, and the resizer is trivially replaceable.Update: sizing and caching
THUMBNAILS_RESOLUTIONSis a ladder of edge lengths, not a list of boxes: each axis is rounded up on its own, so a square request can no longer land in a landscape box, which is what cropped and shrank portrait previews. LegacyWidthxHeightentries still count with both sides, and the cache is keyed on the snapped box, sox=447andx=448share an entry.Clients no longer need to know the list:
xandyare independently optional, an omitted axis is unconstrained and capped at the largest length, while an explicit0stays the 400 it has always been. A cropping processor plus an unconstrained axis is a 400, no dimensions at all stays the legacy 32x32. A public link that needs a password answers 404 again instead of 500.Update: what a thumbnail is made of
Three settings, read as a chain. For a
.heicon a deployment running a stock imagor:Each one merges onto the built-in defaults, so naming a type does not take the answer away from the others. A target the generator has no format for is refused at startup, a source it cannot read answers 404.
image/heicandimage/avif, both unsupported today, were verified end to end this way, no code change.Update: parity suite
services/thumbnails/pkg/parityposts the same image to the built-in generator and to two real imagor images and compares status, dimensions, format, frames and alpha, with imagor from testcontainers locally and service containers in CI. A generator that answers differently is held to that answer by a recorded override carrying the reason, which fails once the divergence disappears.It runs twice, once per resize implementation, because
make testcompiles the pure Go path while the images ship vips, so the shipped resize code had no test at all. That exposed an older problem, not fixed here: CI's libvips is not the one the images ship, soenable_vipstests measure capabilities we do not deliver (#3746).Follow-ups
oc:has-previewis answered from the same configured list, which needed a change in reva: fix(ocdav): answer has-preview from the ocdav configuration reva#885. ocdav takes the types from its own configuration instead of importing an OpenCloud package, and the frontend hands in what webdav attempts. The vendored copy carries it until that PR lands. It also removes the import that makes a module cycle, reva's go.mod requires opencloud v1.1.0, so a standalone reva announced previews from a frozen OpenCloud list. The requirement itself stays,pkg/app/registry/microandpkg/micro/ocdavstill useopencloud/pkg/registry.image/jpgcannot occur, reva mapsjpgtoimage/jpeg. Remove it from the supported list and the encode step if nothing outside the repo depends on it.THUMBNAILS_MAX_ANIMATION_FRAMESnow caps a gif from its block structure before a frame is decoded, butTHUMBNAILS_MAX_INPUT_WIDTHand_HEIGHTdescribe one page, so a multi-page heic or tiff is bounded only by both backends loading a single page today.S3Cachehas two gaps the unit tests cannot see, because the mock is not minio.Secure: trueis hard-wired inBuildS3CacheConfig, andminio.Newprepends the scheme itself, so thehttp://host:portform the config text asks for is refused as an invalid endpoint. Deriving the scheme from the endpoint and handing minio a bare host fixes both, and a real MinIO behind testcontainers (theimagortestpattern) is what should pin it.ThumbnailCache.Gettakes no context, so the s3 backend reads oncontext.Background()and ignores the request deadline.http.ClientcarryingWEBDAV_THUMBNAILS_GENERATOR_TIMEOUTis shared by the generator POST and the source download, so the generator timeout silently doubles as the download timeout for a source of up toWEBDAV_THUMBNAILS_MAX_INPUT_IMAGE_FILE_SIZE.FontLoaderis rebuilt for every text preview, becausepreprocessor.ForTyperuns per request.ETagand noCache-Control, although the cache key (checksum, box, operation, format) is a content-addressed ETag already.push_test.goand in the parity suite. A parity case runs the built-in generator in-process, so it needs no container and compares imagor on top; the service specs should keep only what a non-default config decides (the concurrency limiter, the input limits) and the helper unit tests.processorand withouta, the default is now fill with upscaling, where main capped the result at the source size;previewsAutoAdustedSizing.featurecarries the new numbers. Web always sends a processor, so this is visible only to a client that sends a barex/y.THUMBNAILS_DATA_ENDPOINTandTHUMBNAILS_TRANSFER_TOKENare gone without a deprecation path: they configured the pull pipeline this PR removes, so there is nothing to point them at.THUMBNAILS_FILESYSTEMSTORAGE_ROOTis still read as a fallback forWEBDAV_THUMBNAILS_CACHE_DIRand marked deprecated.Related:
#630
#3364
#3332
opencloud-eu/reva#781
opencloud-eu/reva#773
owncloud/core#31268 - discusses a and mode parameters
Thumbnails lessons from oc10 #1378
Discussion:
https://github.com/orgs/opencloud-eu/discussions/2368
https://github.com/orgs/opencloud-eu/discussions/1090
@dschmidt This PR is not ready, but I want to bring your attention to this approach, which is why I am pushing this code now.Taken over from @butonic with his and the team's agreement, and rebased onto current main. The sizing and caching changes described above are mine. ~ @dschmidt
Follow-up after this lands
OC_DISABLE_PREVIEWSswitches off every preview path in webdav, but nothing stops the announcement: the graph thumbnails relationship and ocdav'soc:has-previewkeep saying a preview exists, so clients go on asking and get404. That is the state on main as well, both only read the mime list, so it is not a regression here, and it is the smaller half of answeringhas-previewhonestly.