// Creates a new tunnel establisher.
func NewSSHTunnelEstablisher(registerInMode bool) *SSHTunnelEstablisher {
	self := new(SSHTunnelEstablisher)
	self.closables = []io.Closer{}
	self.ciHostURL = nil

	self.aliveTicker, self.aliveTickEvaluator = time.NewTicker(nodeSshTunnelAliveMonitoringInterval), time.NewTicker(nodeSshTunnelAliveMonitoringInterval)
	self.expectedAliveTick, self.lastAliveTick = util.NewAtomicInt32(), util.NewAtomicInt32()
	self.tunnelConnected = util.NewAtomicBoolean()

	if registerInMode {
		modes.RegisterModeListener(func(mode modes.ExecutableMode, nextStatus int32, config *util.Config) {
			if !config.CITunnelSSHEnabled || config.CITunnelSSHAddress == "" || mode.Name() != "client" || !config.HasCIConnection() {
				return
			}

			if nextStatus == modes.ModeStarting {
				var err error
				if self.ciHostURL, err = url.Parse(config.CIHostURI); err != nil {
					util.GOut("ssh-tunnel", "ERROR: Failed parsing Jenkins URI. Cannot tunnel connections to Jenkins. Cause: %v", err)
					return
				}

				self.setupSSHTunnel(config)

			} else if nextStatus == modes.ModeStopped {
				self.tearDownSSHTunnel(config)
			}
		})
	}

	return self
}
func NewServerMode() *ServerMode {
	r := new(ServerMode)
	r.status = util.NewAtomicInt32()
	return r
}