mirror of https://github.com/h44z/wg-portal.git
				
				
				
			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= | ||||
|  |  | |||
|  | @ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -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…
	
		Reference in New Issue