Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix VPN client remote startup #470

Merged
merged 17 commits into from
Aug 13, 2020
22 changes: 19 additions & 3 deletions internal/vpn/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func NewClient(cfg ClientConfig, l logrus.FieldLogger, conn net.Conn) (*Client,
cfg: cfg,
log: l,
conn: conn,
directIPs: directIPs,
directIPs: filterOutEqualIPs(directIPs),
defaultGateway: defaultGateway,
closeC: make(chan struct{}),
}, nil
Expand Down Expand Up @@ -199,9 +199,9 @@ func (c *Client) routeTrafficDirectly(tunGateway net.IP) {
func (c *Client) setupDirectRoutes() error {
for _, ip := range c.directIPs {
if !ip.IsLoopback() {
c.log.Infof("Adding direct route to %s", ip.String())
c.log.Infof("Adding direct route to %s, via %s", ip.String(), c.defaultGateway.String())
if err := AddRoute(ip.String()+directRouteNetmaskCIDR, c.defaultGateway.String()); err != nil {
return fmt.Errorf("error adding direct route to %s", ip.String())
return fmt.Errorf("error adding direct route to %s: %w", ip.String(), err)
}
}
}
Expand Down Expand Up @@ -332,3 +332,19 @@ func ipFromEnv(key string) (net.IP, error) {

return ip, nil
}

func filterOutEqualIPs(ips []net.IP) []net.IP {
ipsSet := make(map[string]struct{})
var filteredIPs []net.IP
for _, ip := range ips {
ipStr := ip.String()

if _, ok := ipsSet[ipStr]; !ok {
filteredIPs = append(filteredIPs, ip)
ipsSet[ip.String()] = struct{}{}
}
}

return filteredIPs

}
62 changes: 50 additions & 12 deletions pkg/app/appserver/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,14 @@ type Proc struct {
conn net.Conn // connection to proc - introduced AFTER proc is started
connCh chan struct{} // push here when conn is received - protected by 'connOnce'
connOnce sync.Once // ensures we only push to 'connCh' once

m ProcManager
appName string
}

// NewProc constructs `Proc`.
func NewProc(mLog *logging.MasterLogger, conf appcommon.ProcConfig, disc appdisc.Updater) *Proc {
func NewProc(mLog *logging.MasterLogger, conf appcommon.ProcConfig, disc appdisc.Updater, m ProcManager,
appName string) *Proc {
if mLog == nil {
mLog = logging.NewMasterLogger()
}
Expand All @@ -60,12 +64,14 @@ func NewProc(mLog *logging.MasterLogger, conf appcommon.ProcConfig, disc appdisc
cmd.Stderr = appLog.WithField("_module", moduleName).WithField("func", "(STDERR)").Writer()

return &Proc{
disc: disc,
conf: conf,
log: mLog.PackageLogger(moduleName),
logDB: appLogDB,
cmd: cmd,
connCh: make(chan struct{}, 1),
disc: disc,
conf: conf,
log: mLog.PackageLogger(moduleName),
logDB: appLogDB,
cmd: cmd,
connCh: make(chan struct{}, 1),
m: m,
appName: appName,
}
}

Expand Down Expand Up @@ -99,10 +105,6 @@ func (p *Proc) InjectConn(conn net.Conn) bool {
}

func (p *Proc) awaitConn() bool {
if _, ok := <-p.connCh; !ok {
return false
}

rpcS := rpc.NewServer()
if err := rpcS.RegisterName(p.conf.ProcKey.String(), p.rpcGW); err != nil {
panic(err)
Expand Down Expand Up @@ -149,6 +151,42 @@ func (p *Proc) Start() error {
}

go func() {
waitErrCh := make(chan error)
go func() {
waitErrCh <- p.cmd.Wait()
close(waitErrCh)
}()

select {
case _, ok := <-p.connCh:
if !ok {
// in this case app got stopped from the outer code before initializing the connection,
// just kill the process and exit.
_ = p.cmd.Process.Kill() //nolint:errcheck
p.waitMx.Unlock()

return
}
case waitErr := <-waitErrCh:
// in this case app process finished before initializing the connection. Happens if an
// error occurred during app startup.
p.waitErr = waitErr
p.waitMx.Unlock()

// channel won't get closed outside, close it now.
p.connOnce.Do(func() { close(p.connCh) })

// here will definitely be an error notifying that the process
// is already stopped. We do this to remove proc from the manager,
// therefore giving the correct app status to hypervisor.
_ = p.m.Stop(p.appName) //nolint:errcheck

return
}

// here, the connection is established, so we're not blocked by awaiting it anymore,
// execution may be continued as usual.

if ok := p.awaitConn(); !ok {
_ = p.cmd.Process.Kill() //nolint:errcheck
p.waitMx.Unlock()
Expand All @@ -160,7 +198,7 @@ func (p *Proc) Start() error {
defer p.disc.Stop()

// Wait for proc to exit.
p.waitErr = p.cmd.Wait()
p.waitErr = <-waitErrCh

// Close proc conn and associated listeners and connections.
if err := p.conn.Close(); err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
Expand Down
2 changes: 1 addition & 1 deletion pkg/app/appserver/proc_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (m *procManager) Start(conf appcommon.ProcConfig) (appcommon.ProcID, error)
Debug("No app discovery associated with app.")
}

proc := NewProc(m.mLog, conf, disc)
proc := NewProc(m.mLog, conf, disc, m, conf.AppName)
m.procs[conf.AppName] = proc
m.procsByKey[conf.ProcKey] = proc

Expand Down