Repository: jkroepke/openvpn-auth-oauth2
Pull request #215 (2024-03-17 20:43:26)

increase test coverage
#### What this PR does / why we need it

#### Which issue this PR fixes

*(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

- fixes #

#### Special notes for your reviewer

#### Particularly user-facing changes

#### Checklist

Complete these before marking the PR as `ready to review`:

<!-- [Place an '[x]' (no spaces) in all applicable fields.] -->

- [ ] [DCO](https://github.com/prometheus-community/helm-charts/blob/main/CONTRIBUTING.md#sign-off-your-work) signed
- [ ] The PR title has a summary of the changes
- [ ] The PR body has a summary to reflect any significant (and particularly user-facing) changes introduced by this PR


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/pull/215?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke) Report
Attention: Patch coverage is `78.57143%` with `3 lines` in your changes are missing coverage. Please review.
> Project coverage is 84.80%. Comparing base [(`78b1c98`)](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/commit/78b1c985509c6b3becc9875d77803a9a23b69d0b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke) to head [(`8735968`)](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/pull/215?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke).

| [Files](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/pull/215?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke) | Patch % | Lines |
|---|---|---|
| [internal/httpserver/main.go](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/pull/215?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke#diff-aW50ZXJuYWwvaHR0cHNlcnZlci9tYWluLmdv) | 75.00% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/pull/215?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke) |
| [cmd/daemon/root.go](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/pull/215?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke#diff-Y21kL2RhZW1vbi9yb290Lmdv) | 75.00% | [1 Missing :warning: ](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/pull/215?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #215      +/-   ##
==========================================
+ Coverage   84.07%   84.80%   +0.72%     
==========================================
  Files          45       45              
  Lines        2097     2099       +2     
==========================================
+ Hits         1763     1780      +17     
+ Misses        221      206      -15     
  Partials      113      113              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/jkroepke/openvpn-auth-oauth2/pull/215?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jan-Otto+Kr%C3%B6pke).


Patch:
diff --git a/.golangci.yaml b/.golangci.yaml
index ded45f2..c36515c 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -14,12 +14,9 @@ linters:
     - depguard
     - exhaustruct
     - gomnd
-    - dupl
-    - forbidigo
     - funlen
     - ireturn
     - goerr113
-    - gomoddirectives
 
 issues:
   exclude-rules:
@@ -33,6 +30,8 @@ issues:
         - dogsled
         - contextcheck
         - dupword
+        - dupl
+        - funlen
 
 linters-settings:
   varnamelen:
diff --git a/cmd/daemon/root.go b/cmd/daemon/root.go
index 1e633ec..a147d07 100644
--- a/cmd/daemon/root.go
+++ b/cmd/daemon/root.go
@@ -66,13 +66,13 @@ func Execute(args []string, logWriter io.Writer, version, commit, date string) i
 	}
 
 	ctx, cancel := context.WithCancelCause(context.Background())
-
 	httpClient := &http.Client{Transport: utils.NewUserAgentTransport(nil)}
+
 	storageClient := storage.New(conf.OAuth2.Refresh.Secret.String(), conf.OAuth2.Refresh.Expires)
 	oauth2Client := oauth2.New(logger, conf, storageClient, httpClient)
-	openvpnClient := openvpn.NewClient(ctx, logger, conf, oauth2Client)
+	openvpnClient := openvpn.New(ctx, logger, conf, oauth2Client)
 
-	if err = oauth2Client.Initialize(openvpnClient); err != nil {
+	if err = oauth2Client.Initialize(ctx, openvpnClient); err != nil {
 		logger.Error(err.Error())
 
 		return 1
@@ -84,12 +84,10 @@ func Execute(args []string, logWriter io.Writer, version, commit, date string) i
 		go setupDebugListener(logger, conf, done)
 	}
 
-	server := httpserver.NewHTTPServer(logger, conf, oauth2Client.Handler())
+	server := httpserver.NewHTTPServer(ctx, logger, conf, oauth2Client.Handler())
 
 	go func() {
 		if err := server.Listen(); err != nil {
-			cancel(nil)
-
 			logger.Error(fmt.Errorf("error http listener: %w", err).Error())
 			done <- 1
 
@@ -119,6 +117,8 @@ loop:
 	for {
 		select {
 		case returnCode = <-done:
+			cancel(nil)
+
 			break loop
 		case sig := <-termCh:
 			logger.Info("receiving signal: " + sig.String())
diff --git a/internal/config/config.go b/internal/config/flags.go
similarity index 82%
rename from internal/config/config.go
rename to internal/config/flags.go
index 66049ca..33bbc5b 100644
--- a/internal/config/config.go
+++ b/internal/config/flags.go
@@ -1,12 +1,9 @@
 package config
 
 import (
-	"errors"
 	"flag"
 	"fmt"
 	"log/slog"
-	"net/url"
-	"slices"
 	"strings"
 )
 
@@ -16,8 +13,6 @@ const (
 )
 
 // FlagSet configure the command line parser using the [flag] library.
-//
-//nolint:maintidx
 func FlagSet(name string) *flag.FlagSet {
 	flagSet := flag.NewFlagSet(name, flag.ContinueOnError)
 	flagSet.Usage = func() {
@@ -34,6 +29,34 @@ func FlagSet(name string) *flag.FlagSet {
 		"",
 		"path to one .yaml config file",
 	)
+	flagSet.Bool(
+		"version",
+		false,
+		"show version",
+	)
+
+	flagSetDebug(flagSet)
+	flagSetLog(flagSet)
+	flagSetHTTP(flagSet)
+	flagSetOpenVPN(flagSet)
+	flagSetOAuth2(flagSet)
+
+	flagSet.VisitAll(func(flag *flag.Flag) {
+		if flag.Name == "version" {
+			return
+		}
+
+		env := strings.ToUpper(flag.Name)
+		env = strings.ReplaceAll(env, ".", "_")
+		env = strings.ReplaceAll(env, "-", "__")
+
+		flag.Usage += fmt.Sprintf(" (env: %s%s)", envPrefix, env)
+	})
+
+	return flagSet
+}
+
+func flagSetDebug(flagSet *flag.FlagSet) {
 	flagSet.Bool(
 		"debug.pprof",
 		Defaults.Debug.Pprof,
@@ -44,6 +67,9 @@ func FlagSet(name string) *flag.FlagSet {
 		Defaults.Debug.Listen,
 		"listen address for go profiling endpoint",
 	)
+}
+
+func flagSetLog(flagSet *flag.FlagSet) {
 	flagSet.Bool(
 		"log.vpn-client-ip",
 		Defaults.Log.VPNClientIP,
@@ -59,6 +85,9 @@ func FlagSet(name string) *flag.FlagSet {
 		Defaults.Log.Level,
 		"log level",
 	)
+}
+
+func flagSetHTTP(flagSet *flag.FlagSet) {
 	flagSet.String(
 		"http.listen",
 		Defaults.HTTP.Listen,
@@ -105,6 +134,9 @@ func FlagSet(name string) *flag.FlagSet {
 		Defaults.HTTP.EnableProxyHeaders,
 		"Use X-Forward-For http header for client ips",
 	)
+}
+
+func flagSetOpenVPN(flagSet *flag.FlagSet) {
 	flagSet.String(
 		"openvpn.addr",
 		Defaults.OpenVpn.Addr.String(),
@@ -170,6 +202,9 @@ func FlagSet(name string) *flag.FlagSet {
 		Defaults.OpenVpn.Passthrough.SocketMode,
 		"The unix file permission mode for the pass-through socket. Used only, if openvpn.pass-through.address starts with unix://",
 	)
+}
+
+func flagSetOAuth2(flagSet *flag.FlagSet) {
 	flagSet.String(
 		"oauth2.issuer",
 		Defaults.OAuth2.Issuer.String(),
@@ -299,94 +334,4 @@ func FlagSet(name string) *flag.FlagSet {
 		"oauth2 token scopes. Defaults depends on oauth2.provider. Comma separated list. "+
 			"Example: openid,profile,email",
 	)
-	flagSet.Bool(
-		"version",
-		false,
-		"show version",
-	)
-
-	flagSet.VisitAll(func(flag *flag.Flag) {
-		if flag.Name == "version" {
-			return
-		}
-
-		env := strings.ToUpper(flag.Name)
-		env = strings.ReplaceAll(env, ".", "_")
-		env = strings.ReplaceAll(env, "-", "__")
-
-		flag.Usage += fmt.Sprintf(" (env: %s%s)", envPrefix, env)
-	})
-
-	return flagSet
-}
-
-// Validate validates the config.
-func Validate(mode int, conf Config) error { //nolint:cyclop
-	for key, value := range map[string]string{
-		"oauth2.client.id": conf.OAuth2.Client.ID,
-	} {
-		if value == "" {
-			return fmt.Errorf("%s is %w", key, ErrRequired)
-		}
-	}
-
-	for key, value := range map[string]Secret{
-		"http.secret":          conf.HTTP.Secret,
-		"oauth2.client.secret": conf.OAuth2.Client.Secret,
-	} {
-		if value.String() == "" {
-			return fmt.Errorf("%s is %w", key, ErrRequired)
-		}
-	}
-
-	for key, value := range map[string]*url.URL{
-		"http.baseurl":  conf.HTTP.BaseURL,
-		"oauth2.issuer": conf.OAuth2.Issuer,
-	} {
-		if IsURLEmpty(value) {
-			return fmt.Errorf("%s is %w", key, ErrRequired)
-		}
-	}
-
-	if !slices.Contains([]int{16, 24, 32}, len(conf.HTTP.Secret)) {
-		return errors.New("http.secret requires a length of 16, 24 or 32")
-	}
-
-	for key, uri := range map[string]*url.URL{
-		"http.baseurl":              conf.HTTP.BaseURL,
-		"oauth2.issuer":             conf.OAuth2.Issuer,
-		"oauth2.endpoint.discovery": conf.OAuth2.Endpoints.Discovery,
-		"oauth2.endpoint.token":     conf.OAuth2.Endpoints.Token,
-		"oauth2.endpoint.auth":      conf.OAuth2.Endpoints.Auth,
-	} {
-		if IsURLEmpty(uri) {
-			continue
-		}
-
-		if !slices.Contains([]string{"http", "https"}, uri.Scheme) {
-			return fmt.Errorf("%s: invalid URL. only http:// or https:// scheme supported", key)
-		}
-	}
-
-	if conf.OAuth2.Refresh.Enabled {
-		if !slices.Contains([]int{16, 24, 32}, len(conf.OAuth2.Refresh.Secret)) {
-			return errors.New("oauth2.refresh.secret requires a length of 16, 24 or 32")
-		}
-	}
-
-	if mode == ManagementClient {
-		for key, value := range map[string]*url.URL{
-			"openvpn.addr": conf.OpenVpn.Addr,
-		} {
-			if IsURLEmpty(value) {
-				return fmt.Errorf("%s is %w", key, ErrRequired)
-			}
-		}
-
-		if !slices.Contains([]string{"tcp", "unix"}, conf.OpenVpn.Addr.Scheme) {
-			return errors.New("openvpn.addr: invalid URL. only tcp://addr or unix://addr scheme supported")
-		}
-	}
-
-	return nil
 }
diff --git a/internal/config/config_test.go b/internal/config/flags_test.go
similarity index 100%
rename from internal/config/config_test.go
rename to internal/config/flags_test.go
diff --git a/internal/config/validate.go b/internal/config/validate.go
new file mode 100644
index 0000000..9203e71
--- /dev/null
+++ b/internal/config/validate.go
@@ -0,0 +1,79 @@
+package config
+
+import (
+	"errors"
+	"fmt"
+	"net/url"
+	"slices"
+)
+
+// Validate validates the config.
+func Validate(mode int, conf Config) error { //nolint:cyclop
+	for key, value := range map[string]string{
+		"oauth2.client.id": conf.OAuth2.Client.ID,
+	} {
+		if value == "" {
+			return fmt.Errorf("%s is %w", key, ErrRequired)
+		}
+	}
+
+	for key, value := range map[string]Secret{
+		"http.secret":          conf.HTTP.Secret,
+		"oauth2.client.secret": conf.OAuth2.Client.Secret,
+	} {
+		if value.String() == "" {
+			return fmt.Errorf("%s is %w", key, ErrRequired)
+		}
+	}
+
+	for key, value := range map[string]*url.URL{
+		"http.baseurl":  conf.HTTP.BaseURL,
+		"oauth2.issuer": conf.OAuth2.Issuer,
+	} {
+		if IsURLEmpty(value) {
+			return fmt.Errorf("%s is %w", key, ErrRequired)
+		}
+	}
+
+	if !slices.Contains([]int{16, 24, 32}, len(conf.HTTP.Secret)) {
+		return errors.New("http.secret requires a length of 16, 24 or 32")
+	}
+
+	for key, uri := range map[string]*url.URL{
+		"http.baseurl":              conf.HTTP.BaseURL,
+		"oauth2.issuer":             conf.OAuth2.Issuer,
+		"oauth2.endpoint.discovery": conf.OAuth2.Endpoints.Discovery,
+		"oauth2.endpoint.token":     conf.OAuth2.Endpoints.Token,
+		"oauth2.endpoint.auth":      conf.OAuth2.Endpoints.Auth,
+	} {
+		if IsURLEmpty(uri) {
+			continue
+		}
+
+		if !slices.Contains([]string{"http", "https"}, uri.Scheme) {
+			return fmt.Errorf("%s: invalid URL. only http:// or https:// scheme supported", key)
+		}
+	}
+
+	if conf.OAuth2.Refresh.Enabled {
+		if !slices.Contains([]int{16, 24, 32}, len(conf.OAuth2.Refresh.Secret)) {
+			return errors.New("oauth2.refresh.secret requires a length of 16, 24 or 32")
+		}
+	}
+
+	if mode == ManagementClient {
+		for key, value := range map[string]*url.URL{
+			"openvpn.addr": conf.OpenVpn.Addr,
+		} {
+			if IsURLEmpty(value) {
+				return fmt.Errorf("%s is %w", key, ErrRequired)
+			}
+		}
+
+		if !slices.Contains([]string{"tcp", "unix"}, conf.OpenVpn.Addr.Scheme) {
+			return errors.New("openvpn.addr: invalid URL. only tcp://addr or unix://addr scheme supported")
+		}
+	}
+
+	return nil
+}
diff --git a/internal/httpserver/main.go b/internal/httpserver/main.go
index 331faaa..d467a37 100644
--- a/internal/httpserver/main.go
+++ b/internal/httpserver/main.go
@@ -6,6 +6,7 @@ import (
 	"errors"
 	"fmt"
 	"log/slog"
+	"net"
 	"net/http"
 	"sync"
 	"time"
@@ -22,7 +23,7 @@ type Server struct {
 	tlsCertificateMu sync.RWMutex
 }
 
-func NewHTTPServer(logger *slog.Logger, conf config.Config, fnHandler *http.ServeMux) *Server {
+func NewHTTPServer(ctx context.Context, logger *slog.Logger, conf config.Config, fnHandler *http.ServeMux) *Server {
 	return &Server{
 		conf:   conf,
 		logger: logger,
@@ -33,6 +34,9 @@ func NewHTTPServer(logger *slog.Logger, conf config.Config, fnHandler *http.Serv
 			WriteTimeout:      3 * time.Second,
 			ErrorLog:          slog.NewLogLogger(logger.Handler(), slog.LevelError),
 			Handler:           fnHandler,
+			BaseContext: func(_ net.Listener) context.Context {
+				return ctx
+			},
 		},
 		tlsCertificateMu: sync.RWMutex{},
 	}
@@ -66,8 +70,12 @@ func (s *Server) Listen() error {
 		err = s.server.ListenAndServe()
 	}
 
-	if err != nil && !errors.Is(err, http.ErrServerClosed) {
-		return fmt.Errorf("ListenAndServeTLS: %w", err)
+	if err != nil {
+		if !errors.Is(err, http.ErrServerClosed) {
+			return fmt.Errorf("error http listening: %w", err)
+		}
+
+		s.logger.Info("http server closed")
 	}
 
 	return nil
diff --git a/internal/oauth2/provider.go b/internal/oauth2/provider.go
index 8ef7c18..c757b2d 100644
--- a/internal/oauth2/provider.go
+++ b/internal/oauth2/provider.go
@@ -36,13 +36,11 @@ func New(logger *slog.Logger, conf config.Config, storageClient *storage.Storage
 }
 
 // Initialize initiate the discovery of OIDC provider.
-func (p *Provider) Initialize(openvpn OpenVPN) error {
+func (p *Provider) Initialize(ctx context.Context, openvpn OpenVPN) error {
 	var err error
 
 	p.openvpn = openvpn
 
-	ctx := context.Background()
-
 	p.OIDC, err = newOidcProvider(ctx, p.conf, p.httpClient)
 	if err != nil {
 		return err
diff --git a/internal/openvpn/main.go b/internal/openvpn/main.go
index 8be6eb1..d0dcdfd 100644
--- a/internal/openvpn/main.go
+++ b/internal/openvpn/main.go
@@ -21,7 +21,9 @@ import (
 	"github.com/jkroepke/openvpn-auth-oauth2/internal/utils"
 )
 
-func NewClient(ctx context.Context, logger *slog.Logger, conf config.Config, oauth2Client *oauth2.Provider) *Client {
+const minManagementInterfaceVersion = 5
+
+func New(ctx context.Context, logger *slog.Logger, conf config.Config, oauth2Client *oauth2.Provider) *Client {
 	client := &Client{
 		conf:   conf,
 		logger: logger,
@@ -137,7 +139,7 @@ func (c *Client) checkManagementInterfaceVersion() error {
 
 	// Management Interface Version 5 is required at minimum
 	// ref: https://github.com/OpenVPN/openvpn/commit/a261e173341f8e68505a6ab5a413d09b0797a459
-	if managementInterfaceVersion < 5 {
+	if managementInterfaceVersion < minManagementInterfaceVersion {
 		return errors.New("openvpn-auth-oauth2 requires OpenVPN management interface version 5 or higher")
 	}
 
diff --git a/internal/openvpn/passthrough.go b/internal/openvpn/passthrough.go
index 446a3bb..07f8436 100644
--- a/internal/openvpn/passthrough.go
+++ b/internal/openvpn/passthrough.go
@@ -15,6 +15,8 @@ import (
 	"github.com/jkroepke/openvpn-auth-oauth2/internal/utils"
 )
 
+const writeTimeout = 20 * time.Millisecond
+
 // handlePassthrough starts a listener for the passthrough interface. This allows the management interface to be
 // accessed from a different network interface or even from a different machine.
 // The passthrough interface is a simple text-based protocol that allows the client to send commands to the management
@@ -73,7 +75,7 @@ func (c *Client) handlePassthrough() {
 					continue
 				}
 
-				_ = conn.SetWriteDeadline(time.Now().Add(20 * time.Millisecond))
+				_ = conn.SetWriteDeadline(time.Now().Add(writeTimeout))
 
 				if _, err = conn.Write([]byte(message + "\r\n")); err != nil {
 					connMu.Unlock()
@@ -191,7 +193,7 @@ func (c *Client) handlePassthroughClientAuth(conn net.Conn, scanner *bufio.Scann
 		return nil
 	}
 
-	if err := conn.SetWriteDeadline(time.Now().Add(20 * time.Millisecond)); err != nil {
+	if err := conn.SetWriteDeadline(time.Now().Add(writeTimeout)); err != nil {
 		return fmt.Errorf("unable to set write deadline: %w", err)
 	}
 
@@ -209,7 +211,7 @@ func (c *Client) handlePassthroughClientAuth(conn net.Conn, scanner *bufio.Scann
 	}
 
 	if scanner.Text() != c.conf.OpenVpn.Passthrough.Password.String() {
-		_ = conn.SetWriteDeadline(time.Now().Add(20 * time.Millisecond))
+		_ = conn.SetWriteDeadline(time.Now().Add(writeTimeout))
 		_, _ = conn.Write([]byte("ERROR: bad password\r\n"))
 
 		return errors.New("pass-through: client provide invalid password")
diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go
index feb75f0..adfef49 100644
--- a/pkg/plugin/plugin.go
+++ b/pkg/plugin/plugin.go
@@ -8,6 +8,7 @@ package main
 import "C"
 
 import (
+	"context"
 	"fmt"
 	"log/slog"
 	http2 "net/http"
@@ -79,13 +80,13 @@ func openvpn_plugin_open_v3_go(v3structver C.int, args *C.struct_openvpn_plugin_
 	storageClient := storage.New(conf.OAuth2.Refresh.Secret.String(), conf.OAuth2.Refresh.Expires)
 	provider := oauth2.New(logger, conf, storageClient, http2.DefaultClient)
 
-	if err = provider.Initialize(handle); err != nil {
+	if err = provider.Initialize(context.Background(), handle); err != nil {
 		logger.Error(err.Error())
 
 		return C.OPENVPN_PLUGIN_FUNC_ERROR
 	}
 
-	handle.server = httpserver.NewHTTPServer(logger, conf, provider.Handler())
+	handle.server = httpserver.NewHTTPServer(context.Background(), logger, conf, provider.Handler())
 	go func() {
 		if err := handle.server.Listen(); err != nil {
 			logger.Error(fmt.Errorf("error http listener: %w", err).Error())


Test patch:
diff --git a/cmd/daemon/root_test.go b/cmd/daemon/root_test.go
index 6134686..e84c3a0 100644
--- a/cmd/daemon/root_test.go
+++ b/cmd/daemon/root_test.go
@@ -153,6 +153,8 @@ func TestExecuteConfigFileFound(t *testing.T) {
 		args := []string{
 			"openvpn-auth-oauth2",
 			"--config=../../config.example.yaml",
+			"--debug.pprof",
+			"--debug.listen=127.0.0.1:0",
 			"--http.secret=0123456789101112",
 			"--http.listen=127.0.0.1:0",
 			"--oauth2.issuer", resourceServer.URL,
diff --git a/internal/httpserver/main_test.go b/internal/httpserver/main_test.go
index dd92b16..959cc71 100644
--- a/internal/httpserver/main_test.go
+++ b/internal/httpserver/main_test.go
@@ -1,6 +1,7 @@
 package httpserver_test
 
 import (
+	"context"
 	"errors"
 	gohttp "net/http"
 	"net/url"
@@ -78,7 +79,7 @@ func TestNewHTTPServer(t *testing.T) {
 			mux := gohttp.NewServeMux()
 			mux.Handle("/", gohttp.NotFoundHandler())
 
-			svr := httpserver.NewHTTPServer(logger.Logger, tt.conf, mux)
+			svr := httpserver.NewHTTPServer(context.Background(), logger.Logger, tt.conf, mux)
 
 			errCh := make(chan error, 1)
 
diff --git a/internal/oauth2/provider_test.go b/internal/oauth2/provider_test.go
index 6015f61..cfd4012 100644
--- a/internal/oauth2/provider_test.go
+++ b/internal/oauth2/provider_test.go
@@ -145,13 +145,13 @@ func TestNewProvider(t *testing.T) {
 			tt.conf.OpenVpn.Addr = &url.URL{Scheme: managementInterface.Addr().Network(), Host: managementInterface.Addr().String()}
 
 			storageClient := storage.New(testutils.Secret, time.Hour)
-
 			provider := oauth2.New(logger.Logger, tt.conf, storageClient, http2.DefaultClient)
+			ctx := context.Background()
 
-			client := openvpn.NewClient(context.Background(), logger.Logger, tt.conf, provider)
+			client := openvpn.New(ctx, logger.Logger, tt.conf, provider)
 			defer client.Shutdown()
 
-			err = provider.Initialize(client)
+			err = provider.Initialize(ctx, client)
 			if tt.err != "" {
 				require.Error(t, err)
 				assert.Equal(t, tt.err, strings.TrimSpace(err.Error()))
diff --git a/internal/openvpn/bench_test.go b/internal/openvpn/bench_test.go
index c86a262..f37a1b1 100644
--- a/internal/openvpn/bench_test.go
+++ b/internal/openvpn/bench_test.go
@@ -46,7 +46,7 @@ func BenchmarkOpenVPNHandler(b *testing.B) {
 	}
 
 	storageClient := storage.New(testutils.Secret, time.Hour)
-	openVPNClient := openvpn.NewClient(context.Background(), logger.Logger, conf, oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient))
+	openVPNClient := openvpn.New(context.Background(), logger.Logger, conf, oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient))
 
 	wg := sync.WaitGroup{}
 	wg.Add(1)
@@ -130,7 +130,7 @@ func BenchmarkOpenVPNPassthrough(b *testing.B) {
 
 	storageClient := storage.New(testutils.Secret, time.Hour)
 	provider := oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient)
-	openVPNClient := openvpn.NewClient(context.Background(), logger.Logger, conf, provider)
+	openVPNClient := openvpn.New(context.Background(), logger.Logger, conf, provider)
 
 	defer openVPNClient.Shutdown()
 
diff --git a/internal/openvpn/main_test.go b/internal/openvpn/main_test.go
index 0a783f2..1f2f080 100644
--- a/internal/openvpn/main_test.go
+++ b/internal/openvpn/main_test.go
@@ -41,7 +41,7 @@ func TestClientInvalidServer(t *testing.T) {
 	}
 	storageClient := storage.New(testutils.Secret, time.Hour)
 	provider := oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient)
-	client := openvpn.NewClient(context.Background(), logger.Logger, conf, provider)
+	client := openvpn.New(context.Background(), logger.Logger, conf, provider)
 	err := client.Connect()
 	require.Error(t, err)
 	assert.Equal(t, "unable to connect to openvpn management interface tcp://0.0.0.0:1: dial tcp 0.0.0.0:1: connect: connection refused", err.Error())
@@ -258,7 +258,7 @@ func TestClientFull(t *testing.T) {
 
 			storageClient := storage.New(testutils.Secret, time.Hour)
 			provider := oauth2.New(logger.Logger, tt.conf, storageClient, http2.DefaultClient)
-			client := openvpn.NewClient(context.Background(), logger.Logger, tt.conf, provider)
+			client := openvpn.New(context.Background(), logger.Logger, tt.conf, provider)
 
 			wg := sync.WaitGroup{}
 			wg.Add(1)
@@ -357,7 +357,7 @@ func TestClientInvalidPassword(t *testing.T) {
 
 	storageClient := storage.New(testutils.Secret, time.Hour)
 	provider := oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient)
-	client := openvpn.NewClient(context.Background(), logger.Logger, conf, provider)
+	client := openvpn.New(context.Background(), logger.Logger, conf, provider)
 
 	go func() {
 		conn, err := managementInterface.Accept()
@@ -431,7 +431,7 @@ func TestClientInvalidVersion(t *testing.T) {
 
 			storageClient := storage.New(testutils.Secret, time.Hour)
 			provider := oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient)
-			openVPNClient := openvpn.NewClient(context.Background(), logger.Logger, conf, provider)
+			openVPNClient := openvpn.New(context.Background(), logger.Logger, conf, provider)
 
 			ctx, cancel := context.WithCancelCause(context.Background())
 			wg := sync.WaitGroup{}
@@ -510,7 +510,7 @@ func TestSIGHUP(t *testing.T) {
 
 	storageClient := storage.New(testutils.Secret, time.Hour)
 	provider := oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient)
-	client := openvpn.NewClient(context.Background(), logger.Logger, conf, provider)
+	client := openvpn.New(context.Background(), logger.Logger, conf, provider)
 
 	wg := sync.WaitGroup{}
 	wg.Add(1)
@@ -582,7 +582,7 @@ func TestDeadLocks(t *testing.T) {
 
 			storageClient := storage.New(testutils.Secret, time.Hour)
 			provider := oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient)
-			client := openvpn.NewClient(context.Background(), logger.Logger, conf, provider)
+			client := openvpn.New(context.Background(), logger.Logger, conf, provider)
 
 			wg := sync.WaitGroup{}
 			wg.Add(1)
@@ -653,7 +653,7 @@ func TestInvalidCommandResponses(t *testing.T) {
 
 			storageClient := storage.New(testutils.Secret, time.Hour)
 			provider := oauth2.New(logger.Logger, conf, storageClient, http2.DefaultClient)
-			client := openvpn.NewClient(context.Background(), logger.Logger, conf, provider)
+			client := openvpn.New(context.Background(), logger.Logger, conf, provider)
 
 			wg := sync.WaitGroup{}
 			wg.Add(1)
diff --git a/internal/openvpn/passthrough_test.go b/internal/openvpn/passthrough_test.go
index 53d53c3..c8675c1 100644
--- a/internal/openvpn/passthrough_test.go
+++ b/internal/openvpn/passthrough_test.go
@@ -171,7 +171,7 @@ func TestPassthroughFull(t *testing.T) {
 
 			storageClient := storage.New(testutils.Secret, time.Hour)
 			provider := oauth2.New(logger.Logger, tt.conf, storageClient, http2.DefaultClient)
-			openVPNClient := openvpn.NewClient(context.Background(), logger.Logger, tt.conf, provider)
+			openVPNClient := openvpn.New(context.Background(), logger.Logger, tt.conf, provider)
 
 			defer openVPNClient.Shutdown()
 
diff --git a/pkg/testutils/main.go b/pkg/testutils/main.go
index 9acb2d3..70d5617 100644
--- a/pkg/testutils/main.go
+++ b/pkg/testutils/main.go
@@ -284,9 +284,9 @@ func SetupMockEnvironment(tb testing.TB, conf config.Config) (config.Config, *op
 	httpClient := &http.Client{Transport: NewMockRoundTripper(utils.NewUserAgentTransport(nil))}
 	storageClient := storage.New(Secret, conf.OAuth2.Refresh.Expires)
 	provider := oauth2.New(logger.Logger, conf, storageClient, httpClient)
-	openvpnClient := openvpn.NewClient(context.Background(), logger.Logger, conf, provider)
+	openvpnClient := openvpn.New(context.Background(), logger.Logger, conf, provider)
 
-	require.NoError(tb, provider.Initialize(openvpnClient))
+	require.NoError(tb, provider.Initialize(context.Background(), openvpnClient))
 
 	httpClientListener := httptest.NewUnstartedServer(provider.Handler())
 	httpClientListener.Listener.Close()