Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[full-ci] thumbnails: add libvips based thumbnail generator #10310

Merged
merged 11 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .drone.star
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,7 @@ def dockerRelease(ctx, arch, repo, build_type):
"image": OC_CI_GOLANG,
"environment": DRONE_HTTP_PROXY_ENV,
"commands": [
"make -C ocis release-linux-docker-%s" % (arch),
"make -C ocis release-linux-docker-%s ENABLE_VIPS=true" % (arch),
],
},
{
Expand Down
30 changes: 18 additions & 12 deletions .make/release.mk
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,30 @@ release-dirs:
DOCKER_LDFLAGS += -X "$(OCIS_REPO)/ocis-pkg/config/defaults.BaseDataPathType=path" -X "$(OCIS_REPO)/ocis-pkg/config/defaults.BaseDataPathValue=/var/lib/ocis"
DOCKER_LDFLAGS += -X "$(OCIS_REPO)/ocis-pkg/config/defaults.BaseConfigPathType=path" -X "$(OCIS_REPO)/ocis-pkg/config/defaults.BaseConfigPathValue=/etc/ocis"

# We can't link statically when vips is enabled but we still
# prefer static linking where possible
ifndef ENABLE_VIPS
DOCKER_LDFLAGS += -extldflags "-static"
endif

release-linux-docker-amd64: release-dirs
GOOS=linux \
GOARCH=amd64 \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-buildmode=pie \
-trimpath \
-ldflags '-extldflags "-static" $(LDFLAGS) $(DOCKER_LDFLAGS)' \
-ldflags '$(LDFLAGS) $(DOCKER_LDFLAGS)' \
-o '$(DIST)/binaries/$(EXECUTABLE)-linux-amd64' \
./cmd/$(NAME)

release-linux-docker-arm: release-dirs
GOOS=linux \
GOARCH=arm \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-trimpath \
-ldflags '-extldflags "-static" $(LDFLAGS) $(DOCKER_LDFLAGS)' \
-ldflags '$(LDFLAGS) $(DOCKER_LDFLAGS)' \
-o '$(DIST)/binaries/$(EXECUTABLE)-linux-arm' \
./cmd/$(NAME)

Expand All @@ -41,10 +47,10 @@ release-linux-docker-arm64: release-dirs
GOOS=linux \
GOARCH=arm64 \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-buildmode=pie \
-trimpath \
-ldflags '-extldflags "-static" $(LDFLAGS) $(DOCKER_LDFLAGS)' \
-ldflags '$(LDFLAGS) $(DOCKER_LDFLAGS)' \
-o '$(DIST)/binaries/$(EXECUTABLE)-linux-arm64' \
./cmd/$(NAME)

Expand All @@ -53,7 +59,7 @@ release-linux: release-dirs
GOOS=linux \
GOARCH=amd64 \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-buildmode=pie \
-trimpath \
-ldflags '-extldflags "-static" $(LDFLAGS)' \
Expand All @@ -63,7 +69,7 @@ release-linux: release-dirs
GOOS=linux \
GOARCH=386 \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-trimpath \
-ldflags '-extldflags "-static" $(LDFLAGS)' \
-o '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-linux-386' \
Expand All @@ -72,7 +78,7 @@ release-linux: release-dirs
GOOS=linux \
GOARCH=arm64 \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-buildmode=pie \
-trimpath \
-ldflags '-extldflags "-static" $(LDFLAGS)' \
Expand All @@ -82,7 +88,7 @@ release-linux: release-dirs
GOOS=linux \
GOARCH=arm \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-trimpath \
-ldflags '-extldflags "-static" $(LDFLAGS)' \
-o '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-linux-arm' \
Expand All @@ -96,7 +102,7 @@ release-darwin: release-dirs
GOOS=darwin \
GOARCH=amd64 \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-buildmode=pie \
-trimpath \
-ldflags '$(LDFLAGS)' \
Expand All @@ -106,7 +112,7 @@ release-darwin: release-dirs
GOOS=darwin \
GOARCH=arm64 \
go build \
-tags 'netgo $(TAGS)' \
-tags 'netgo,$(TAGS)' \
-buildmode=pie \
-trimpath \
-ldflags '$(LDFLAGS)' \
Expand Down
3 changes: 3 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"type": "go",
"request": "launch",
"mode": "debug",
"buildFlags": [
// "-tags", "enable_vips"
],
"program": "${workspaceFolder}/ocis/cmd/ocis",
"args": [
"server"
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ FROM owncloudci/golang:1.22 as build
COPY --from=generate /ocis /ocis

WORKDIR /ocis/ocis
RUN make ci-go-generate build
RUN make ci-go-generate build ENABLE_VIPS=true

FROM alpine:3.18
FROM alpine:3.20

RUN apk add --no-cache ca-certificates mailcap tree attr curl && \
RUN apk add --no-cache ca-certificates mailcap tree attr curl vips && \
echo 'hosts: files dns' >| /etc/nsswitch.conf

LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
Expand Down
8 changes: 8 additions & 0 deletions changelog/unreleased/thumbnails-libvips.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Enhancement: Allow to use libvips for generating thumbnails

To improve performance (and to be able to support a wider range of images formats in the future)
the thumbnails service is now able to utilize libvips (https://www.libvips.org/) for generating thumbnails.
Enabling the use of libvips is implemented as a build-time option which is currently disabled for the
"bare-metal" build of the ocis binary and enabled for the docker image builds.

https://github.com/owncloud/ocis/pull/10310
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/coreos/go-oidc/v3 v3.11.0
github.com/cs3org/go-cs3apis v0.0.0-20240724121416-062c4e3046cb
github.com/cs3org/reva/v2 v2.25.1-0.20241016145214-e5baaccf6614
github.com/davidbyttow/govips/v2 v2.15.0
github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/egirna/icap-client v0.1.1
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidbyttow/govips/v2 v2.15.0 h1:h3lF+rQElBzGXbQSSPqmE3XGySPhcQo2x3t5l/dZ+pU=
github.com/davidbyttow/govips/v2 v2.15.0/go.mod h1:3OQCHj0nf5Mnrplh5VlNvmx3IhJXyxbAoTJZPflUjmM=
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
Expand Down Expand Up @@ -845,6 +847,7 @@ github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDm
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI=
github.com/nrdcg/desec v0.5.0/go.mod h1:2ejvMazkav1VdDbv2HeQO7w+Ta1CGHqzQr27ZBYTuEQ=
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
Expand Down Expand Up @@ -1248,6 +1251,7 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down Expand Up @@ -1468,6 +1472,7 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
Expand Down Expand Up @@ -1640,6 +1645,7 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
Expand Down
4 changes: 4 additions & 0 deletions ocis/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ NAME := ocis

TAGS := disable_crypt

ifdef ENABLE_VIPS
TAGS := ${TAGS},enable_vips
endif

include ../.make/recursion.mk

############ tooling ############
Expand Down
4 changes: 2 additions & 2 deletions ocis/docker/Dockerfile.linux.amd64
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
FROM amd64/alpine:3.18
FROM amd64/alpine:3.20

ARG VERSION=""
ARG REVISION=""

RUN apk add --no-cache ca-certificates mailcap tree attr curl inotify-tools bash libc6-compat && \
RUN apk add --no-cache ca-certificates mailcap tree attr curl inotify-tools bash libc6-compat vips && \
echo 'hosts: files dns' >| /etc/nsswitch.conf

LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
Expand Down
4 changes: 2 additions & 2 deletions ocis/docker/Dockerfile.linux.arm64
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
FROM arm64v8/alpine:3.18
FROM arm64v8/alpine:3.20

ARG VERSION=""
ARG REVISION=""

RUN apk add --no-cache ca-certificates mailcap tree attr curl inotify-tools bash libc6-compat && \
RUN apk add --no-cache ca-certificates mailcap tree attr curl inotify-tools bash libc6-compat vips && \
echo 'hosts: files dns' >| /etc/nsswitch.conf

LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
Expand Down
2 changes: 1 addition & 1 deletion ocis/docker/Dockerfile.linux.debug.amd64
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM amd64/alpine:latest
ARG VERSION=""
ARG REVISION=""

RUN apk add --no-cache ca-certificates mailcap tree attr curl libc6-compat delve && \
RUN apk add --no-cache ca-certificates mailcap tree attr curl inotify-tools bash libc6-compat vips delve && \
echo 'hosts: files dns' >| /etc/nsswitch.conf

LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
Expand Down
26 changes: 26 additions & 0 deletions services/thumbnails/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,29 @@ To have more control over memory (and CPU) consumption the maximum number of con
## Thumbnails and SecureView

If a resource is shared using SecureView, the share reciever will get a 403 (forbidden) response when requesting a thumbnail. The requesting client needs to decide what to show and usually a placeholder thumbnail is used.

## Using libvips for Thumbnail Generation

To improve performance and to support a wider range of images formats, the thumbnails service is able to utilize the [libvips library](https://www.libvips.org/) for thumbnail generation. Support for libvips needs to be
enabled at buildtime and has a couple of implications:

* With libvips support enabled, it is not possible to create a statically linked ocis binary.
* Therefore, the libvips shared libraries need to be available at runtime in the same release that was used to build the ocis binary.
* When using the ocis docker images, the libvips shared libraries are included in the image and are correctly embedded.

Support of libvips is disabled by default. To enable it, make sure libvips and its buildtime dependencies are installed in your build environment.
Then you just need to set the `ENABLE_VIPS` variable on the `make` command:

```shell
make -C ocis build ENABLE_VIPS=1
```

Or include the `enable_vips` build tag in the `go build` command:

```shell
go build -tags enable_vips -o ocis -o bin/ocis ./cmd/ocis
```

When building a docker image using the Dockerfile in the top-level directory of ocis, libvips support is enabled and the libvips shared libraries are included
in the resulting docker image.

2 changes: 2 additions & 0 deletions services/thumbnails/pkg/errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ var (
ErrNoEncoderForType = errors.New("thumbnails: no encoder for this type found")
// ErrNoImageFromAudioFile defines an error when an image cannot be extracted from an audio file
ErrNoImageFromAudioFile = errors.New("thumbnails: could not extract image from audio file")
// ErrNoConverterForExtractedImageFromGgsFile defines an error when the extracted image from an ggs file could not be converted
ErrNoConverterForExtractedImageFromGgsFile = errors.New("thumbnails: could not find converter for image extracted from ggs file")
// ErrNoConverterForExtractedImageFromAudioFile defines an error when the extracted image from an audio file could not be converted
ErrNoConverterForExtractedImageFromAudioFile = errors.New("thumbnails: could not find converter for image extracted from audio file")
// ErrCS3AuthorizationMissing defines an error when the CS3 authorization is missing
Expand Down
20 changes: 5 additions & 15 deletions services/thumbnails/pkg/preprocessor/preprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"mime"
"strings"

"github.com/kovidgoyal/imaging"
"github.com/pkg/errors"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
Expand All @@ -27,18 +26,6 @@ type FileConverter interface {
Convert(r io.Reader) (interface{}, error)
}

// ImageDecoder is a converter for the image file
type ImageDecoder struct{}

// Convert reads the image file and returns the thumbnail image
func (i ImageDecoder) Convert(r io.Reader) (interface{}, error) {
img, err := imaging.Decode(r, imaging.AutoOrientation(true))
if err != nil {
return nil, errors.Wrap(err, `could not decode the image`)
}
return img, nil
}

// GifDecoder is a converter for the gif file
type GifDecoder struct{}

Expand Down Expand Up @@ -72,8 +59,11 @@ func (g GgsDecoder) Convert(r io.Reader) (interface{}, error) {
if err != nil {
return nil, err
}

img, err := imaging.Decode(thumbnail, imaging.AutoOrientation(true))
converter := ForType("image/png", nil)
if converter == nil {
return nil, thumbnailerErrors.ErrNoConverterForExtractedImageFromGgsFile
}
img, err := converter.Convert(thumbnail)
if err != nil {
return nil, errors.Wrap(err, `could not decode the image`)
}
Expand Down
22 changes: 22 additions & 0 deletions services/thumbnails/pkg/preprocessor/preprocessor_imaging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build !enable_vips

package preprocessor

import (
"io"

"github.com/kovidgoyal/imaging"
"github.com/pkg/errors"
)

// ImageDecoder is a converter for the image file
type ImageDecoder struct{}

// Convert reads the image file and returns the thumbnail image
func (i ImageDecoder) Convert(r io.Reader) (interface{}, error) {
img, err := imaging.Decode(r, imaging.AutoOrientation(true))
if err != nil {
return nil, errors.Wrap(err, `could not decode the image`)
}
return img, nil
}
20 changes: 20 additions & 0 deletions services/thumbnails/pkg/preprocessor/preprocessor_vips.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//go:build enable_vips

package preprocessor

import (
"io"

"github.com/davidbyttow/govips/v2/vips"
)

func init() {
vips.LoggingSettings(nil, vips.LogLevelError)
}

type ImageDecoder struct{}

func (v ImageDecoder) Convert(r io.Reader) (interface{}, error) {
img, err := vips.NewImageFromReader(r)
return img, err
}
Loading