Maint: move OpenVPN streams processing to config package
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
package openvpn
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
@@ -9,48 +8,6 @@ import (
|
|||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (l *Loop) collectLines(ctx context.Context, done chan<- struct{},
|
|
||||||
stdout, stderr chan string, tunnelUpData tunnelUpData) {
|
|
||||||
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:
|
|
||||||
l.logger.Debug(line)
|
|
||||||
case logging.LevelInfo:
|
|
||||||
l.logger.Info(line)
|
|
||||||
case logging.LevelWarn:
|
|
||||||
l.logger.Warn(line)
|
|
||||||
case logging.LevelError:
|
|
||||||
l.logger.Error(line)
|
|
||||||
}
|
|
||||||
if strings.Contains(line, "Initialization Sequence Completed") {
|
|
||||||
l.onTunnelUp(ctx, tunnelUpData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func processLogLine(s string) (filtered string, level logging.Level) {
|
func processLogLine(s string) (filtered string, level logging.Level) {
|
||||||
for _, ignored := range []string{
|
for _, ignored := range []string{
|
||||||
"WARNING: you are using user/group/chroot/setcon without persist-tun -- this may cause restarts to fail",
|
"WARNING: you are using user/group/chroot/setcon without persist-tun -- this may cause restarts to fail",
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package openvpn
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@@ -6,10 +6,12 @@ import (
|
|||||||
"github.com/qdm12/golibs/logging"
|
"github.com/qdm12/golibs/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Interface = (*Configurator)(nil)
|
||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
VersionGetter
|
VersionGetter
|
||||||
AuthWriter
|
AuthWriter
|
||||||
Starter
|
Runner
|
||||||
Writer
|
Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
41
internal/openvpn/config/run.go
Normal file
41
internal/openvpn/config/run.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,12 +17,7 @@ const (
|
|||||||
binOpenvpn25 = "openvpn"
|
binOpenvpn25 = "openvpn"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Starter interface {
|
func (c *Configurator) start(ctx context.Context, version string, flags []string) (
|
||||||
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) (
|
|
||||||
stdoutLines, stderrLines chan string, waitError chan error, err error) {
|
stdoutLines, stderrLines chan string, waitError chan error, err error) {
|
||||||
var bin string
|
var bin string
|
||||||
switch version {
|
switch version {
|
||||||
|
|||||||
53
internal/openvpn/config/stream.go
Normal file
53
internal/openvpn/config/stream.go
Normal 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{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,28 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// waitForError waits 100ms for an error in the waitError channel.
|
||||||
|
func (l *Loop) waitForError(ctx context.Context,
|
||||||
|
waitError chan error) (err error) {
|
||||||
|
const waitDurationForError = 100 * time.Millisecond
|
||||||
|
timer := time.NewTimer(waitDurationForError)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
return ctx.Err()
|
||||||
|
case <-timer.C:
|
||||||
|
return nil
|
||||||
|
case err := <-waitError:
|
||||||
|
close(waitError)
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Loop) crashed(ctx context.Context, err error) {
|
func (l *Loop) crashed(ctx context.Context, err error) {
|
||||||
l.signalOrSetStatus(constants.Crashed)
|
l.signalOrSetStatus(constants.Crashed)
|
||||||
l.logAndWait(ctx, err)
|
l.logAndWait(ctx, err)
|
||||||
|
|||||||
@@ -31,29 +31,23 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
l.crashed(ctx, err)
|
l.crashed(ctx, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
openvpnCtx, openvpnCancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
stdoutLines, stderrLines, waitError, err := l.openvpnConf.Start(
|
|
||||||
openvpnCtx, openVPNSettings.Version, openVPNSettings.Flags)
|
|
||||||
if err != nil {
|
|
||||||
openvpnCancel()
|
|
||||||
l.crashed(ctx, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
linesCollectionCtx, linesCollectionCancel := context.WithCancel(context.Background())
|
|
||||||
lineCollectionDone := make(chan struct{})
|
|
||||||
tunnelUpData := tunnelUpData{
|
tunnelUpData := tunnelUpData{
|
||||||
portForwarding: providerSettings.PortForwarding.Enabled,
|
portForwarding: providerSettings.PortForwarding.Enabled,
|
||||||
serverName: serverName,
|
serverName: serverName,
|
||||||
portForwarder: providerConf,
|
portForwarder: providerConf,
|
||||||
}
|
}
|
||||||
go l.collectLines(linesCollectionCtx, lineCollectionDone,
|
|
||||||
stdoutLines, stderrLines, tunnelUpData)
|
openvpnCtx, openvpnCancel := context.WithCancel(context.Background())
|
||||||
closeStreams := func() {
|
waitError := make(chan error)
|
||||||
linesCollectionCancel()
|
tunnelReady := make(chan struct{})
|
||||||
<-lineCollectionDone
|
|
||||||
|
go l.openvpnConf.Run(openvpnCtx, waitError, tunnelReady,
|
||||||
|
l.logger, openVPNSettings)
|
||||||
|
|
||||||
|
if err := l.waitForError(ctx, waitError); err != nil {
|
||||||
|
openvpnCancel()
|
||||||
|
l.crashed(ctx, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
l.backoffTime = defaultBackoffTime
|
l.backoffTime = defaultBackoffTime
|
||||||
@@ -62,6 +56,8 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
stayHere := true
|
stayHere := true
|
||||||
for stayHere {
|
for stayHere {
|
||||||
select {
|
select {
|
||||||
|
case <-tunnelReady:
|
||||||
|
go l.onTunnelUp(openvpnCtx, tunnelUpData)
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
const pfTimeout = 100 * time.Millisecond
|
const pfTimeout = 100 * time.Millisecond
|
||||||
l.stopPortForwarding(context.Background(),
|
l.stopPortForwarding(context.Background(),
|
||||||
@@ -69,7 +65,6 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
openvpnCancel()
|
openvpnCancel()
|
||||||
<-waitError
|
<-waitError
|
||||||
close(waitError)
|
close(waitError)
|
||||||
closeStreams()
|
|
||||||
return
|
return
|
||||||
case <-l.stop:
|
case <-l.stop:
|
||||||
l.userTrigger = true
|
l.userTrigger = true
|
||||||
@@ -79,7 +74,6 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
<-waitError
|
<-waitError
|
||||||
// do not close waitError or the waitError
|
// do not close waitError or the waitError
|
||||||
// select case will trigger
|
// select case will trigger
|
||||||
closeStreams()
|
|
||||||
l.stopped <- struct{}{}
|
l.stopped <- struct{}{}
|
||||||
case <-l.start:
|
case <-l.start:
|
||||||
l.userTrigger = true
|
l.userTrigger = true
|
||||||
@@ -87,7 +81,6 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
|||||||
stayHere = false
|
stayHere = false
|
||||||
case err := <-waitError: // unexpected error
|
case err := <-waitError: // unexpected error
|
||||||
close(waitError)
|
close(waitError)
|
||||||
closeStreams()
|
|
||||||
|
|
||||||
l.statusManager.Lock() // prevent SetStatus from running in parallel
|
l.statusManager.Lock() // prevent SetStatus from running in parallel
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user