refactor
This commit is contained in:
parent
185316b9ec
commit
05753d63b3
13
.app.config
Normal file
13
.app.config
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"ID": "api-gateway",
|
||||
"Name": "api-gateway",
|
||||
"Address": "__IP__",
|
||||
"Port": 443,
|
||||
"Check": {
|
||||
"HTTP": "http://__IP__:443/ping",
|
||||
"Interval": "10s",
|
||||
"Timeout": "1s",
|
||||
"Status": "passing",
|
||||
"DeregisterCriticalServiceAfter": "10s"
|
||||
}
|
||||
}
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
||||
.vscode
|
||||
|
||||
api-gateway/plugins/src/git.pbiernat.dev/traefik/plugin-egommerce-auth
|
||||
api-gateway/plugins/src/git.pbiernat.io/traefik/plugin-egommerce-auth
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "api-gateway/plugins/src/git.pbiernat.dev/traefik/plugin-egommerce-auth"]
|
||||
path = api-gateway/plugins/src/git.pbiernat.dev/traefik/plugin-egommerce-auth
|
||||
url = git@git.pbiernat.dev:traefik/plugin-egommerce-auth.git
|
||||
[submodule "api-gateway/plugins/src/git.pbiernat.io/traefik/plugin-egommerce-auth"]
|
||||
path = api-gateway/plugins/src/git.pbiernat.io/traefik/plugin-egommerce-auth
|
||||
url = git@git.pbiernat.io:traefik/plugin-egommerce-auth.git
|
||||
|
11
Dockerfile
11
Dockerfile
@ -1,4 +1,4 @@
|
||||
FROM traefik:v3.0
|
||||
FROM traefik:v3.1
|
||||
|
||||
ARG BUILD_TIME
|
||||
|
||||
@ -8,11 +8,14 @@ LABEL dev.egommerce.image.service="api-gateway"
|
||||
LABEL dev.egommerce.image.version="1.0"
|
||||
LABEL dev.egommerce.image.build_time=${BUILD_TIME}
|
||||
|
||||
# Fix for running Go apps in container @https://stackoverflow.com/a/35613430
|
||||
# RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
|
||||
|
||||
COPY ./api-gateway/etc /etc/traefik
|
||||
COPY ./api-gateway/plugins /plugins-local
|
||||
COPY ./api-gateway/entrypoint.sh ./api-gateway/wait-for-it.sh /
|
||||
COPY ./api-gateway/entrypoint.sh ./api-gateway/wait-for-it.sh ./.app.config /
|
||||
|
||||
EXPOSE 443 8080
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["traefik"]
|
||||
|
||||
EXPOSE 443 8080
|
||||
|
@ -1,5 +1,8 @@
|
||||
#!/bin/sh
|
||||
set +e
|
||||
set +e
|
||||
|
||||
update-ca-certificates
|
||||
update-resolv
|
||||
|
||||
waitForService()
|
||||
{
|
||||
@ -33,5 +36,6 @@ else
|
||||
echo "= '$1' is not a Traefik command: assuming shell execution." 1>&2
|
||||
fi
|
||||
|
||||
# echo "Executing: $@"
|
||||
register-service
|
||||
|
||||
exec "$@"
|
||||
|
@ -1,9 +1,11 @@
|
||||
tls:
|
||||
certificates:
|
||||
certFile: /etc/traefik/certs/gateway.pem
|
||||
keyFile: /etc/traefik/certs/gateway.key
|
||||
- certFile: "/etc/traefik/certs/gw.internal.crt"
|
||||
keyFile: "/etc/traefik/certs/gw.key"
|
||||
- certFile: "/etc/traefik/certs/gw.local.crt"
|
||||
keyFile: "/etc/traefik/certs/gw.key"
|
||||
stores:
|
||||
default:
|
||||
defaultCertificate:
|
||||
certFile: /etc/traefik/certs/gateway.pem
|
||||
keyFile: /etc/traefik/certs/gateway.key
|
||||
certFile: "/etc/traefik/certs/gw.internal.crt"
|
||||
keyFile: "/etc/traefik/certs/gw.key"
|
||||
|
@ -1,6 +1,6 @@
|
||||
################################################################
|
||||
global:
|
||||
checkNewVersion: false
|
||||
checkNewVersion: true
|
||||
sendAnonymousUsage: false
|
||||
|
||||
################################################################
|
||||
@ -20,25 +20,28 @@ entryPoints:
|
||||
metrics:
|
||||
address: :8084
|
||||
|
||||
certificatesResolvers:
|
||||
tls:
|
||||
acme:
|
||||
email: keedosn+egommerce@gmail.com
|
||||
storage: acme.json
|
||||
httpChallenge:
|
||||
# used during the challenge
|
||||
entryPoint: https
|
||||
# certificatesResolvers:
|
||||
# tls:
|
||||
# acme:
|
||||
# email: keedosn+egommerce@gmail.com
|
||||
# storage: acme.json
|
||||
# httpChallenge:
|
||||
# # used during the challenge
|
||||
# entryPoint: https
|
||||
|
||||
################################################################
|
||||
# serversTransport:
|
||||
# insecureSkipVerify: true
|
||||
# rootCAs:
|
||||
# - /etc/traefik/certs/client.cert
|
||||
serversTransport:
|
||||
insecureSkipVerify: true # dev only...
|
||||
rootCAs:
|
||||
- /etc/traefik/certs/gw.internal.crt
|
||||
|
||||
################################################################
|
||||
ping:
|
||||
entryPoint: "traefik"
|
||||
|
||||
api:
|
||||
insecure: true
|
||||
# dashboard: true
|
||||
insecure: true # dev only
|
||||
dashboard: true # dev only
|
||||
|
||||
################################################################
|
||||
providers:
|
||||
@ -46,22 +49,24 @@ providers:
|
||||
filename: /etc/traefik/tls.yml
|
||||
docker:
|
||||
exposedByDefault: false
|
||||
# refreshInterval: 1s
|
||||
# Default host rule.
|
||||
# Optional
|
||||
# Default: "Host(`{{ normalize .Name }}`)"
|
||||
# defaultRule: Host(`{{ normalize .Name }}.docker.localhost`)
|
||||
################################################################
|
||||
consulCatalog:
|
||||
refreshInterval: 1s
|
||||
exposedByDefault: false
|
||||
refreshInterval: 5s
|
||||
# ^^ configure in stack`s yml api-registry `command:` section: --providers.consulcatalog.refreshInterval=10s
|
||||
# connectAware: true
|
||||
# connectByDefault: true
|
||||
# serviceName: traefik
|
||||
endpoint:
|
||||
address: api-registry:8500
|
||||
# ^^ FIXME: Use ENV var
|
||||
scheme: http
|
||||
|
||||
################################################################
|
||||
# log:
|
||||
# level: DEBUG
|
||||
log:
|
||||
level: DEBUG
|
||||
|
||||
################################################################
|
||||
accessLog: {}
|
||||
@ -78,6 +83,8 @@ metrics:
|
||||
experimental:
|
||||
localPlugins:
|
||||
requestid:
|
||||
moduleName: "git.pbiernat.dev/traefik/plugin-requestid"
|
||||
moduleName: "git.pbiernat.io/traefik/plugin-requestid"
|
||||
auth:
|
||||
moduleName: "git.pbiernat.dev/traefik/plugin-egommerce-auth"
|
||||
moduleName: "git.pbiernat.io/traefik/plugin-egommerce-auth"
|
||||
retryif:
|
||||
moduleName: "github.com/fusionmedialimited/retryif"
|
||||
|
@ -1,3 +0,0 @@
|
||||
module git.pbiernat.dev/traefik/plugin-requestid
|
||||
|
||||
go 1.18
|
@ -1,6 +1,6 @@
|
||||
displayName: Add X-Request-ID Header
|
||||
type: middleware
|
||||
import: git.pbiernat.dev/traefik/plugin-requestid
|
||||
import: git.pbiernat.io/traefik/plugin-requestid
|
||||
summary: 'Add a X-Request-ID header for tracing'
|
||||
|
||||
testData: {}
|
@ -0,0 +1,3 @@
|
||||
module git.pbiernat.io/traefik/plugin-requestid
|
||||
|
||||
go 1.18
|
@ -0,0 +1,11 @@
|
||||
displayName: Traefik Retry If
|
||||
type: middleware
|
||||
|
||||
import: github.com/fusionmedialimited/retryif
|
||||
|
||||
summary: Retry requests based on status codes
|
||||
|
||||
testData:
|
||||
attempts: 3
|
||||
statusCodes:
|
||||
- 503
|
@ -0,0 +1,20 @@
|
||||
.PHONY: lint test vendor clean
|
||||
|
||||
export GO111MODULE=on
|
||||
|
||||
default: lint test
|
||||
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
test:
|
||||
go test -v -cover ./...
|
||||
|
||||
yaegi_test:
|
||||
yaegi test -v .
|
||||
|
||||
vendor:
|
||||
go mod vendor
|
||||
|
||||
clean:
|
||||
rm -rf ./vendor
|
@ -0,0 +1,3 @@
|
||||
module github.com/fusionmedialimited/retryif
|
||||
|
||||
go 1.18
|
@ -0,0 +1,179 @@
|
||||
package retryif
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const typeName = "RetryIf"
|
||||
|
||||
// retry is a middleware that retries requests.
|
||||
type retry struct {
|
||||
config *RetryConfig
|
||||
next http.Handler
|
||||
name string
|
||||
}
|
||||
|
||||
type RetryConfig struct {
|
||||
// Attempts defines how many times the request should be retried.
|
||||
Attempts int `json:"attempts,omitempty" toml:"attempts,omitempty" yaml:"attempts,omitempty" export:"true"`
|
||||
|
||||
// StatusCodes defines the status codes which will trigger a retry if received
|
||||
StatusCodes []int `json:"statusCodes,omitempty" toml:"statusCodes,omitempty" yaml:"statusCodes,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
func CreateConfig() *RetryConfig {
|
||||
return &RetryConfig{
|
||||
Attempts: 2,
|
||||
StatusCodes: []int{503},
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a new retry middleware.
|
||||
func New(ctx context.Context, next http.Handler, config *RetryConfig, name string) (http.Handler, error) {
|
||||
if config.Attempts <= 0 {
|
||||
return nil, fmt.Errorf("incorrect (or empty) value for attempt (%d)", config.Attempts)
|
||||
}
|
||||
|
||||
return &retry{
|
||||
config: config,
|
||||
next: next,
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *retry) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
if r.config.Attempts == 1 {
|
||||
r.next.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
closableBody := req.Body
|
||||
if closableBody != nil {
|
||||
defer closableBody.Close()
|
||||
}
|
||||
|
||||
// if we might make multiple attempts, swap the body for an io.NopCloser
|
||||
// cf https://github.com/traefik/traefik/issues/1008
|
||||
req.Body = io.NopCloser(closableBody)
|
||||
|
||||
attempts := 1
|
||||
|
||||
operation := func() error {
|
||||
shouldRetry := attempts < r.config.Attempts
|
||||
retryResponseWriter := newResponseWriter(rw, shouldRetry, r.config.StatusCodes, attempts-1)
|
||||
|
||||
r.next.ServeHTTP(retryResponseWriter, req)
|
||||
|
||||
if !retryResponseWriter.ShouldRetry() {
|
||||
return nil
|
||||
}
|
||||
|
||||
attempts++
|
||||
return fmt.Errorf("attempt %d failed", attempts-1)
|
||||
}
|
||||
|
||||
var err error
|
||||
for true {
|
||||
err = operation()
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newResponseWriter(rw http.ResponseWriter, shouldRetry bool, codes []int, attempt int) *responseWriter {
|
||||
return &responseWriter{
|
||||
responseWriter: rw,
|
||||
headers: make(http.Header),
|
||||
shouldRetry: shouldRetry,
|
||||
statusCodes: codes,
|
||||
attempt: attempt,
|
||||
}
|
||||
}
|
||||
|
||||
type responseWriter struct {
|
||||
attempt int
|
||||
statusCodes []int
|
||||
responseWriter http.ResponseWriter
|
||||
headers http.Header
|
||||
shouldRetry bool
|
||||
written bool
|
||||
}
|
||||
|
||||
func (r *responseWriter) ShouldRetry() bool {
|
||||
return r.shouldRetry
|
||||
}
|
||||
|
||||
func (r *responseWriter) DisableRetries() {
|
||||
r.shouldRetry = false
|
||||
}
|
||||
|
||||
func (r *responseWriter) Header() http.Header {
|
||||
if r.written {
|
||||
return r.responseWriter.Header()
|
||||
}
|
||||
return r.headers
|
||||
}
|
||||
|
||||
func (r *responseWriter) Write(buf []byte) (int, error) {
|
||||
if r.ShouldRetry() {
|
||||
return len(buf), nil
|
||||
}
|
||||
return r.responseWriter.Write(buf)
|
||||
}
|
||||
|
||||
func (r *responseWriter) WriteHeader(code int) {
|
||||
if r.ShouldRetry() && !r.ShouldRetryOnCode(code) {
|
||||
r.DisableRetries()
|
||||
}
|
||||
|
||||
if r.ShouldRetry() {
|
||||
return
|
||||
}
|
||||
|
||||
// In that case retry case is set to false which means we at least managed
|
||||
// to write headers to the backend : we are not going to perform any further retry.
|
||||
// So it is now safe to alter current response headers with headers collected during
|
||||
// the latest try before writing headers to client.
|
||||
headers := r.responseWriter.Header()
|
||||
headers.Set("X-RetryIf-Retries", strconv.Itoa(r.attempt))
|
||||
for header, value := range r.headers {
|
||||
headers[header] = value
|
||||
}
|
||||
|
||||
r.responseWriter.WriteHeader(code)
|
||||
r.written = true
|
||||
}
|
||||
|
||||
func (r *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hijacker, ok := r.responseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("%T is not a http.Hijacker", r.responseWriter)
|
||||
}
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
|
||||
func (r *responseWriter) Flush() {
|
||||
if flusher, ok := r.responseWriter.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *responseWriter) ShouldRetryOnCode(stCode int) bool {
|
||||
for _, code := range r.statusCodes {
|
||||
if code == stCode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *responseWriter) CloseNotify() <-chan bool {
|
||||
return r.responseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# RUN IN REPO ROOT DIR !!
|
||||
|
||||
export IMAGE_NAME="git.pbiernat.dev/egommerce/api-gateway"
|
||||
export IMAGE_NAME="git.pbiernat.io/egommerce/api-gateway"
|
||||
export BUILD_TIME=$(date +"%Y%m%d%H%M%S")
|
||||
|
||||
TARGET=${1:-latest}
|
||||
|
@ -1,9 +1,11 @@
|
||||
#!/bin/sh
|
||||
# RUN IN REPO ROOT DIR !!
|
||||
|
||||
export IMAGE_NAME="git.pbiernat.dev/egommerce/api-gateway"
|
||||
export IMAGE_NAME="git.pbiernat.io/egommerce/api-gateway"
|
||||
|
||||
TARGET=${1:-latest}
|
||||
|
||||
echo $DOCKER_PASSWORD | docker login git.pbiernat.dev -u $DOCKER_USERNAME --password-stdin
|
||||
echo $DOCKER_PASSWORD | docker login git.pbiernat.io -u $DOCKER_USERNAME --password-stdin
|
||||
docker push "$IMAGE_NAME:$TARGET"
|
||||
|
||||
curl http://127.0.0.1:9001/api/webhooks/da6eb8e6-bf58-46ff-8546-1bf925cb7ea0
|
||||
|
Loading…
Reference in New Issue
Block a user