mirror of
https://github.com/foomo/contentfulproxy.git
synced 2025-10-16 12:25:37 +00:00
* optimize logging
* add gitlab actions * add make support * configure docker tasks
This commit is contained in:
parent
23e1432203
commit
f3b25c2cbf
31
.github/workflows/ci.yml
vendored
Normal file
31
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOFLAGS: -mod=readonly
|
||||
GOPROXY: https://proxy.golang.org
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
|
||||
- name: Test
|
||||
run: go test ./...
|
||||
36
.github/workflows/release.yml
vendored
Normal file
36
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: goreleaser
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*.*.*
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOFLAGS: -mod=readonly
|
||||
GOPROXY: https://proxy.golang.org
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN }}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
||||
.*
|
||||
!.git*
|
||||
|
||||
bin/contentfulproxy
|
||||
|
||||
32
Dockerfile
32
Dockerfile
@ -1,30 +1,38 @@
|
||||
##############################
|
||||
###### STAGE: BUILD ######
|
||||
##############################
|
||||
FROM golang:1.14-alpine AS build-env
|
||||
FROM golang:1.17-alpine AS build-env
|
||||
|
||||
ENV GO111MODULE=on
|
||||
|
||||
RUN apk add --no-cache upx
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go mod download
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
RUN go mod download && go mod vendor
|
||||
RUN GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -trimpath -o /contentfulproxy
|
||||
RUN GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -ldflags "-w -s" -trimpath -o ./bin/contentfulproxy cmd/contentfulproxy/main.go
|
||||
|
||||
ENV UPX="-1"
|
||||
RUN upx /src/bin/contentfulproxy
|
||||
|
||||
##############################
|
||||
###### STAGE: PACKAGE ######
|
||||
##############################
|
||||
FROM alpine:3.11
|
||||
FROM alpine:latest
|
||||
|
||||
ENV CONTENTFULPROXY_SERVER_ADDR=0.0.0.0:80
|
||||
ENV LOG_JSON=1
|
||||
|
||||
RUN apk add --update --no-cache ca-certificates curl bash && rm -rf /var/cache/apk/*
|
||||
RUN apk add --update --no-cache ca-certificates
|
||||
|
||||
COPY --from=build-env /contentfulproxy /usr/sbin/contentfulproxy
|
||||
|
||||
ENTRYPOINT ["/usr/sbin/contentfulproxy"]
|
||||
|
||||
CMD ["-webserver-address=$CONTENTFULPROXY_SERVER_ADDR"]
|
||||
COPY --from=build-env /src/bin/contentfulproxy /usr/sbin/contentfulproxy
|
||||
|
||||
EXPOSE 80
|
||||
# Zap
|
||||
@ -33,3 +41,7 @@ EXPOSE 9100
|
||||
EXPOSE 9200
|
||||
# Viper
|
||||
EXPOSE 9300
|
||||
|
||||
ENTRYPOINT ["/usr/sbin/contentfulproxy"]
|
||||
|
||||
CMD ["-webserver-address=$CONTENTFULPROXY_SERVER_ADDR"]
|
||||
|
||||
114
Makefile
114
Makefile
@ -1,47 +1,87 @@
|
||||
SHELL := /bin/bash
|
||||
.DEFAULT_GOAL:=help
|
||||
|
||||
TAG?=latest
|
||||
IMAGE=foomo/contentfulproxy
|
||||
#IMAGE=foomo/contentfulproxy
|
||||
IMAGE=docker-registry.bestbytes.net/galeria/site/contentfulproxy
|
||||
# https://hub.docker.com/repository/docker/foomo/contentfulproxy
|
||||
|
||||
# Utils
|
||||
## === Tasks ===
|
||||
|
||||
all: build test
|
||||
tag:
|
||||
echo $(TAG)
|
||||
dep:
|
||||
env GO111MODULE=on go mod download && env GO111MODULE=on go mod vendor && go install -i ./vendor/...
|
||||
.PHONY: clean
|
||||
## Clean build
|
||||
clean:
|
||||
rm -fv bin/contentfulprox*
|
||||
|
||||
# Build
|
||||
|
||||
build: clean
|
||||
go build -o bin/contentfulproxy
|
||||
build-arch: clean
|
||||
GOOS=linux GOARCH=amd64 go build -o bin/contentfulproxy-linux-amd64
|
||||
GOOS=darwin GOARCH=amd64 go build -o bin/contentfulproxy-darwin-amd64
|
||||
build-docker: clean build-arch
|
||||
curl https://curl.haxx.se/ca/cacert.pem > .cacert.pem
|
||||
docker build -q . > .image_id
|
||||
docker tag `cat .image_id` $(IMAGE):$(TAG)
|
||||
echo "# tagged container `cat .image_id` as $(IMAGE):$(TAG)"
|
||||
rm -vf .image_id .cacert.pem
|
||||
|
||||
package: build
|
||||
pkg/build.sh
|
||||
|
||||
# Docker
|
||||
|
||||
docker-build:
|
||||
docker build -t $(IMAGE):$(TAG) .
|
||||
|
||||
docker-push:
|
||||
docker push $(IMAGE):$(TAG)
|
||||
|
||||
# Testing / benchmarks
|
||||
|
||||
.PHONY: test
|
||||
## Run tests
|
||||
test:
|
||||
go test -v ./...
|
||||
|
||||
bench:
|
||||
go test -run=none -bench=. ./...
|
||||
.PHONY: lint
|
||||
## Run linter
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: lint.fix
|
||||
## Fix lint violations
|
||||
lint.fix:
|
||||
golangci-lint run --fix
|
||||
|
||||
## === Binary ===
|
||||
|
||||
.PHONY: build
|
||||
## Build binary
|
||||
build: clean
|
||||
go build -o bin/contentfulproxy cmd/contentfulproxy/main.go
|
||||
|
||||
.PHONY: build.arch
|
||||
## Build arch binaries
|
||||
build.arch: clean
|
||||
GOOS=linux GOARCH=amd64 go build -o bin/contentfulproxy-linux-amd64 cmd/contentfulproxy
|
||||
GOOS=darwin GOARCH=amd64 go build -o bin/contentfulproxy-darwin-amd64 cmd/contentfulproxy
|
||||
|
||||
## === Docker ===
|
||||
|
||||
.PHONY: docker.build
|
||||
## Build docker image
|
||||
docker.build:
|
||||
docker build -t $(IMAGE):$(TAG) .
|
||||
|
||||
.PHONY: docker.push
|
||||
## Push docker image
|
||||
docker.push:
|
||||
docker push $(IMAGE):$(TAG)
|
||||
|
||||
## === Utils ===
|
||||
|
||||
## Show help text
|
||||
help:
|
||||
@awk '{ \
|
||||
if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \
|
||||
helpCommand = substr($$0, index($$0, ":") + 2); \
|
||||
if (helpMessage) { \
|
||||
printf "\033[36m%-23s\033[0m %s\n", \
|
||||
helpCommand, helpMessage; \
|
||||
helpMessage = ""; \
|
||||
} \
|
||||
} else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \
|
||||
helpCommand = substr($$0, 0, index($$0, ":")); \
|
||||
if (helpMessage) { \
|
||||
printf "\033[36m%-23s\033[0m %s\n", \
|
||||
helpCommand, helpMessage"\n"; \
|
||||
helpMessage = ""; \
|
||||
} \
|
||||
} else if ($$0 ~ /^##/) { \
|
||||
if (helpMessage) { \
|
||||
helpMessage = helpMessage"\n "substr($$0, 3); \
|
||||
} else { \
|
||||
helpMessage = substr($$0, 3); \
|
||||
} \
|
||||
} else { \
|
||||
if (helpMessage) { \
|
||||
print "\n "helpMessage"\n" \
|
||||
} \
|
||||
helpMessage = ""; \
|
||||
} \
|
||||
}' \
|
||||
$(MAKEFILE_LIST)
|
||||
|
||||
@ -1,2 +1,5 @@
|
||||
# contentfulproxy
|
||||
an experimental proxy for read access to contentful to save your API quota
|
||||
|
||||
|
||||
docker run -p 8080 -e SERVICE_ZAP_ENDABLED=true -e RPOXOY=a.com,b.com foomo/contentfulproxy
|
||||
|
||||
66
cmd/contentfulproxy/main.go
Normal file
66
cmd/contentfulproxy/main.go
Normal file
@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/foomo/contentfulproxy/packages/go/config"
|
||||
"github.com/foomo/contentfulproxy/proxy"
|
||||
"github.com/foomo/keel"
|
||||
"github.com/foomo/keel/log"
|
||||
"github.com/foomo/keel/net/http/middleware"
|
||||
)
|
||||
|
||||
const (
|
||||
ServiceName = "Contentful Proxy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
svr := keel.NewServer(
|
||||
keel.WithHTTPZapService(false),
|
||||
keel.WithHTTPViperService(false),
|
||||
keel.WithHTTPPrometheusService(false),
|
||||
)
|
||||
|
||||
// get the logger
|
||||
l := svr.Logger()
|
||||
|
||||
// register Closers for graceful shutdowns
|
||||
svr.AddClosers()
|
||||
|
||||
c := svr.Config()
|
||||
webhookURLs := config.DefaultWebhookURLs(c)
|
||||
webserverAddress := config.DefaultWebserverAddress(c)
|
||||
webserverPath := config.DefaultWebserverPath(c)
|
||||
backendURL := config.DefaultBackendURL(c)
|
||||
|
||||
|
||||
// create proxy
|
||||
proxy, _ := proxy.NewProxy(
|
||||
context.Background(),
|
||||
log.WithServiceName(l, ServiceName),
|
||||
backendURL,
|
||||
webserverPath,
|
||||
webhookURLs,
|
||||
)
|
||||
|
||||
// add the service to keel
|
||||
svr.AddServices(
|
||||
keel.NewServiceHTTP(
|
||||
log.WithServiceName(l, ServiceName),
|
||||
ServiceName,
|
||||
webserverAddress(),
|
||||
proxy,
|
||||
getMiddleWares()...,
|
||||
),
|
||||
)
|
||||
svr.Run()
|
||||
}
|
||||
|
||||
func getMiddleWares() []middleware.Middleware {
|
||||
return []middleware.Middleware{
|
||||
middleware.Logger(),
|
||||
middleware.Telemetry(),
|
||||
middleware.RequestID(),
|
||||
middleware.Recover(),
|
||||
}
|
||||
}
|
||||
@ -1,82 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/foomo/contentfulproxy/proxy"
|
||||
"github.com/foomo/keel"
|
||||
"github.com/foomo/keel/log"
|
||||
"github.com/foomo/keel/net/http/middleware"
|
||||
)
|
||||
|
||||
const (
|
||||
ServiceName = "Contentful Proxy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
svr := keel.NewServer(
|
||||
keel.WithHTTPZapService(true),
|
||||
keel.WithHTTPViperService(true),
|
||||
keel.WithHTTPPrometheusService(true),
|
||||
)
|
||||
|
||||
// get the logger
|
||||
l := svr.Logger()
|
||||
|
||||
// register Closers for graceful shutdowns
|
||||
svr.AddClosers()
|
||||
|
||||
// define and process flags and arguments
|
||||
webserverAddress := flag.String("webserver-address", ":80", "address to bind web server host:port")
|
||||
webserverPath := flag.String("webserver-path", "", "path to export the webserver on")
|
||||
backendURL := flag.String("backend-url", "https://cdn.contentful.com", "endpoint of the contentful api")
|
||||
flag.Parse()
|
||||
webhooks, err := getWebhooks()
|
||||
if err != nil {
|
||||
l.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// create proxy
|
||||
proxy, _ := proxy.NewProxy(
|
||||
context.Background(),
|
||||
l,
|
||||
*backendURL,
|
||||
*webserverPath,
|
||||
webhooks,
|
||||
)
|
||||
|
||||
// add the service to keel
|
||||
svr.AddServices(
|
||||
keel.NewServiceHTTP(
|
||||
log.WithServiceName(l, ServiceName),
|
||||
ServiceName,
|
||||
*webserverAddress,
|
||||
proxy,
|
||||
getMiddleWares()...,
|
||||
),
|
||||
)
|
||||
svr.Run()
|
||||
}
|
||||
|
||||
func getMiddleWares() []middleware.Middleware {
|
||||
return []middleware.Middleware{
|
||||
middleware.Logger(),
|
||||
middleware.Telemetry(),
|
||||
middleware.RequestID(),
|
||||
middleware.Recover(),
|
||||
}
|
||||
}
|
||||
|
||||
func getWebhooks() (proxy.WebHooks, error) {
|
||||
args := flag.Args()
|
||||
if len(args) == 0 {
|
||||
return nil, fmt.Errorf("missing webhook arguments on startup")
|
||||
}
|
||||
webhooks := proxy.WebHooks{}
|
||||
for _, v := range args {
|
||||
webhooks = append(webhooks, proxy.WebHookURL(v))
|
||||
}
|
||||
return webhooks, nil
|
||||
}
|
||||
4
go.mod
4
go.mod
@ -3,11 +3,9 @@ module github.com/foomo/contentfulproxy
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/foomo/contentful v0.3.6
|
||||
github.com/foomo/keel v0.3.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.uber.org/zap v1.19.1
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
)
|
||||
|
||||
require (
|
||||
@ -58,6 +56,7 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
||||
@ -66,5 +65,4 @@ require (
|
||||
gopkg.in/ini.v1 v1.51.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
moul.io/http2curl v1.0.0 // indirect
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@ -93,8 +93,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/foomo/contentful v0.3.6 h1:yiwhWayrXCe0wpQGzhO32bl8AE/46T261bBqNdPODWk=
|
||||
github.com/foomo/contentful v0.3.6/go.mod h1:6Pf8efSKeMbwKgVkjSFWeBsLYdUA0NTW7OUsMWzHXvY=
|
||||
github.com/foomo/keel v0.3.1 h1:be9EDLWsRzCXuf2rKhS6iShSAOMFQ3GpuoWOm80OKew=
|
||||
github.com/foomo/keel v0.3.1/go.mod h1:gZPfO3DjBFkauNeeurMqvLuXNBV5hNgUZj0hJfib+kk=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
@ -789,8 +787,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
|
||||
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
|
||||
29
packages/go/config/proxy.go
Normal file
29
packages/go/config/proxy.go
Normal file
@ -0,0 +1,29 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
keelconfig "github.com/foomo/keel/config"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
WebserverAddress = "webserver.address"
|
||||
WebserverPath = "webserver.path"
|
||||
BackendURL = "backend.url"
|
||||
WebhookURLs = "webhook.urls"
|
||||
)
|
||||
|
||||
func DefaultWebhookURLs(c *viper.Viper) func() []string {
|
||||
return keelconfig.GetStringSlice(c, WebhookURLs, []string{})
|
||||
}
|
||||
|
||||
func DefaultWebserverAddress(c *viper.Viper) func() string {
|
||||
return keelconfig.GetString(c, WebserverAddress, ":80")
|
||||
}
|
||||
|
||||
func DefaultWebserverPath(c *viper.Viper) func() string {
|
||||
return keelconfig.GetString(c, WebserverPath, "")
|
||||
}
|
||||
|
||||
func DefaultBackendURL(c *viper.Viper) func() string {
|
||||
return keelconfig.GetString(c, BackendURL, "https://cdn.contentful.com")
|
||||
}
|
||||
26
packages/go/log/log.go
Normal file
26
packages/go/log/log.go
Normal file
@ -0,0 +1,26 @@
|
||||
package log
|
||||
|
||||
import "go.uber.org/zap"
|
||||
|
||||
const (
|
||||
ServiceRoutineKey = "service_routine"
|
||||
CacheIdKey = "cache_id"
|
||||
URLKey = "url"
|
||||
NumberOfWaitingClientsKey = "num_waiting_clients"
|
||||
)
|
||||
|
||||
func FServiceRoutine(name string) zap.Field {
|
||||
return zap.String(ServiceRoutineKey, name)
|
||||
}
|
||||
|
||||
func FCacheId(name string) zap.Field {
|
||||
return zap.String(CacheIdKey, name)
|
||||
}
|
||||
|
||||
func FURL(name string) zap.Field {
|
||||
return zap.String(URLKey, name)
|
||||
}
|
||||
|
||||
func FNumberOfWaitingClients(num int) zap.Field {
|
||||
return zap.Int(NumberOfWaitingClientsKey, num)
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
Packaging & Deployment
|
||||
----------------------
|
||||
|
||||
In order to build packages and upload to Package Cloud, please install the following requirements and run the make task.
|
||||
|
||||
[Package Cloud Command Line Client](https://packagecloud.io/docs#cli_install)
|
||||
|
||||
```
|
||||
$ gem install package_cloud
|
||||
```
|
||||
|
||||
[FPM](https://github.com/jordansissel/fpm)
|
||||
|
||||
```
|
||||
$ gem install fpm
|
||||
```
|
||||
|
||||
Building package
|
||||
|
||||
```
|
||||
$ make package
|
||||
```
|
||||
|
||||
*NOTE: you will be prompted for Package Cloud credentials.*
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
```
|
||||
$ git clone https://github.com/foomo/contentfulproxy.git
|
||||
$ cd contentfulproxy
|
||||
$ make test
|
||||
```
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests and examples for any new or changed functionality.
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`\)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`\)
|
||||
4. Push to the branch (`git push origin my-new-feature`\)
|
||||
5. Create new Pull Request
|
||||
61
pkg/build.sh
61
pkg/build.sh
@ -1,61 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
USER="foomo"
|
||||
NAME="contentfulproxy"
|
||||
URL="http://www.foomo.org"
|
||||
DESCRIPTION="An experimental proxy for read access to contentful to save your API quota"
|
||||
LICENSE="LGPL-3.0"
|
||||
|
||||
# get version
|
||||
VERSION=`bin/contentfulproxy --version | sed 's/contentfulproxy //'`
|
||||
|
||||
# create temp dir
|
||||
TEMP=`pwd`/pkg/tmp
|
||||
mkdir -p $TEMP
|
||||
|
||||
package()
|
||||
{
|
||||
OS=$1
|
||||
ARCH=$2
|
||||
TYPE=$3
|
||||
TARGET=$4
|
||||
|
||||
# copy license file
|
||||
cp LICENSE $LICENSE
|
||||
|
||||
# define source dir
|
||||
SOURCE=`pwd`/pkg/${TYPE}
|
||||
|
||||
# create build folder
|
||||
BUILD=${TEMP}/${NAME}-${VERSION}
|
||||
#rsync -rv --exclude **/.git* --exclude /*.sh $SOURCE/ $BUILD/
|
||||
|
||||
# build binary
|
||||
GOOS=$OS GOARCH=$ARCH go build -o $BUILD/usr/local/bin/${NAME}
|
||||
|
||||
# create package
|
||||
fpm -s dir \
|
||||
-t $TYPE \
|
||||
--name $NAME \
|
||||
--maintainer $USER \
|
||||
--version $VERSION \
|
||||
--license $LICENSE \
|
||||
--description "${DESCRIPTION}" \
|
||||
--architecture $ARCH \
|
||||
--package $TEMP \
|
||||
--url "${URL}" \
|
||||
-C $BUILD \
|
||||
.
|
||||
|
||||
# push
|
||||
package_cloud push $TARGET $TEMP/${NAME}_${VERSION}_${ARCH}.${TYPE}
|
||||
|
||||
# cleanup
|
||||
rm -rf $TEMP
|
||||
rm $LICENSE
|
||||
}
|
||||
|
||||
package linux amd64 deb foomo/contentfulproxy/ubuntu/precise
|
||||
package linux amd64 deb foomo/contentfulproxy/ubuntu/trusty
|
||||
|
||||
#package linux amd64 rpm
|
||||
@ -3,6 +3,7 @@ package proxy
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"github.com/foomo/contentfulproxy/packages/go/log"
|
||||
"go.uber.org/zap"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@ -21,14 +22,14 @@ type cachedResponse struct {
|
||||
}
|
||||
type cacheMap map[cacheID]*cachedResponse
|
||||
|
||||
type cache struct {
|
||||
type Cache struct {
|
||||
sync.RWMutex
|
||||
cacheMap cacheMap
|
||||
webHooks WebHooks
|
||||
webHooks func() []string
|
||||
l *zap.Logger
|
||||
}
|
||||
|
||||
func (c *cache) set(id cacheID, response *http.Response) (*cachedResponse, error) {
|
||||
func (c *Cache) set(id cacheID, response *http.Response) (*cachedResponse, error) {
|
||||
responseBytes, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -47,33 +48,39 @@ func (c *cache) set(id cacheID, response *http.Response) (*cachedResponse, error
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
func (c *cache) get(id cacheID) (*cachedResponse, bool) {
|
||||
func (c *Cache) get(id cacheID) (*cachedResponse, bool) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
response, ok := c.cacheMap[id]
|
||||
return response, ok
|
||||
}
|
||||
|
||||
func (c *cache) update() {
|
||||
func (c *Cache) update() {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
c.cacheMap = cacheMap{}
|
||||
c.l.Info("flushed the cache", zap.Int("length", len(c.cacheMap)))
|
||||
c.l.Info("flushed the cache")
|
||||
}
|
||||
|
||||
func (c *cache) callWebHooks() {
|
||||
type r struct {
|
||||
Url WebHookURL
|
||||
Resp *http.Response
|
||||
Err error
|
||||
func (c *Cache) callWebHooks() {
|
||||
for _, url := range c.webHooks() {
|
||||
go func(url string, l *zap.Logger) {
|
||||
l.Info("call webhook")
|
||||
_, err := http.Get(url)
|
||||
if err != nil {
|
||||
l.Error("error while calling webhook", zap.Error(err))
|
||||
}
|
||||
}(url, c.l.With(log.FURL(url)))
|
||||
}
|
||||
}
|
||||
|
||||
for _, url := range c.webHooks {
|
||||
go func(url WebHookURL, l *zap.Logger) {
|
||||
l.Info("call webhook", zap.String("url", string(url)))
|
||||
http.Get(string(url))
|
||||
}(url, c.l.With(zap.String("url", string(url))))
|
||||
func NewCache(l *zap.Logger, webHooks func() []string) *Cache {
|
||||
c := &Cache{
|
||||
cacheMap: cacheMap{},
|
||||
webHooks: webHooks,
|
||||
l: l.With(log.FServiceRoutine("cache")),
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func getCacheIDForRequest(r *http.Request) cacheID {
|
||||
|
||||
@ -15,9 +15,9 @@ type requestJob struct {
|
||||
|
||||
type jobRunner func(job requestJob, id cacheID)
|
||||
|
||||
func getJobRunner(c *cache, backendURL string, chanJobDone chan requestJobDone) jobRunner {
|
||||
func getJobRunner(c *Cache, backendURL func() string, chanJobDone chan requestJobDone) jobRunner {
|
||||
return func(job requestJob, id cacheID) {
|
||||
req, err := http.NewRequest("GET", backendURL+job.request.URL.RequestURI(), nil)
|
||||
req, err := http.NewRequest("GET", backendURL()+job.request.URL.RequestURI(), nil)
|
||||
if err != nil {
|
||||
chanJobDone <- requestJobDone{
|
||||
id: id,
|
||||
|
||||
@ -3,39 +3,37 @@ package proxy
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/foomo/contentfulproxy/packages/go/log"
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type WebHookURL string
|
||||
type WebHooks []WebHookURL
|
||||
|
||||
type Info struct {
|
||||
WebHooks WebHooks `json:"webhooks,omitempty"`
|
||||
WebHooks []string `json:"webhooks,omitempty"`
|
||||
CacheLength int `json:"cachelength,omitempty"`
|
||||
BackendURL string `json:"backendurl,omitempty"`
|
||||
}
|
||||
|
||||
type Proxy struct {
|
||||
l *zap.Logger
|
||||
cache *cache
|
||||
backendURL string
|
||||
pathPrefix string
|
||||
cache *Cache
|
||||
backendURL func() string
|
||||
pathPrefix func() string
|
||||
chanRequestJob chan requestJob
|
||||
chanFlushJob chan requestFlush
|
||||
}
|
||||
|
||||
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case p.pathPrefix + "/update":
|
||||
case p.pathPrefix() + "/update":
|
||||
command := requestFlush("doit")
|
||||
p.chanFlushJob <- command
|
||||
return
|
||||
case p.pathPrefix + "/info":
|
||||
case p.pathPrefix() + "/info":
|
||||
info := Info{
|
||||
WebHooks: p.cache.webHooks,
|
||||
BackendURL: p.backendURL,
|
||||
WebHooks: p.cache.webHooks(),
|
||||
BackendURL: p.backendURL(),
|
||||
CacheLength: len(p.cache.cacheMap),
|
||||
}
|
||||
jsonResponse(w, info, http.StatusOK)
|
||||
@ -55,14 +53,14 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
jobDone := <-chanDone
|
||||
if jobDone.err != nil {
|
||||
p.l.Error("cache / job error", zap.String("url", r.RequestURI))
|
||||
http.Error(w, "cache / job error", http.StatusInternalServerError)
|
||||
p.l.Error("Cache / job error", zap.String("url", r.RequestURI))
|
||||
http.Error(w, "Cache / job error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
cachedResponse = jobDone.cachedResponse
|
||||
p.l.Info("serve response after cache creation", zap.String("url", r.RequestURI), zap.String("cache id", string(cacheID)))
|
||||
p.l.Info("serve response after cache creation", log.FURL(r.RequestURI), log.FCacheId(string(cacheID)))
|
||||
} else {
|
||||
p.l.Info("serve response from cache", zap.String("url", r.RequestURI), zap.String("cache id", string(cacheID)))
|
||||
p.l.Info("serve response from cache", log.FURL(r.RequestURI), log.FCacheId(string(cacheID)))
|
||||
}
|
||||
for key, values := range cachedResponse.header {
|
||||
for _, value := range values {
|
||||
@ -71,21 +69,17 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
_, err := w.Write(cachedResponse.response)
|
||||
if err != nil {
|
||||
p.l.Info("writing cached response failed", zap.String("url", r.RequestURI), zap.String("cache id", string(cacheID)))
|
||||
p.l.Info("writing cached response failed", log.FURL(r.RequestURI), log.FCacheId(string(cacheID)))
|
||||
}
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func NewProxy(ctx context.Context, l *zap.Logger, backendURL string, pathPrefix string, webHooks WebHooks) (*Proxy, error) {
|
||||
func NewProxy(ctx context.Context, l *zap.Logger, backendURL func() string, pathPrefix func() string, webHooks func() []string) (*Proxy, error) {
|
||||
chanRequest := make(chan requestJob)
|
||||
chanFlush := make(chan requestFlush)
|
||||
c := &cache{
|
||||
cacheMap: cacheMap{},
|
||||
webHooks: webHooks,
|
||||
l: l,
|
||||
}
|
||||
c := NewCache(l, webHooks)
|
||||
go getLoop(ctx, l, backendURL, c, chanRequest, chanFlush)
|
||||
return &Proxy{
|
||||
l: l,
|
||||
@ -100,8 +94,8 @@ func NewProxy(ctx context.Context, l *zap.Logger, backendURL string, pathPrefix
|
||||
func getLoop(
|
||||
ctx context.Context,
|
||||
l *zap.Logger,
|
||||
backendURL string,
|
||||
c *cache,
|
||||
backendURL func() string,
|
||||
c *Cache,
|
||||
chanRequestJob chan requestJob,
|
||||
chanFlush chan requestFlush,
|
||||
) {
|
||||
@ -110,20 +104,20 @@ func getLoop(
|
||||
jobRunner := getJobRunner(c, backendURL, chanJobDone)
|
||||
for {
|
||||
select {
|
||||
case command := <-chanFlush:
|
||||
l.Info("cache update command coming in", zap.String("flushCommand", string(command)))
|
||||
case <-chanFlush:
|
||||
l.Info("Cache update command coming in")
|
||||
c.update()
|
||||
c.callWebHooks()
|
||||
case nextJob := <-chanRequestJob:
|
||||
id := getCacheIDForRequest(nextJob.request)
|
||||
pendingRequests[id] = append(pendingRequests[id], nextJob.chanDone)
|
||||
requests := pendingRequests[id]
|
||||
cacheID := getCacheIDForRequest(nextJob.request)
|
||||
pendingRequests[cacheID] = append(pendingRequests[cacheID], nextJob.chanDone)
|
||||
requests := pendingRequests[cacheID]
|
||||
if len(requests) == 1 {
|
||||
l.Info("starting jobrunner for", zap.String("uri", nextJob.request.RequestURI), zap.String("id", string(id)))
|
||||
go jobRunner(nextJob, id)
|
||||
l.Info("starting jobrunner for", log.FURL(nextJob.request.RequestURI), log.FCacheId(string(cacheID)))
|
||||
go jobRunner(nextJob, cacheID)
|
||||
}
|
||||
case jobDone := <-chanJobDone:
|
||||
l.Info("request complete", zap.String("id", string(jobDone.id)), zap.Int("num-waiting-clients", len(pendingRequests[jobDone.id])))
|
||||
l.Info("request complete", log.FCacheId(string(jobDone.id)), log.FNumberOfWaitingClients(len(pendingRequests[jobDone.id])))
|
||||
for _, chanPending := range pendingRequests[jobDone.id] {
|
||||
chanPending <- jobDone
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -40,10 +41,10 @@ func GetBackend(t *testing.T) (getStats, http.HandlerFunc) {
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/foo":
|
||||
w.Write([]byte(responseFoo))
|
||||
_, _ = w.Write([]byte(responseFoo))
|
||||
return
|
||||
case "/bar":
|
||||
w.Write([]byte(responseBar))
|
||||
_, _ = w.Write([]byte(responseBar))
|
||||
return
|
||||
}
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
@ -69,11 +70,11 @@ func GetWebHook(t *testing.T) (getStats, http.HandlerFunc) {
|
||||
t.Log("webhook: url called", r.URL.Path)
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/update":
|
||||
w.Write([]byte(responseUpdate))
|
||||
case "/test1":
|
||||
_, _ = w.Write([]byte(responseUpdate))
|
||||
return
|
||||
case "/update":
|
||||
w.Write([]byte(responseFlush))
|
||||
case "/test2":
|
||||
_, _ = w.Write([]byte(responseFlush))
|
||||
return
|
||||
}
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
@ -92,11 +93,13 @@ func getTestServer(t *testing.T) (gs func(path string) int, ws func(path string)
|
||||
p, _ := NewProxy(
|
||||
context.Background(),
|
||||
l,
|
||||
backendServer.URL,
|
||||
"",
|
||||
[]WebHookURL{
|
||||
WebHookURL(webHookServer.URL + "/update"),
|
||||
WebHookURL(webHookServer.URL + "/update"),
|
||||
func() string {return backendServer.URL},
|
||||
func() string {return ""},
|
||||
func() []string {
|
||||
return []string{
|
||||
webHookServer.URL + "/test1",
|
||||
webHookServer.URL + "/test2",
|
||||
}
|
||||
},
|
||||
)
|
||||
s = httptest.NewServer(p)
|
||||
@ -118,7 +121,7 @@ func TestProxy(t *testing.T) {
|
||||
}
|
||||
for j := 0; j < 10; j++ {
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 1; i++ {
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
get("/foo")
|
||||
@ -129,12 +132,17 @@ func TestProxy(t *testing.T) {
|
||||
}
|
||||
assert.Equal(t, 1, gs("/foo"))
|
||||
|
||||
//
|
||||
http.Get(server.URL + "/update")
|
||||
|
||||
// check the current status
|
||||
//response, err := http.Get(server.URL + "/info")
|
||||
//assert.NoError(t, err)
|
||||
|
||||
//
|
||||
assert.Equal(t, 1, ws("/update"))
|
||||
assert.Equal(t, 1, ws("/update"))
|
||||
_, _ = http.Get(server.URL + "/update")
|
||||
|
||||
time.Sleep(time.Second * 1)
|
||||
|
||||
//
|
||||
assert.Equal(t, 1, ws("/test1"))
|
||||
assert.Equal(t, 1, ws("/test2"))
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user