Maint: move OpenVPN streams processing to config package

This commit is contained in:
Quentin McGaw (desktop)
2021-08-18 21:16:28 +00:00
parent a0cb6fabfd
commit 0027a76c49
8 changed files with 136 additions and 73 deletions

View File

@@ -0,0 +1,60 @@
package config
import (
"strings"
"github.com/fatih/color"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/logging"
)
func processLogLine(s string) (filtered string, level logging.Level) {
for _, ignored := range []string{
"WARNING: you are using user/group/chroot/setcon without persist-tun -- this may cause restarts to fail",
"NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay",
} {
if s == ignored {
return "", logging.LevelDebug
}
}
switch {
case strings.HasPrefix(s, "NOTE: "):
filtered = strings.TrimPrefix(s, "NOTE: ")
level = logging.LevelInfo
case strings.HasPrefix(s, "WARNING: "):
filtered = strings.TrimPrefix(s, "WARNING: ")
level = logging.LevelWarn
case strings.HasPrefix(s, "Options error: "):
filtered = strings.TrimPrefix(s, "Options error: ")
level = logging.LevelError
case s == "Initialization Sequence Completed":
return color.HiGreenString(s), logging.LevelInfo
case s == "AUTH: Received control message: AUTH_FAILED":
filtered = s + `
Your credentials might be wrong 🤨
`
level = logging.LevelError
case strings.Contains(s, "TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)"): //nolint:lll
filtered = s + `
🚒🚒🚒🚒🚒🚨🚨🚨🚨🚨🚨🚒🚒🚒🚒🚒
That error usually happens because either:
1. The VPN server IP address you are trying to connect to is no longer valid 🔌
Update your server information using https://github.com/qdm12/gluetun/wiki/Updating-Servers
2. The VPN server crashed 💥, try changing your VPN servers filtering options such as REGION
3. Your Internet connection is not working 🤯, ensure it works
4. Something else ➡️ https://github.com/qdm12/gluetun/issues/new/choose
`
level = logging.LevelWarn
default:
filtered = s
level = logging.LevelInfo
}
filtered = constants.ColorOpenvpn().Sprintf(filtered)
return filtered, level
}

View File

@@ -0,0 +1,57 @@
package config
import (
"testing"
"github.com/qdm12/golibs/logging"
"github.com/stretchr/testify/assert"
)
func Test_processLogLine(t *testing.T) {
t.Parallel()
tests := map[string]struct {
s string
filtered string
level logging.Level
}{
"empty string": {"", "", logging.LevelInfo},
"random string": {"asdasqdb", "asdasqdb", logging.LevelInfo},
"openvpn unknown": {
"message",
"message",
logging.LevelInfo},
"openvpn note": {
"NOTE: message",
"message",
logging.LevelInfo},
"openvpn warning": {
"WARNING: message",
"message",
logging.LevelWarn},
"openvpn options error": {
"Options error: message",
"message",
logging.LevelError},
"openvpn ignored message": {
"NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay",
"",
logging.LevelDebug},
"openvpn success": {
"Initialization Sequence Completed",
"Initialization Sequence Completed",
logging.LevelInfo},
"openvpn auth failed": {
"AUTH: Received control message: AUTH_FAILED",
"AUTH: Received control message: AUTH_FAILED\n\nYour credentials might be wrong 🤨\n\n",
logging.LevelError},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
filtered, level := processLogLine(tc.s)
assert.Equal(t, tc.filtered, filtered)
assert.Equal(t, tc.level, level)
})
}
}

View File

@@ -6,10 +6,12 @@ import (
"github.com/qdm12/golibs/logging"
)
var _ Interface = (*Configurator)(nil)
type Interface interface {
VersionGetter
AuthWriter
Starter
Runner
Writer
}

View File

@@ -0,0 +1,41 @@
package config
import (
"context"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/golibs/logging"
)
type Runner interface {
Run(ctx context.Context, errCh chan<- error, ready chan<- struct{},
logger logging.Logger, settings configuration.OpenVPN)
}
func (c *Configurator) Run(ctx context.Context, errCh chan<- error,
ready chan<- struct{}, logger logging.Logger, settings configuration.OpenVPN) {
stdoutLines, stderrLines, waitError, err := c.start(ctx, settings.Version, settings.Flags)
if err != nil {
errCh <- err
return
}
streamCtx, streamCancel := context.WithCancel(context.Background())
streamDone := make(chan struct{})
go streamLines(streamCtx, streamDone, logger,
stdoutLines, stderrLines, ready)
select {
case <-ctx.Done():
<-waitError
close(waitError)
streamCancel()
<-streamDone
errCh <- ctx.Err()
case err := <-waitError:
close(waitError)
streamCancel()
<-streamDone
errCh <- err
}
}

View File

@@ -17,12 +17,7 @@ const (
binOpenvpn25 = "openvpn"
)
type Starter interface {
Start(ctx context.Context, version string, flags []string) (
stdoutLines, stderrLines chan string, waitError chan error, err error)
}
func (c *Configurator) Start(ctx context.Context, version string, flags []string) (
func (c *Configurator) start(ctx context.Context, version string, flags []string) (
stdoutLines, stderrLines chan string, waitError chan error, err error) {
var bin string
switch version {

View File

@@ -0,0 +1,53 @@
package config
import (
"context"
"strings"
"github.com/qdm12/golibs/logging"
)
func streamLines(ctx context.Context, done chan<- struct{},
logger logging.Logger, stdout, stderr chan string,
tunnelReady chan<- struct{}) {
defer close(done)
var line string
for {
errLine := false
select {
case <-ctx.Done():
// Context should only be canceled after stdout and stderr are done
// being written to.
close(stdout)
close(stderr)
return
case line = <-stdout:
case line = <-stderr:
errLine = true
}
line, level := processLogLine(line)
if line == "" {
continue // filtered out
}
if errLine {
level = logging.LevelError
}
switch level {
case logging.LevelDebug:
logger.Debug(line)
case logging.LevelInfo:
logger.Info(line)
case logging.LevelWarn:
logger.Warn(line)
case logging.LevelError:
logger.Error(line)
}
if strings.Contains(line, "Initialization Sequence Completed") {
// do not close tunnelReady in case the initialization
// happens multiple times without Openvpn restarting
tunnelReady <- struct{}{}
}
}
}