mirror of
https://github.com/h44z/wg-portal.git
synced 2025-04-19 00:45:17 +00:00
replace inaccessible external lib
This commit is contained in:
parent
b3a5f2ac60
commit
dd86d0ff49
@ -9,7 +9,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.prolicht.digital/golib/healthcheck"
|
||||
"github.com/h44z/wg-portal/internal/common/healthcheck"
|
||||
"github.com/h44z/wg-portal/internal/server"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
1
go.mod
1
go.mod
@ -3,7 +3,6 @@ module github.com/h44z/wg-portal
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
git.prolicht.digital/golib/healthcheck v1.1.1
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible
|
||||
github.com/gin-contrib/sessions v0.0.5
|
||||
github.com/gin-gonic/gin v1.8.2
|
||||
|
2
go.sum
2
go.sum
@ -1,5 +1,3 @@
|
||||
git.prolicht.digital/golib/healthcheck v1.1.1 h1:bdx0MuGqAq0PCooPpiuPXsr4/Ok+yfJwq8P9ITq2eLI=
|
||||
git.prolicht.digital/golib/healthcheck v1.1.1/go.mod h1:wEqVrqHJ8NsSx5qlFGUlw74wJ/wDSKaA34QoyvsEkdc=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
|
116
internal/common/healthcheck/healthcheck.go
Normal file
116
internal/common/healthcheck/healthcheck.go
Normal file
@ -0,0 +1,116 @@
|
||||
// source taken from https://git.prolicht.digital/golib/healthcheck/-/blob/master/healthcheck.go
|
||||
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
listenAddress string
|
||||
checkFunc func() int
|
||||
}
|
||||
|
||||
type Option func(svc *service)
|
||||
|
||||
// New creates a new healthcheck instance that can be started with either Start() or StartWithContext().
|
||||
func New(opts ...Option) *service {
|
||||
svc := &service{
|
||||
listenAddress: ":11223",
|
||||
checkFunc: func() int {
|
||||
return http.StatusOK
|
||||
},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(svc)
|
||||
}
|
||||
return svc
|
||||
}
|
||||
|
||||
// Start starts a background goroutine with the healthcheck webserver. This goroutine is only stopped
|
||||
// if the whole program is shut down.
|
||||
func (s *service) Start() {
|
||||
s.StartWithContext(context.Background())
|
||||
}
|
||||
|
||||
// StartForeground starts a goroutine with the healthcheck webserver. This function will block until the context
|
||||
// gets canceled or the healthcheck server crashes.
|
||||
func (s *service) StartForeground(ctx context.Context) {
|
||||
router := http.NewServeMux()
|
||||
router.Handle("/health", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(s.checkFunc())
|
||||
}))
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: s.listenAddress,
|
||||
Handler: router,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
IdleTimeout: 15 * time.Second,
|
||||
}
|
||||
|
||||
srvContext, cancelFn := context.WithCancel(ctx)
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
fmt.Printf("[HEALTHCHECK] web service on %s exited: %v\n", s.listenAddress, err)
|
||||
cancelFn()
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for the main context to end, this call blocks
|
||||
<-srvContext.Done()
|
||||
|
||||
// 1-second grace period
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
srv.SetKeepAlivesEnabled(false) // disable keep-alive kills idle connections
|
||||
_ = srv.Shutdown(shutdownCtx)
|
||||
|
||||
fmt.Println("[HEALTHCHECK] web service stopped")
|
||||
}
|
||||
|
||||
// StartWithContext starts a background goroutine with the healthcheck webserver. The goroutine will be
|
||||
// stopped if the context gets canceled or the healthcheck server crashes.
|
||||
func (s *service) StartWithContext(ctx context.Context) {
|
||||
go s.StartForeground(ctx)
|
||||
}
|
||||
|
||||
// ListenOn allows to change the default listening address of ":11223".
|
||||
func ListenOn(addr string) Option {
|
||||
return func(svc *service) {
|
||||
svc.listenAddress = addr
|
||||
}
|
||||
}
|
||||
|
||||
// WithCustomCheck allows to use a custom check function. The integer return value of the check
|
||||
// function is used as HTTP status code.
|
||||
func WithCustomCheck(fnc func() int) Option {
|
||||
return func(svc *service) {
|
||||
if fnc != nil {
|
||||
svc.checkFunc = fnc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListenOnFromEnv sets the listening address to a value retrieved from the environment variable
|
||||
// HC_LISTEN_ADDR.
|
||||
// If the argument list is not empty, the listening address value will be loaded from an
|
||||
// environment variable with the name of the first list entry.
|
||||
// If the environment variable was empty, the listening address will not be overridden.
|
||||
func ListenOnFromEnv(envName ...string) Option {
|
||||
return func(svc *service) {
|
||||
varName := "HC_LISTEN_ADDR"
|
||||
if len(envName) > 0 {
|
||||
varName = envName[0]
|
||||
}
|
||||
|
||||
listenAddr := os.Getenv(varName)
|
||||
if listenAddr != "" {
|
||||
svc.listenAddress = listenAddr
|
||||
}
|
||||
}
|
||||
}
|
243
internal/common/healthcheck/healthcheck_test.go
Normal file
243
internal/common/healthcheck/healthcheck_test.go
Normal file
@ -0,0 +1,243 @@
|
||||
// source taken from https://git.prolicht.digital/golib/healthcheck/-/blob/master/healthcheck_test.go
|
||||
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
type args struct {
|
||||
opts []Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *service
|
||||
}{
|
||||
{
|
||||
name: "Test Plain",
|
||||
args: args{},
|
||||
want: &service{
|
||||
listenAddress: ":11223",
|
||||
checkFunc: nil, // we cannot compare the check function
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test With Empty Options",
|
||||
args: args{
|
||||
opts: []Option{},
|
||||
},
|
||||
want: &service{
|
||||
listenAddress: ":11223",
|
||||
checkFunc: nil, // we cannot compare the check function
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test With Options",
|
||||
args: args{
|
||||
opts: []Option{ListenOn(":123456")},
|
||||
},
|
||||
want: &service{
|
||||
listenAddress: ":123456",
|
||||
checkFunc: nil, // we cannot compare the check function
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := New(tt.args.opts...)
|
||||
got.checkFunc = nil
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("New() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListenOn(t *testing.T) {
|
||||
type args struct {
|
||||
addr string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *service
|
||||
}{
|
||||
{
|
||||
name: "Test Port Only",
|
||||
args: args{
|
||||
addr: ":8080",
|
||||
},
|
||||
want: &service{
|
||||
listenAddress: ":8080",
|
||||
checkFunc: nil, // cannot deeply compare check func,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test Addr:Port Only",
|
||||
args: args{
|
||||
addr: "localhost:8080",
|
||||
},
|
||||
want: &service{
|
||||
listenAddress: "localhost:8080",
|
||||
checkFunc: nil, // cannot deeply compare check func,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := New(ListenOn(tt.args.addr))
|
||||
got.checkFunc = nil
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ListenOn() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListenOnEnv(t *testing.T) {
|
||||
_ = os.Setenv("HC_LISTEN_ADDR", "")
|
||||
hc := New(ListenOnFromEnv())
|
||||
if hc.listenAddress != New().listenAddress {
|
||||
t.Errorf("ListenOnFromEnv() = %v, want %v", hc.listenAddress, New().listenAddress)
|
||||
}
|
||||
|
||||
want := ":1337"
|
||||
_ = os.Setenv("HC_LISTEN_ADDR", want)
|
||||
hc = New(ListenOnFromEnv())
|
||||
if hc.listenAddress != want {
|
||||
t.Errorf("ListenOnFromEnv() = %v, want %v", hc.listenAddress, want)
|
||||
}
|
||||
|
||||
hc = New() // check that the env var has no effect
|
||||
if hc.listenAddress != New().listenAddress {
|
||||
t.Errorf("ListenOnFromEnv() = %v, want %v", hc.listenAddress, New().listenAddress)
|
||||
}
|
||||
|
||||
want = ":1338"
|
||||
_ = os.Setenv("SOME_RANDOM_ENV_VAR", want)
|
||||
hc = New(ListenOnFromEnv("SOME_RANDOM_ENV_VAR"))
|
||||
if hc.listenAddress != want {
|
||||
t.Errorf("ListenOnFromEnv() = %v, want %v", hc.listenAddress, want)
|
||||
}
|
||||
|
||||
hc = New(ListenOnFromEnv("SOME_RANDOM_ENV_VAR", "ignored", "ignored 2"))
|
||||
if hc.listenAddress != want {
|
||||
t.Errorf("ListenOnFromEnv() = %v, want %v", hc.listenAddress, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithCustomCheck(t *testing.T) {
|
||||
customFnc := func() int { return 123 }
|
||||
|
||||
type args struct {
|
||||
fnc func() int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *service
|
||||
wantFnc func() int
|
||||
}{
|
||||
{
|
||||
name: "Test Custom Function",
|
||||
args: args{
|
||||
fnc: customFnc,
|
||||
},
|
||||
want: &service{
|
||||
listenAddress: New().listenAddress,
|
||||
checkFunc: nil, // cannot deeply compare check func,
|
||||
},
|
||||
wantFnc: customFnc,
|
||||
},
|
||||
{
|
||||
name: "Test Nil Function",
|
||||
args: args{
|
||||
fnc: nil,
|
||||
},
|
||||
want: &service{
|
||||
listenAddress: New().listenAddress,
|
||||
checkFunc: nil, // cannot deeply compare check func,
|
||||
},
|
||||
wantFnc: New().checkFunc,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := New(WithCustomCheck(tt.args.fnc))
|
||||
gotFnc := got.checkFunc
|
||||
got.checkFunc = nil
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("WithContext() = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
||||
if reflect.ValueOf(gotFnc).Pointer() != reflect.ValueOf(tt.wantFnc).Pointer() {
|
||||
t.Error("WithContext() function mismatch")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_service_StartForeground(t *testing.T) {
|
||||
runTime := 550 * time.Millisecond
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
hc := New()
|
||||
start := time.Now()
|
||||
hc.StartForeground(ctx)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
// check if execution time is within +-10% of the runTime
|
||||
if elapsed > (runTime+(runTime/10)) || elapsed < (runTime-(runTime/10)) {
|
||||
t.Errorf("StartForeground() invalid execution time = %v, want %v", elapsed, runTime)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_service_HTTPResponse(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
hc := New()
|
||||
hc.StartWithContext(ctx)
|
||||
time.Sleep(200 * time.Millisecond) // ensure that web server is up and running
|
||||
|
||||
cl := http.Client{Timeout: time.Millisecond * 200}
|
||||
req, _ := http.NewRequest("GET", "http://localhost:11223/health", nil)
|
||||
resp, err := cl.Do(req)
|
||||
if err != nil {
|
||||
t.Errorf("http request failed: %v", err)
|
||||
return
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("http request with wrong response code: %v, want %v", err, http.StatusOK)
|
||||
}
|
||||
|
||||
<-ctx.Done() // wait for clean shutdown
|
||||
}
|
||||
|
||||
func Test_service_CustomCheckResponse(t *testing.T) {
|
||||
want := http.StatusExpectationFailed
|
||||
hc := New(WithCustomCheck(func() int {
|
||||
return want
|
||||
}))
|
||||
hc.Start()
|
||||
time.Sleep(200 * time.Millisecond) // ensure that web server is up and running
|
||||
|
||||
cl := http.Client{Timeout: time.Millisecond * 200}
|
||||
req, _ := http.NewRequest("GET", "http://localhost:11223/health", nil)
|
||||
resp, err := cl.Do(req)
|
||||
if err != nil {
|
||||
t.Errorf("http request failed: %v", err)
|
||||
return
|
||||
}
|
||||
if resp.StatusCode != want {
|
||||
t.Errorf("http request with wrong response code: %v, want %v", err, want)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user