Skip to content
Open
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go_version: [ 1.21 ]
go_version: [ 1.22 ]
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Dependencies
run: |
sudo apt update
sudo apt install -y webp
- name: Setup Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go_version }}
- name: Test
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.21-bullseye AS build
FROM golang:1.22-bullseye AS build

ARG OYAKI_VERSION

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ Environment variables bellow are available.
- `OYAKI_ORIGIN_HOST`: Your origin host. Example: `example.com` (required)
- `OYAKI_ORIGIN_SCHEME`: Scheme to request to your origin. Default: `https`
- `OYAKI_QUALITY`: Image quality. Default: `90`

### Use OpenTelemetry

- `OTEL_EXPORTER_OTLP_ENDPOINT`: OpenTelemetry collector endpoint. Example: `localhost:4317`
6 changes: 5 additions & 1 deletion convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ package main

import (
"bytes"
"context"
"image/jpeg"
"io"

"github.com/disintegration/imaging"
)

func convert(src io.Reader, q int) (*bytes.Buffer, error) {
func convert(ctx context.Context, src io.Reader, q int) (*bytes.Buffer, error) {
ctx, span := tracer.Start(ctx, "convert")
defer span.End()

img, err := imaging.Decode(src, imaging.AutoOrientation(true))
if err != nil {
return nil, err
Expand Down
27 changes: 25 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@ module github.com/pepabo/oyaki

go 1.19

require github.com/disintegration/imaging v1.6.2
require (
github.com/disintegration/imaging v1.6.2
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
go.opentelemetry.io/otel/sdk v1.24.0
go.opentelemetry.io/otel/trace v1.24.0
google.golang.org/grpc v1.62.1
)

require golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
require (
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/protobuf v1.32.0 // indirect
)
51 changes: 51 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
24 changes: 19 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bytes"
"context"
"errors"
"flag"
"fmt"
Expand All @@ -28,6 +29,12 @@ func main() {
flag.BoolVar(&ver, "version", false, "show version")
flag.Parse()

ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
shutdown := initProvider(ctx)
defer cancel()
defer shutdown()

if ver {
fmt.Printf("oyaki %s\n", getVersion())
return
Expand All @@ -46,10 +53,17 @@ func main() {

log.Printf("starting oyaki %s\n", getVersion())
http.HandleFunc("/", proxy)
http.ListenAndServe(":8080", nil)

if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}

func proxy(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx, span := tracer.Start(ctx, "httpRequest")
defer span.End()

path := r.URL.RequestURI()
if path == "/" {
fmt.Fprintln(w, "Oyaki lives!")
Expand Down Expand Up @@ -82,7 +96,7 @@ func proxy(w http.ResponseWriter, r *http.Request) {
var orgRes *http.Response
pathExt := filepath.Ext(req.URL.Path)
if pathExt == ".webp" {
orgRes, err = doWebp(req)
orgRes, err = doWebp(ctx, req)
} else {
orgRes, err = client.Do(req)
}
Expand Down Expand Up @@ -147,15 +161,15 @@ func proxy(w http.ResponseWriter, r *http.Request) {

body := io.NopCloser(bytes.NewBuffer(resBytes))
defer body.Close()
buf, err = convWebp(body, []string{})
buf, err = convWebp(ctx, body, []string{})
if err == nil {
defer buf.Reset()
w.Header().Set("Content-Type", "image/webp")
} else {
// if err, normally convertion will be proceeded
body = io.NopCloser(bytes.NewBuffer(resBytes))
defer body.Close()
buf, err = convert(body, quality)
buf, err = convert(ctx, body, quality)
if err != nil {
http.Error(w, "Image convert failed", http.StatusInternalServerError)
log.Printf("Image convert failed. %v\n", err)
Expand All @@ -165,7 +179,7 @@ func proxy(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/jpeg")
}
} else {
buf, err = convert(orgRes.Body, quality)
buf, err = convert(ctx, orgRes.Body, quality)
if err != nil {
http.Error(w, "Image convert failed", http.StatusInternalServerError)
log.Printf("Image convert failed. %v\n", err)
Expand Down
74 changes: 74 additions & 0 deletions trace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

import (
"context"
"log"
"os"
"strconv"
"time"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"google.golang.org/grpc"
)

var tracer = otel.Tracer("oyaki")

func initProvider(ctx context.Context) func() {
res, err := resource.New(ctx,
resource.WithFromEnv(),
resource.WithProcess(),
resource.WithTelemetrySDK(),
resource.WithHost(),
resource.WithAttributes(
semconv.ServiceNameKey.String("oyaki"),
),
)
handleErr(err, "failed to create resource")

traceClient := otlptracegrpc.NewClient(
otlptracegrpc.WithInsecure(),
otlptracegrpc.WithDialOption(grpc.WithBlock()))
traceExp, err := otlptrace.New(ctx, traceClient)
handleErr(err, "Failed to create the collector trace exporter")

samplingRate := os.Getenv("OTEL_TRACES_SAMPLING_RATE")
if samplingRate == "" {
samplingRate = "1"
}
samplingRateFloat, err := strconv.ParseFloat(samplingRate, 64)
if err != nil {
handleErr(err, "Failed to parse OTEL_TRACES_SAMPLING_RATE as float64, using default 1.0")
}
sampler := sdktrace.ParentBased(sdktrace.TraceIDRatioBased(samplingRateFloat))

bsp := sdktrace.NewBatchSpanProcessor(traceExp)
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sampler),
sdktrace.WithResource(res),
sdktrace.WithSpanProcessor(bsp),
)

// set global propagator to tracecontext (the default is no-op).
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
otel.SetTracerProvider(tracerProvider)

return func() {
cxt, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
if err := traceExp.Shutdown(cxt); err != nil {
otel.Handle(err)
}
}
}

func handleErr(err error, message string) {
if err != nil {
log.Printf("%s: %v", message, err)
}
}
11 changes: 9 additions & 2 deletions webp.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bytes"
"context"
"fmt"
"io"
"log"
Expand All @@ -13,7 +14,10 @@ import (
"github.com/disintegration/imaging"
)

func doWebp(req *http.Request) (*http.Response, error) {
func doWebp(ctx context.Context, req *http.Request) (*http.Response, error) {
ctx, span := tracer.Start(ctx, "doWebp")
defer span.End()

var orgRes *http.Response
orgURL := req.URL
newPath := orgURL.Path[:len(orgURL.Path)-len(".webp")]
Expand Down Expand Up @@ -44,7 +48,10 @@ func doWebp(req *http.Request) (*http.Response, error) {
return orgRes, nil
}

func convWebp(src io.Reader, params []string) (*bytes.Buffer, error) {
func convWebp(ctx context.Context, src io.Reader, params []string) (*bytes.Buffer, error) {
ctx, span := tracer.Start(ctx, "convWebp")
defer span.End()

f, err := os.CreateTemp("/tmp", "")
if err != nil {
return nil, err
Expand Down
10 changes: 7 additions & 3 deletions webp_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"io"
"net/http"
"net/http/httptest"
Expand All @@ -18,7 +19,9 @@ func TestProxyWebP(t *testing.T) {
url := ts.URL + "/oyaki.jpg.webp"

req, _ := http.NewRequest("GET", url, nil)
resp, err := doWebp(req)

ctx := context.Background()
resp, err := doWebp(ctx, req)
if err != nil {
t.Fatal(err)
} else {
Expand All @@ -43,12 +46,13 @@ func TestConvJPG2WebP(t *testing.T) {
url := ts.URL + "/oyaki.jpg.webp"

req, _ := http.NewRequest("GET", url, nil)
resp, err := doWebp(req)
ctx := context.Background()
resp, err := doWebp(ctx, req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
_, err = convWebp(resp.Body, []string{})
_, err = convWebp(ctx, resp.Body, []string{})
if err != nil {
t.Fatal(err)
}
Expand Down