mirror of
https://github.com/willnorris/imageproxy.git
synced 2025-04-15 03:03:10 -05:00
DEVOPS-677: Updates tests to fit TWM needs
This commit is contained in:
parent
ddd4375765
commit
02b81763ea
7 changed files with 99 additions and 25 deletions
17
Dockerfile
17
Dockerfile
|
@ -1,33 +1,30 @@
|
|||
FROM golang:1.15 as build
|
||||
|
||||
RUN useradd -u 1001 go
|
||||
WORKDIR /go/src/willnorris.com/go/imageproxy
|
||||
ADD . .
|
||||
|
||||
WORKDIR /app
|
||||
#WORKDIR /go/src/willnorris.com/go/imageproxy/cmd/imageproxy
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY third_party/envy/go.mod ./third_party/envy/
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -v ./cmd/imageproxy
|
||||
RUN CGO_ENABLED=0 GOOS=linux go install -v ./cmd/imageproxy
|
||||
|
||||
FROM alpine:3.14
|
||||
RUN apk update && apk add pngquant jpegoptim libwebp-tools
|
||||
|
||||
COPY --from=build /etc/passwd /etc/passwd
|
||||
|
||||
WORKDIR /go/bin
|
||||
|
||||
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
|
||||
USER go
|
||||
|
||||
COPY --from=build /etc/ssl/certs /etc/ssl/certs
|
||||
COPY --from=build /go/bin/imageproxy .
|
||||
COPY --from=build /go/src/willnorris.com/go/imageproxy/indicators-size /indicators-size
|
||||
COPY --from=build /go/src/willnorris.com/go/imageproxy/assets /assets
|
||||
|
||||
CMD ["-addr", "0.0.0.0:8080"]
|
||||
ENTRYPOINT ["/go/bin/imageproxy"]
|
||||
CMD ["-verbose", "true"]
|
||||
ENTRYPOINT ["/go/bin/imageproxy", "-scaleUp"]
|
||||
|
||||
EXPOSE 8080
|
||||
|
|
|
@ -154,10 +154,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
timer := prometheus.NewTimer(metricRequestDuration)
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
start := time.Now()
|
||||
h.ServeHTTP(w, r)
|
||||
|
||||
httpRequestsResponseTime.Observe(float64(time.Since(start).Seconds()))
|
||||
}
|
||||
|
||||
// serveImage handles incoming requests for proxied images.
|
||||
|
@ -198,7 +195,6 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
p.log(msg)
|
||||
http.Error(w, msg, http.StatusInternalServerError)
|
||||
metricRemoteErrors.Inc()
|
||||
remoteImageFetchErrors.Inc()
|
||||
return
|
||||
}
|
||||
// close the original resp.Body, even if we wrap it in a NopCloser below
|
||||
|
@ -208,7 +204,7 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
msg := fmt.Sprintf("remote image not found: %v", req.String())
|
||||
log.Print(msg)
|
||||
http.Error(w, msg, http.StatusNotFound)
|
||||
remoteImageFetchErrors.Inc()
|
||||
metricRemoteErrors.Inc()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -220,10 +216,9 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if cached {
|
||||
metricServedFromCache.Inc()
|
||||
requestServedFromCacheCount.Inc()
|
||||
}
|
||||
|
||||
copyHeader(w.Header(), resp.Header, "Cache-Control", "Last-Modified", "Expires", "Etag", "Link")
|
||||
copyHeader(w.Header(), resp.Header, "Cache-Control", "Expires", "Etag", "Link")
|
||||
|
||||
if should304(r, resp) {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
|
@ -233,6 +228,8 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
contentType, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||
|
||||
if contentType == "" || contentType == "application/octet-stream" || contentType == "binary/octet-stream" {
|
||||
fmt.Println(fmt.Sprintf("requested image content type (%s) isn't valid, need to peek: %s", contentType, req.String()))
|
||||
|
||||
// try to detect content type
|
||||
b := bufio.NewReader(resp.Body)
|
||||
resp.Body = ioutil.NopCloser(b)
|
||||
|
@ -245,7 +242,7 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if resp.ContentLength != 0 && !validContentType(p.ContentTypes, contentType) {
|
||||
if resp.ContentLength != 0 && !contentTypeMatches(p.ContentTypes, contentType) {
|
||||
msg := fmt.Sprintf("forbidden content-type: %q", contentType)
|
||||
log.Print(msg)
|
||||
http.Error(w, msg, http.StatusForbidden)
|
||||
|
@ -270,6 +267,12 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
// peekContentType peeks at the first 512 bytes of p, and attempts to detect
|
||||
// the content type. Returns empty string if error occurs.
|
||||
func peekContentType(p *bufio.Reader) string {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("recovered in peekContentType", r)
|
||||
}
|
||||
}()
|
||||
|
||||
byt, err := p.Peek(512)
|
||||
if err != nil && err != bufio.ErrBufferFull && err != io.EOF {
|
||||
return ""
|
||||
|
@ -419,6 +422,7 @@ func should304(req *http.Request, resp *http.Response) bool {
|
|||
// TODO(willnorris): if-none-match header can be a comma separated list
|
||||
// of multiple tags to be matched, or the special value "*" which
|
||||
// matches all etags
|
||||
|
||||
etag := resp.Header.Get("Etag")
|
||||
if etag != "" && etag == req.Header.Get("If-None-Match") {
|
||||
return true
|
||||
|
|
BIN
test-images/unanime-400x,200ml.png
Executable file
BIN
test-images/unanime-400x,200ml.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
BIN
test-images/unanime-400x.png
Executable file
BIN
test-images/unanime-400x.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
BIN
test-images/unanime.png
Normal file
BIN
test-images/unanime.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 136 KiB |
10
transform.go
10
transform.go
|
@ -91,6 +91,7 @@ func Transform(img []byte, opt Options) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputBytes = buf.Bytes()
|
||||
case "gif":
|
||||
fn := func(img image.Image) image.Image {
|
||||
return transformImage(img, opt)
|
||||
|
@ -107,6 +108,7 @@ func Transform(img []byte, opt Options) ([]byte, error) {
|
|||
}
|
||||
|
||||
m = transformImage(m, opt)
|
||||
|
||||
err = jpeg.Encode(buf, m, &jpeg.Options{Quality: quality})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -304,7 +306,7 @@ func cropParams(m image.Image, opt Options) image.Rectangle {
|
|||
return image.Rect(x0, y0, x1, y1)
|
||||
}
|
||||
|
||||
//make image square by adding transparency to max side
|
||||
// make image square by adding transparency to max side
|
||||
func makeSquare(m image.Image, sidePadding int) image.Image {
|
||||
x := m.Bounds().Dx()
|
||||
x = x + sidePadding
|
||||
|
@ -376,6 +378,9 @@ func exifOrientation(r io.Reader) (opt Options) {
|
|||
|
||||
func addSizeIndicator(m image.Image, size string) image.Image {
|
||||
relativePath := "../.."
|
||||
if os.Getenv("GOTESTS") == "true" {
|
||||
relativePath = "./"
|
||||
}
|
||||
if os.Getenv("DOCKER") == "true" {
|
||||
relativePath = ""
|
||||
}
|
||||
|
@ -431,7 +436,6 @@ func transformImage(m image.Image, opt Options) image.Image {
|
|||
// Parse crop and resize parameters before applying any transforms.
|
||||
// This is to ensure that any percentage-based values are based off the
|
||||
// size of the original image.
|
||||
start := time.Now()
|
||||
rect := cropParams(m, opt)
|
||||
w, h, resize := resizeParams(m, opt)
|
||||
|
||||
|
@ -478,7 +482,5 @@ func transformImage(m image.Image, opt Options) image.Image {
|
|||
m = addSizeIndicator(m, opt.IndicatorSize)
|
||||
}
|
||||
|
||||
imageTransformationSummary.Observe(float64(time.Since(start).Seconds()))
|
||||
|
||||
return m
|
||||
}
|
||||
|
|
|
@ -15,8 +15,11 @@
|
|||
package imageproxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"golang.org/x/image/bmp"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
|
@ -24,11 +27,11 @@ import (
|
|||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
"golang.org/x/image/bmp"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -116,7 +119,7 @@ func TestTransform(t *testing.T) {
|
|||
{"bmp", func(w io.Writer, m image.Image) { bmp.Encode(w, m) }, true},
|
||||
{"gif", func(w io.Writer, m image.Image) { gif.Encode(w, m, nil) }, true},
|
||||
{"jpeg", func(w io.Writer, m image.Image) { jpeg.Encode(w, m, nil) }, false},
|
||||
{"png", func(w io.Writer, m image.Image) { png.Encode(w, m) }, true},
|
||||
{"png", func(w io.Writer, m image.Image) { png.Encode(w, m) }, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
@ -128,7 +131,8 @@ func TestTransform(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Transform with encoder %s returned unexpected error: %v", tt.name, err)
|
||||
}
|
||||
if !reflect.DeepEqual(in, out) {
|
||||
|
||||
if tt.exactOutput && !reflect.DeepEqual(in, out) {
|
||||
t.Errorf("Transform with with encoder %s with empty options returned modified result", tt.name)
|
||||
}
|
||||
|
||||
|
@ -136,6 +140,7 @@ func TestTransform(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Transform with encoder %s returned unexpected error: %v", tt.name, err)
|
||||
}
|
||||
|
||||
if len(out) == 0 {
|
||||
t.Errorf("Transform with encoder %s returned empty bytes", tt.name)
|
||||
}
|
||||
|
@ -380,3 +385,69 @@ func TestTransformImage(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTWMChanges(t *testing.T) {
|
||||
src, err := getTwmTestImage("test-images/unanime.png")
|
||||
if err != nil {
|
||||
t.Errorf("test failed getting image: %v", err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
matchOutFilename string
|
||||
options Options
|
||||
}{
|
||||
{"jpeg 400x", "test-images/unanime-400x.png", Options{Width: 400}},
|
||||
{"jpeg 400x,200ml", "test-images/unanime-400x,200ml.png", Options{Width: 400, IndicatorSize: optIndicatorSize200ml, Square: true}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
shouldOut, err := getTwmTestImage(tt.matchOutFilename)
|
||||
if err != nil {
|
||||
t.Errorf("test failed getting out image %s: %v", tt.matchOutFilename, err)
|
||||
continue
|
||||
}
|
||||
out, err := Transform(src, tt.options)
|
||||
if err != nil {
|
||||
t.Errorf("Transform with encoder %s returned unexpected error: %v", tt.name, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(shouldOut, out) {
|
||||
TMPORARYWriteTestImage("test-images/tmepout.png", out)
|
||||
t.Errorf("Transform with with encoder %s with empty options returned modified result", tt.name)
|
||||
}
|
||||
|
||||
fmt.Println("done", tt.name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getTwmTestImage(name string) ([]byte, error) {
|
||||
src, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stat, err := src.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bs := make([]byte, stat.Size())
|
||||
bufio.NewReader(src).Read(bs)
|
||||
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
func TMPORARYWriteTestImage(name string, bs []byte) {
|
||||
f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
n, err := f.Write(bs)
|
||||
fmt.Println(n, err)
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue