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
|
.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"]
|
[submodule "api-gateway/plugins/src/git.pbiernat.io/traefik/plugin-egommerce-auth"]
|
||||||
path = api-gateway/plugins/src/git.pbiernat.dev/traefik/plugin-egommerce-auth
|
path = api-gateway/plugins/src/git.pbiernat.io/traefik/plugin-egommerce-auth
|
||||||
url = git@git.pbiernat.dev:traefik/plugin-egommerce-auth.git
|
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
|
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.version="1.0"
|
||||||
LABEL dev.egommerce.image.build_time=${BUILD_TIME}
|
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/etc /etc/traefik
|
||||||
COPY ./api-gateway/plugins /plugins-local
|
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"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
CMD ["traefik"]
|
CMD ["traefik"]
|
||||||
|
|
||||||
EXPOSE 443 8080
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set +e
|
set +e
|
||||||
|
|
||||||
|
update-ca-certificates
|
||||||
|
update-resolv
|
||||||
|
|
||||||
waitForService()
|
waitForService()
|
||||||
{
|
{
|
||||||
@ -33,5 +36,6 @@ else
|
|||||||
echo "= '$1' is not a Traefik command: assuming shell execution." 1>&2
|
echo "= '$1' is not a Traefik command: assuming shell execution." 1>&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# echo "Executing: $@"
|
register-service
|
||||||
|
|
||||||
exec "$@"
|
exec "$@"
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
tls:
|
tls:
|
||||||
certificates:
|
certificates:
|
||||||
certFile: /etc/traefik/certs/gateway.pem
|
- certFile: "/etc/traefik/certs/gw.internal.crt"
|
||||||
keyFile: /etc/traefik/certs/gateway.key
|
keyFile: "/etc/traefik/certs/gw.key"
|
||||||
|
- certFile: "/etc/traefik/certs/gw.local.crt"
|
||||||
|
keyFile: "/etc/traefik/certs/gw.key"
|
||||||
stores:
|
stores:
|
||||||
default:
|
default:
|
||||||
defaultCertificate:
|
defaultCertificate:
|
||||||
certFile: /etc/traefik/certs/gateway.pem
|
certFile: "/etc/traefik/certs/gw.internal.crt"
|
||||||
keyFile: /etc/traefik/certs/gateway.key
|
keyFile: "/etc/traefik/certs/gw.key"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
################################################################
|
################################################################
|
||||||
global:
|
global:
|
||||||
checkNewVersion: false
|
checkNewVersion: true
|
||||||
sendAnonymousUsage: false
|
sendAnonymousUsage: false
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
@ -20,25 +20,28 @@ entryPoints:
|
|||||||
metrics:
|
metrics:
|
||||||
address: :8084
|
address: :8084
|
||||||
|
|
||||||
certificatesResolvers:
|
# certificatesResolvers:
|
||||||
tls:
|
# tls:
|
||||||
acme:
|
# acme:
|
||||||
email: keedosn+egommerce@gmail.com
|
# email: keedosn+egommerce@gmail.com
|
||||||
storage: acme.json
|
# storage: acme.json
|
||||||
httpChallenge:
|
# httpChallenge:
|
||||||
# used during the challenge
|
# # used during the challenge
|
||||||
entryPoint: https
|
# entryPoint: https
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# serversTransport:
|
serversTransport:
|
||||||
# insecureSkipVerify: true
|
insecureSkipVerify: true # dev only...
|
||||||
# rootCAs:
|
rootCAs:
|
||||||
# - /etc/traefik/certs/client.cert
|
- /etc/traefik/certs/gw.internal.crt
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
|
ping:
|
||||||
|
entryPoint: "traefik"
|
||||||
|
|
||||||
api:
|
api:
|
||||||
insecure: true
|
insecure: true # dev only
|
||||||
# dashboard: true
|
dashboard: true # dev only
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
providers:
|
providers:
|
||||||
@ -46,22 +49,24 @@ providers:
|
|||||||
filename: /etc/traefik/tls.yml
|
filename: /etc/traefik/tls.yml
|
||||||
docker:
|
docker:
|
||||||
exposedByDefault: false
|
exposedByDefault: false
|
||||||
|
# refreshInterval: 1s
|
||||||
# Default host rule.
|
# Default host rule.
|
||||||
# Optional
|
# Optional
|
||||||
# Default: "Host(`{{ normalize .Name }}`)"
|
# Default: "Host(`{{ normalize .Name }}`)"
|
||||||
# defaultRule: Host(`{{ normalize .Name }}.docker.localhost`)
|
# defaultRule: Host(`{{ normalize .Name }}.docker.localhost`)
|
||||||
################################################################
|
################################################################
|
||||||
consulCatalog:
|
consulCatalog:
|
||||||
|
refreshInterval: 1s
|
||||||
exposedByDefault: false
|
exposedByDefault: false
|
||||||
refreshInterval: 5s
|
# connectAware: true
|
||||||
# ^^ configure in stack`s yml api-registry `command:` section: --providers.consulcatalog.refreshInterval=10s
|
# connectByDefault: true
|
||||||
|
# serviceName: traefik
|
||||||
endpoint:
|
endpoint:
|
||||||
address: api-registry:8500
|
scheme: http
|
||||||
# ^^ FIXME: Use ENV var
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# log:
|
log:
|
||||||
# level: DEBUG
|
level: DEBUG
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
accessLog: {}
|
accessLog: {}
|
||||||
@ -78,6 +83,8 @@ metrics:
|
|||||||
experimental:
|
experimental:
|
||||||
localPlugins:
|
localPlugins:
|
||||||
requestid:
|
requestid:
|
||||||
moduleName: "git.pbiernat.dev/traefik/plugin-requestid"
|
moduleName: "git.pbiernat.io/traefik/plugin-requestid"
|
||||||
auth:
|
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
|
displayName: Add X-Request-ID Header
|
||||||
type: middleware
|
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'
|
summary: 'Add a X-Request-ID header for tracing'
|
||||||
|
|
||||||
testData: {}
|
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
|
#!/bin/sh
|
||||||
# RUN IN REPO ROOT DIR !!
|
# 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")
|
export BUILD_TIME=$(date +"%Y%m%d%H%M%S")
|
||||||
|
|
||||||
TARGET=${1:-latest}
|
TARGET=${1:-latest}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# RUN IN REPO ROOT DIR !!
|
# 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}
|
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"
|
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