Browse Source

more robust versioning system and cli option for printing version information

Alan Shreve 12 years ago
parent
commit
793d827b98

+ 11 - 0
src/ngrok/client/cli.go

@@ -5,6 +5,7 @@ import (
 	"flag"
 	"fmt"
 	"net"
+	"ngrok/version"
 	"os"
 	"strconv"
 )
@@ -120,8 +121,18 @@ func parseArgs() *Options {
 		4040,
 		"The port on which the web interface is served")
 
+	v := flag.Bool(
+		"version",
+		false,
+		"Print ngrok version and exit")
+
 	flag.Parse()
 
+	if *v {
+		fmt.Println(version.MajorMinor())
+		os.Exit(0)
+	}
+
 	return &Options{
 		server:    *server,
 		httpAuth:  *httpAuth,

+ 39 - 5
src/ngrok/client/main.go

@@ -2,9 +2,11 @@ package client
 
 import (
 	log "code.google.com/p/log4go"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"math"
+	"net/http"
 	"ngrok/client/ui"
 	"ngrok/client/views/term"
 	"ngrok/client/views/web"
@@ -13,14 +15,17 @@ import (
 	"ngrok/msg"
 	"ngrok/proto"
 	"ngrok/util"
+	"ngrok/version"
 	"runtime"
 	"sync/atomic"
 	"time"
 )
 
 const (
-	pingInterval   = 20 * time.Second
-	maxPongLatency = 15 * time.Second
+	pingInterval         = 20 * time.Second
+	maxPongLatency       = 15 * time.Second
+	versionCheckInterval = 6 * time.Hour
+	versionEndpoint      = "http://ngrok.com/dl/versions"
 )
 
 /**
@@ -71,8 +76,37 @@ func proxy(proxyAddr string, s *State, ctl *ui.Controller) {
 	ctl.Update(s)
 }
 
+func versionCheck(s *State, ctl *ui.Controller) {
+	for {
+		resp, err := http.Get(versionEndpoint)
+		if err != nil {
+			log.Warn("Failed to get version info %s: %v", versionEndpoint, err)
+			continue
+		}
+
+		var payload struct {
+			client struct {
+				version string
+			}
+		}
+
+		err = json.NewDecoder(resp.Body).Decode(payload)
+		if err != nil {
+			log.Warn("Failed to read version info: %v", err)
+			continue
+		}
+
+		if payload.client.version != version.MajorMinor() {
+			s.newVersion = payload.client.version
+			ctl.Update(s)
+		}
+
+		time.Sleep(versionCheckInterval)
+	}
+}
+
 /*
- * Hearbeating ensure our connection ngrokd is still live
+ * Hearbeating to ensure our connection ngrokd is still live
  */
 func heartbeat(lastPongAddr *int64, c conn.Conn) {
 	lastPing := time.Unix(atomic.LoadInt64(lastPongAddr)-1, 0)
@@ -156,7 +190,7 @@ func control(s *State, ctl *ui.Controller) {
 		Hostname:  s.opts.hostname,
 		Subdomain: s.opts.subdomain,
 		ClientId:  s.id,
-		Version:   msg.Version,
+		Version:   version.Proto,
 	})
 
 	if err != nil {
@@ -179,7 +213,7 @@ func control(s *State, ctl *ui.Controller) {
 	conn.Info("Tunnel established at %v", regAck.Url)
 	s.publicUrl = regAck.Url
 	s.status = "online"
-	s.serverVersion = regAck.Version
+	s.serverVersion = regAck.MmVersion
 	ctl.Update(s)
 
 	// start the heartbeat

+ 5 - 3
src/ngrok/client/state.go

@@ -2,8 +2,8 @@ package client
 
 import (
 	metrics "github.com/inconshreveable/go-metrics"
-	"ngrok/msg"
 	"ngrok/proto"
+	"ngrok/version"
 )
 
 // client state
@@ -11,6 +11,7 @@ type State struct {
 	id            string
 	publicUrl     string
 	serverVersion string
+	newVersion    string
 	protocol      proto.Protocol
 	opts          *Options
 	metrics       *ClientMetrics
@@ -20,13 +21,14 @@ type State struct {
 }
 
 // implement client.ui.State
-func (s State) GetClientVersion() string    { return msg.Version }
-func (s State) GetServerVersion() string    { return msg.Version }
+func (s State) GetClientVersion() string    { return version.MajorMinor() }
+func (s State) GetServerVersion() string    { return s.serverVersion }
 func (s State) GetPublicUrl() string        { return s.publicUrl }
 func (s State) GetLocalAddr() string        { return s.opts.localaddr }
 func (s State) GetWebPort() int             { return s.opts.webport }
 func (s State) GetStatus() string           { return s.status }
 func (s State) GetProtocol() proto.Protocol { return s.protocol }
+func (s State) GetNewVersion() string       { return s.newVersion }
 
 func (s State) GetConnectionMetrics() (metrics.Meter, metrics.Timer) {
 	return s.metrics.connMeter, s.metrics.connTimer

+ 1 - 0
src/ngrok/client/ui/state.go

@@ -8,6 +8,7 @@ import (
 type State interface {
 	GetClientVersion() string
 	GetServerVersion() string
+	GetNewVersion() string
 	GetPublicUrl() string
 	GetLocalAddr() string
 	GetStatus() string

+ 8 - 1
src/ngrok/client/views/term/view.go

@@ -73,7 +73,14 @@ func (v *TermView) Render() {
 
 	// quit instructions
 	quitMsg := "(Ctrl+C to quit)"
-	v.Printf(v.x-len(quitMsg), 0, quitMsg)
+	v.Printf(v.w-len(quitMsg), 0, quitMsg)
+
+	// new version message
+	//newVersion := v.state.GetNewVersion()
+	//if newVersion != "" {
+	newVersionMsg := fmt.Sprintf("new version available")
+	v.APrintf(termbox.ColorYellow, 30, 0, newVersionMsg)
+	//}
 
 	v.APrintf(termbox.ColorBlue|termbox.AttrBold, 0, 0, "ngrok")
 

+ 4 - 5
src/ngrok/msg/msg.go

@@ -7,10 +7,6 @@ import (
 
 var TypeMap map[string]reflect.Type
 
-const (
-	Version = "0.1"
-)
-
 func init() {
 	TypeMap = make(map[string]reflect.Type)
 
@@ -36,6 +32,7 @@ type Envelope struct {
 
 type RegMsg struct {
 	Version   string
+	MmVersion string
 	Protocol  string
 	Hostname  string
 	Subdomain string
@@ -49,6 +46,7 @@ type RegMsg struct {
 
 type RegAckMsg struct {
 	Version   string
+	MmVersion string
 	Url       string
 	ProxyAddr string
 	Error     string
@@ -71,7 +69,8 @@ type VersionMsg struct {
 }
 
 type VersionRespMsg struct {
-	Version string
+	Version   string
+	MmVersion string
 }
 
 type MetricsMsg struct {

+ 5 - 1
src/ngrok/server/control.go

@@ -4,6 +4,7 @@ import (
 	"io"
 	"ngrok/conn"
 	"ngrok/msg"
+	"ngrok/version"
 	"runtime/debug"
 	"time"
 )
@@ -88,7 +89,10 @@ func (c *Control) managerThread() {
 				c.out <- &msg.PongMsg{}
 
 			case *msg.VersionMsg:
-				c.out <- &msg.VersionRespMsg{Version: version}
+				c.out <- &msg.VersionRespMsg{
+					Version:   version.Proto,
+					MmVersion: version.MajorMinor(),
+				}
 			}
 		}
 	}

+ 0 - 1
src/ngrok/server/main.go

@@ -21,7 +21,6 @@ type Options struct {
 /* GLOBALS */
 var (
 	hostRegex *regexp.Regexp
-	version   string = "0.1"
 	proxyAddr string
 	tunnels   *TunnelManager
 )

+ 5 - 3
src/ngrok/server/tunnel.go

@@ -8,6 +8,7 @@ import (
 	"ngrok/conn"
 	nlog "ngrok/log"
 	"ngrok/msg"
+	"ngrok/version"
 )
 
 /**
@@ -60,8 +61,8 @@ func newTunnel(m *msg.RegMsg, ctl *Control) (t *Tunnel) {
 		return
 	}
 
-	if m.Version != msg.Version {
-		t.ctl.stop <- &msg.RegAckMsg{Error: fmt.Sprintf("Incompatible versions. Server %s, client %s.", msg.Version, m.Version)}
+	if m.Version != version.Proto {
+		t.ctl.stop <- &msg.RegAckMsg{Error: fmt.Sprintf("Incompatible versions. Server %s, client %s.", version.MajorMinor(), m.Version)}
 	}
 
 	// pre-encode the http basic auth for fast comparisons later
@@ -75,7 +76,8 @@ func newTunnel(m *msg.RegMsg, ctl *Control) (t *Tunnel) {
 	t.ctl.out <- &msg.RegAckMsg{
 		Url:       t.url,
 		ProxyAddr: fmt.Sprintf("%s", proxyAddr),
-		Version:   msg.Version,
+		Version:   version.Proto,
+		MmVersion: version.MajorMinor(),
 	}
 
 	return

+ 23 - 0
src/ngrok/version/version.go

@@ -0,0 +1,23 @@
+package version
+
+import (
+	"fmt"
+)
+
+const (
+	Proto = "1"
+	Major = "0"
+	Minor = "1"
+)
+
+func MajorMinor() string {
+	return fmt.Sprintf("%s.%s", Major, Minor)
+}
+
+func Full() string {
+	return fmt.Sprintf("%s-%s.%s", Proto, Major, Minor)
+}
+
+func Compat(client string, server string) bool {
+	return client == server
+}