<commit_before>package localstore

import (
	"bytes"
	"fmt"
	"time"

	. "github.com/pingcap/check"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/store/localstore/goleveldb"
)

var _ = Suite(&testMvccSuite{})

type testMvccSuite struct {
	s kv.Storage
}

func createMemStore() kv.Storage {
	// avoid cache
	path := fmt.Sprintf("memory://%d", time.Now().UnixNano())
	d := Driver{
		goleveldb.MemoryDriver{},
	}
	store, err := d.Open(path)
	if err != nil {
		panic(err)
	}
	return store
}

func (t *testMvccSuite) addDirtyData() {
	engineDB := t.s.(*dbStore).db
	b := engineDB.NewBatch()
	b.Put([]byte("\xf0dirty"), []byte("testvalue"))
	b.Put([]byte("\x00dirty"), []byte("testvalue"))
	engineDB.Commit(b)
}

func (t *testMvccSuite) TestMvccEncode(c *C) {
	encodedKey1 := MvccEncodeVersionKey([]byte("A"), kv.Version{Ver: 1})
	encodedKey2 := MvccEncodeVersionKey([]byte("A"), kv.Version{Ver: 2})
	// A_2
	// A_1
	c.Assert(encodedKey1.Cmp(encodedKey2), Greater, 0)

	// decode test
	key, ver, err := MvccDecode(encodedKey1)
	c.Assert(err, IsNil)
	c.Assert(bytes.Compare(key, []byte("A")), Equals, 0)
	c.Assert(ver.Ver, Equals, uint64(1))
}

func (t *testMvccSuite) scanRawEngine(c *C, f func([]byte, []byte)) {
	// scan raw db
	s, err := t.s.(*dbStore).db.GetSnapshot()
	c.Assert(err, IsNil)
	it := s.NewIterator(nil)
	for it.Next() {
		f(it.Key(), it.Value())
	}
}

func (t *testMvccSuite) SetUpTest(c *C) {
	// create new store
	t.s = createMemStore()
	t.addDirtyData()
	// insert test data
	txn, err := t.s.Begin()
	c.Assert(err, IsNil)
	for i := 0; i < 5; i++ {
		val := encodeInt(i)
		err := txn.Set(val, val)
		c.Assert(err, IsNil)
	}
	txn.Commit()
}

func (t *testMvccSuite) TestMvccGet(c *C) {
	txn, err := t.s.Begin()
	c.Assert(err, IsNil)
	k := encodeInt(1)
	_, err = txn.Get(k)
	c.Assert(err, IsNil)
	// no such key
	k = encodeInt(1024)
	_, err = txn.Get(k)
	c.Assert(err, NotNil)
	txn.Commit()
}

func (t *testMvccSuite) TestMvccPutAndDel(c *C) {
	txn, err := t.s.Begin()
	c.Assert(err, IsNil)
	// remove 0,1,2
	for i := 0; i < 3; i++ {
		val := encodeInt(i)
		err := txn.Delete(val)
		c.Assert(err, IsNil)
	}
	txn.Commit()

	txn, _ = t.s.Begin()
	_, err = txn.Get(encodeInt(0))
	c.Assert(err, NotNil)
	v, err := txn.Get(encodeInt(4))
	c.Assert(err, IsNil)
	c.Assert(len(v), Greater, 0)
	txn.Commit()

	cnt := 0
	t.scanRawEngine(c, func(k, v []byte) {
		cnt++
	})
	txn, _ = t.s.Begin()
	txn.Set(encodeInt(0), []byte("v"))
	v, err = txn.Get(encodeInt(0))
	c.Assert(err, IsNil)
	txn.Commit()

	cnt1 := 0
	t.scanRawEngine(c, func(k, v []byte) {
		cnt1++
	})
	c.Assert(cnt1, Greater, cnt)
}

func (t *testMvccSuite) TestMvccNext(c *C) {
	txn, _ := t.s.Begin()
	it, err := txn.Seek(encodeInt(2), nil)
	c.Assert(err, IsNil)
	c.Assert(it.Valid(), IsTrue)
	for it.Valid() {
		it, err = it.Next(nil)
		c.Assert(err, IsNil)
	}
	txn.Commit()
}

func encodeTestDataKey(i int) []byte {
	return kv.EncodeKey(encodeInt(i))
}

func (t *testMvccSuite) TestSnapshotGet(c *C) {
	tx, _ := t.s.Begin()
	b, err := tx.Get(encodeInt(1))
	c.Assert(err, IsNil)
	tx.Commit()

	// Modify
	tx, _ = t.s.Begin()
	err = tx.Set(encodeInt(1), []byte("new"))
	c.Assert(err, IsNil)
	err = tx.Commit()
	c.Assert(err, IsNil)
	v, err := tx.CommittedVersion()
	c.Assert(err, IsNil)
	testKey := encodeTestDataKey(1)

	snapshot, err := t.s.GetSnapshot()
	defer snapshot.MvccRelease()
	b, err = snapshot.MvccGet(testKey, kv.MaxVersion)
	c.Assert(err, IsNil)
	c.Assert(string(b), Equals, "new")

	// Get last version
	b, err = snapshot.MvccGet(testKey, kv.NewVersion(v.Ver-1))
	c.Assert(err, IsNil)
	c.Assert(string(b), Equals, string(encodeInt(1)))

	// Get version not exists
	b, err = snapshot.MvccGet(testKey, kv.MinVersion)
	c.Assert(err, NotNil)
}

func (t *testMvccSuite) TestMvccSnapshotScan(c *C) {
	tx, _ := t.s.Begin()
	err := tx.Set(encodeInt(1), []byte("new"))
	c.Assert(err, IsNil)
	err = tx.Commit()
	c.Assert(err, IsNil)
	v, err := tx.CommittedVersion()
	c.Assert(err, IsNil)

	snapshot, err := t.s.GetSnapshot()
	defer snapshot.MvccRelease()
	c.Assert(err, IsNil)

	testKey := encodeTestDataKey(1)
	// iter helper function
	iterFunc := func(it kv.Iterator) bool {
		found := false
		for it.Valid() {
			if string(it.Value()) == "new" {
				found = true
			}
			it, err = it.Next(nil)
			c.Assert(err, IsNil)
		}
		return found
	}

	it := snapshot.NewMvccIterator(testKey, kv.MaxVersion)
	found := iterFunc(it)
	c.Assert(found, IsTrue)

	it = snapshot.NewMvccIterator(testKey, kv.NewVersion(v.Ver-1))
	found = iterFunc(it)
	c.Assert(found, IsFalse)
}
<commit_msg>kv : Add some MVCC test<commit_after>package localstore

import (
	"bytes"
	"fmt"
	"time"

	. "github.com/pingcap/check"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/store/localstore/goleveldb"
)

var _ = Suite(&testMvccSuite{})

type testMvccSuite struct {
	s kv.Storage
}

func createMemStore() kv.Storage {
	// avoid cache
	path := fmt.Sprintf("memory://%d", time.Now().UnixNano())
	d := Driver{
		goleveldb.MemoryDriver{},
	}
	store, err := d.Open(path)
	if err != nil {
		panic(err)
	}
	return store
}

func (t *testMvccSuite) addDirtyData() {
	engineDB := t.s.(*dbStore).db
	b := engineDB.NewBatch()
	b.Put([]byte("\xf0dirty"), []byte("testvalue"))
	b.Put([]byte("\x00dirty"), []byte("testvalue"))
	engineDB.Commit(b)
}

func (t *testMvccSuite) TestMvccEncode(c *C) {
	encodedKey1 := MvccEncodeVersionKey([]byte("A"), kv.Version{Ver: 1})
	encodedKey2 := MvccEncodeVersionKey([]byte("A"), kv.Version{Ver: 2})
	// A_2
	// A_1
	c.Assert(encodedKey1.Cmp(encodedKey2), Greater, 0)

	// decode test
	key, ver, err := MvccDecode(encodedKey1)
	c.Assert(err, IsNil)
	c.Assert(bytes.Compare(key, []byte("A")), Equals, 0)
	c.Assert(ver.Ver, Equals, uint64(1))
}

func (t *testMvccSuite) scanRawEngine(c *C, f func([]byte, []byte)) {
	// scan raw db
	s, err := t.s.(*dbStore).db.GetSnapshot()
	c.Assert(err, IsNil)
	it := s.NewIterator(nil)
	for it.Next() {
		f(it.Key(), it.Value())
	}
}

func (t *testMvccSuite) SetUpTest(c *C) {
	// create new store
	t.s = createMemStore()
	t.addDirtyData()
	// insert test data
	txn, err := t.s.Begin()
	c.Assert(err, IsNil)
	for i := 0; i < 5; i++ {
		val := encodeInt(i)
		err := txn.Set(val, val)
		c.Assert(err, IsNil)
	}
	txn.Commit()
}

func (t *testMvccSuite) TestMvccGet(c *C) {
	txn, err := t.s.Begin()
	c.Assert(err, IsNil)
	k := encodeInt(1)
	_, err = txn.Get(k)
	c.Assert(err, IsNil)
	// no such key
	k = encodeInt(1024)
	_, err = txn.Get(k)
	c.Assert(err, NotNil)
	txn.Commit()
}

func (t *testMvccSuite) TestMvccPutAndDel(c *C) {
	txn, err := t.s.Begin()
	c.Assert(err, IsNil)
	// remove 0,1,2
	for i := 0; i < 3; i++ {
		val := encodeInt(i)
		err := txn.Delete(val)
		c.Assert(err, IsNil)
	}
	txn.Commit()

	txn, _ = t.s.Begin()
	_, err = txn.Get(encodeInt(0))
	c.Assert(err, NotNil)
	v, err := txn.Get(encodeInt(4))
	c.Assert(err, IsNil)
	c.Assert(len(v), Greater, 0)
	txn.Commit()

	cnt := 0
	t.scanRawEngine(c, func(k, v []byte) {
		cnt++
	})
	txn, _ = t.s.Begin()
	txn.Set(encodeInt(0), []byte("v"))
	v, err = txn.Get(encodeInt(0))
	c.Assert(err, IsNil)
	txn.Commit()

	cnt1 := 0
	t.scanRawEngine(c, func(k, v []byte) {
		cnt1++
	})
	c.Assert(cnt1, Greater, cnt)
}

func (t *testMvccSuite) TestMvccNext(c *C) {
	txn, _ := t.s.Begin()
	it, err := txn.Seek(encodeInt(2), nil)
	c.Assert(err, IsNil)
	c.Assert(it.Valid(), IsTrue)
	for it.Valid() {
		it, err = it.Next(nil)
		c.Assert(err, IsNil)
	}
	txn.Commit()
}

func encodeTestDataKey(i int) []byte {
	return kv.EncodeKey(encodeInt(i))
}

func (t *testMvccSuite) TestSnapshotGet(c *C) {
	tx, _ := t.s.Begin()
	b, err := tx.Get(encodeInt(1))
	c.Assert(err, IsNil)
	tx.Commit()

	// Modify
	tx, _ = t.s.Begin()
	err = tx.Set(encodeInt(1), []byte("new"))
	c.Assert(err, IsNil)
	err = tx.Commit()
	c.Assert(err, IsNil)
	v, err := tx.CommittedVersion()
	c.Assert(err, IsNil)
	testKey := encodeTestDataKey(1)

	snapshot, err := t.s.GetSnapshot()
	defer snapshot.MvccRelease()
	b, err = snapshot.MvccGet(testKey, kv.MaxVersion)
	c.Assert(err, IsNil)
	c.Assert(string(b), Equals, "new")

	// Get last version
	b, err = snapshot.MvccGet(testKey, kv.NewVersion(v.Ver-1))
	c.Assert(err, IsNil)
	c.Assert(string(b), Equals, string(encodeInt(1)))

	// Get version not exists
	b, err = snapshot.MvccGet(testKey, kv.MinVersion)
	c.Assert(err, NotNil)
}

func (t *testMvccSuite) TestMvccSuiteGetLatest(c *C) {
	// update some new data
	for i := 0; i < 10; i++ {
		tx, _ := t.s.Begin()
		err := tx.Set(encodeInt(5), encodeInt(100+i))
		c.Assert(err, IsNil)
		err = tx.Commit()
		c.Assert(err, IsNil)
	}
	// we can always read newest data
	tx, _ := t.s.Begin()
	b, err := tx.Get(encodeInt(5))
	c.Assert(err, IsNil)
	c.Assert(string(b), Equals, string(encodeInt(100+9)))
	// we can always scan newest data
	it, err := tx.Seek(encodeInt(5), nil)
	c.Assert(err, IsNil)
	c.Assert(it.Valid(), IsTrue)
	c.Assert(string(it.Value()), Equals, string(encodeInt(100+9)))
	tx.Commit()
}

func (t *testMvccSuite) TestMvccSnapshotScan(c *C) {
	tx, _ := t.s.Begin()
	err := tx.Set(encodeInt(1), []byte("new"))
	c.Assert(err, IsNil)
	err = tx.Commit()
	c.Assert(err, IsNil)
	v, err := tx.CommittedVersion()
	c.Assert(err, IsNil)

	snapshot, err := t.s.GetSnapshot()
	defer snapshot.MvccRelease()
	c.Assert(err, IsNil)

	testKey := encodeTestDataKey(1)
	// iter helper function
	iterFunc := func(it kv.Iterator) bool {
		found := false
		for it.Valid() {
			if string(it.Value()) == "new" {
				found = true
			}
			it, err = it.Next(nil)
			c.Assert(err, IsNil)
		}
		return found
	}

	it := snapshot.NewMvccIterator(testKey, kv.MaxVersion)
	found := iterFunc(it)
	c.Assert(found, IsTrue)

	it = snapshot.NewMvccIterator(testKey, kv.NewVersion(v.Ver-1))
	found = iterFunc(it)
	c.Assert(found, IsFalse)
}
<|endoftext|>


<commit_before>package main

import (
    "flag"
    "fmt"
    "github.com/abh/geoip"
    "github.com/growse/pcap"
    "github.com/miekg/dns"
    "log"
    "net"
    "os"
    "strings"
)

const (
    TYPE_IP  = 0x0800
    TYPE_ARP = 0x0806
    TYPE_IP6 = 0x86DD

    IP_ICMP = 1
    IP_INIP = 4
    IP_TCP  = 6
    IP_UDP  = 17
)

var (
    device         = flag.String("i", "", "interface")
    statsd_address = flag.String("s", "", "statsd_address")
    verbose        = flag.Bool("v", false, "verbose")
    dnsport        = flag.Int("p", 53, "dnsport")
    snaplen        = 65536
)

func flipstringslice(s []string) []string {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
    if len(s[0]) == 0 {
        s = s[1:]
    }
    return s
}

func main() {
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "usage: %s [ -p dnsport ] [ -i interface ] [ -s statsd address ] [ -v ]\n", os.Args[0])
        os.Exit(1)
    }

    flag.Parse()
    expr := fmt.Sprintf("port %d", *dnsport)

    if *statsd_address == "" {
        flag.Usage()
    }

    if *device == "" {
        devs, err := pcap.FindAllDevs()
        fmt.Println(devs)
        if err != "" {
            fmt.Fprintln(os.Stderr, "tcpdump: couldn't find any devices:", err)
        }
        if 0 == len(devs) {
            flag.Usage()
        }
        *device = devs[0].Name
    }
    h, err := pcap.OpenLive(*device, int32(snaplen), true, 500)
    if h == nil {
        log.Fatal(fmt.Sprintf("tcpdump fatal error: %s", err))
    }

    derr := h.SetDirection("out")
    if derr != nil {
        fmt.Println("tcpdump:", derr)
    }

    ferr := h.SetFilter(expr)
    if ferr != nil {
        fmt.Println("tcpdump:", ferr)
    }

    hostname, err := os.Hostname()

    if err == nil {
        statsd_namespace_prefix = fmt.Sprintf("%s.dnscollector.", hostname)
    }

    go statsd_pollloop(*statsd_address)
    geoasn, err := geoip.OpenType(geoip.GEOIP_ASNUM_EDITION)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_ASNUM_EDITION")
    }
    geocountry, err := geoip.OpenType(geoip.GEOIP_COUNTRY_EDITION)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_COUNTRY_EDITION")
    }
    geoasn6, err := geoip.OpenType(geoip.GEOIP_ASNUM_EDITION_V6)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_ASNUM_EDITION_V6")
    }
    geocountry6, err := geoip.OpenType(geoip.GEOIP_COUNTRY_EDITION_V6)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_COUNTRY_EDITION_V6")
    }
    var name, country string
    var ipOk bool
    var ipHdr *pcap.Iphdr
    var ip6Hdr *pcap.Ip6hdr
    for pkt, r := h.NextEx(); r >= 0; pkt, r = h.NextEx() {
        if r == 0 {
            // timeout, continue
            continue
        }
        pkt.Decode()
        if *verbose {
            fmt.Println(pkt)
        }
        msg := new(dns.Msg)
        err := msg.Unpack(pkt.Payload)
        // We only want packets which have been successfully unpacked
        // and have at least one answer
        if err == nil && len(msg.Answer) > 0 {
            if *verbose {
                fmt.Println(pkt)
            }
            if len(pkt.Headers) > 0 {
                //IPv4
                if ipHdr, ipOk = pkt.Headers[0].(*pcap.Iphdr); ipOk {
                    ip := net.IPv4(
                        ipHdr.DestIp[0],
                        ipHdr.DestIp[1],
                        ipHdr.DestIp[2],
                        ipHdr.DestIp[3],
                    ).String()
                    name, _ = geoasn.GetName(ip)
                    country, _ = geocountry.GetCountry(ip)
                } else if ip6Hdr, ipOk = pkt.Headers[0].(*pcap.Ip6hdr); ipOk {
                    //IPv6
                    ip := net.IP(ip6Hdr.DestIp).String()
                    name, _ = geoasn6.GetNameV6(ip)
                    country, _ = geocountry6.GetCountry_v6(ip)
                }
            }
            for i := range msg.Question {
                domainparts := strings.Split(strings.ToLower(msg.Question[i].Name), ".")
                domainparts = flipstringslice(domainparts)
                domainname := strings.Join(domainparts, ".")
                domainname = strings.Replace(domainname, ".", ">", -1)
                dnstype := strings.ToUpper(dns.TypeToString[msg.Question[i].Qtype])
                if len(name) == 0 {
                    name = "n/a"
                }
                if len(country) == 0 {
                    country = "n/a"
                }
                name = strings.Replace(name, ".", "{dot}", -1)
                country = strings.Replace(country, ".", "{dot}", -1)
                if *verbose {
                    fmt.Printf("name: %s type: %s asn %s country %s\n", domainname, dnstype, name, country)
                }
                key := fmt.Sprintf("%s.%s.%s.%s", domainname, name, country, dnstype)
                countermap.Lock()
                countermap.m[key]++
                countermap.Unlock()
                bytesmap.Lock()
                bytesmap.m[key] += pkt.Len
                bytesmap.Unlock()
            }
        }
    }
    fmt.Fprintln(os.Stderr, "tcpdump:", h.Geterror())

}
<commit_msg>Graphite hates special chars. so, encode the AS name<commit_after>package main

import (
    "flag"
    "net/url"
    "fmt"
    "github.com/abh/geoip"
    "github.com/growse/pcap"
    "github.com/miekg/dns"
    "log"
    "net"
    "os"
    "strings"
)

const (
    TYPE_IP  = 0x0800
    TYPE_ARP = 0x0806
    TYPE_IP6 = 0x86DD

    IP_ICMP = 1
    IP_INIP = 4
    IP_TCP  = 6
    IP_UDP  = 17
)

var (
    device         = flag.String("i", "", "interface")
    statsd_address = flag.String("s", "", "statsd_address")
    verbose        = flag.Bool("v", false, "verbose")
    dnsport        = flag.Int("p", 53, "dnsport")
    snaplen        = 65536
)

func flipstringslice(s []string) []string {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
    if len(s[0]) == 0 {
        s = s[1:]
    }
    return s
}

func main() {
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "usage: %s [ -p dnsport ] [ -i interface ] [ -s statsd address ] [ -v ]\n", os.Args[0])
        os.Exit(1)
    }

    flag.Parse()
    expr := fmt.Sprintf("port %d", *dnsport)

    if *statsd_address == "" {
        flag.Usage()
    }

    if *device == "" {
        devs, err := pcap.FindAllDevs()
        fmt.Println(devs)
        if err != "" {
            fmt.Fprintln(os.Stderr, "tcpdump: couldn't find any devices:", err)
        }
        if 0 == len(devs) {
            flag.Usage()
        }
        *device = devs[0].Name
    }
    h, err := pcap.OpenLive(*device, int32(snaplen), true, 500)
    if h == nil {
        log.Fatal(fmt.Sprintf("tcpdump fatal error: %s", err))
    }

    derr := h.SetDirection("out")
    if derr != nil {
        fmt.Println("tcpdump:", derr)
    }

    ferr := h.SetFilter(expr)
    if ferr != nil {
        fmt.Println("tcpdump:", ferr)
    }

    hostname, err := os.Hostname()

    if err == nil {
        statsd_namespace_prefix = fmt.Sprintf("%s.dnscollector.", hostname)
    }

    go statsd_pollloop(*statsd_address)
    geoasn, err := geoip.OpenType(geoip.GEOIP_ASNUM_EDITION)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_ASNUM_EDITION")
    }
    geocountry, err := geoip.OpenType(geoip.GEOIP_COUNTRY_EDITION)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_COUNTRY_EDITION")
    }
    geoasn6, err := geoip.OpenType(geoip.GEOIP_ASNUM_EDITION_V6)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_ASNUM_EDITION_V6")
    }
    geocountry6, err := geoip.OpenType(geoip.GEOIP_COUNTRY_EDITION_V6)
    if err != nil {
        log.Fatal("Could not open geoip database: GEOIP_COUNTRY_EDITION_V6")
    }
    var name, country string
    var ipOk bool
    var ipHdr *pcap.Iphdr
    var ip6Hdr *pcap.Ip6hdr
    for pkt, r := h.NextEx(); r >= 0; pkt, r = h.NextEx() {
        if r == 0 {
            // timeout, continue
            continue
        }
        pkt.Decode()
        msg := new(dns.Msg)
        err := msg.Unpack(pkt.Payload)
        // We only want packets which have been successfully unpacked
        // and have at least one answer
        if err == nil && len(msg.Answer) > 0 {
            if *verbose {
                fmt.Println(pkt)
            }
            if len(pkt.Headers) > 0 {
                //IPv4
                if ipHdr, ipOk = pkt.Headers[0].(*pcap.Iphdr); ipOk {
                    ip := net.IPv4(
                        ipHdr.DestIp[0],
                        ipHdr.DestIp[1],
                        ipHdr.DestIp[2],
                        ipHdr.DestIp[3],
                    ).String()
                    name, _ = geoasn.GetName(ip)
                    country, _ = geocountry.GetCountry(ip)
                } else if ip6Hdr, ipOk = pkt.Headers[0].(*pcap.Ip6hdr); ipOk {
                    //IPv6
                    ip := net.IP(ip6Hdr.DestIp).String()
                    name, _ = geoasn6.GetNameV6(ip)
                    country, _ = geocountry6.GetCountry_v6(ip)
                }
            }
            for i := range msg.Question {
                domainparts := strings.Split(strings.ToLower(msg.Question[i].Name), ".")
                domainparts = flipstringslice(domainparts)
                domainname := strings.Join(domainparts, ".")
                domainname = strings.Replace(domainname, ".", ">", -1)
                dnstype := strings.ToUpper(dns.TypeToString[msg.Question[i].Qtype])
                if len(name) == 0 {
                    name = "n/a"
                }
                if len(country) == 0 {
                    country = "n/a"
                }
                name = strings.Replace(name, ".", "{dot}", -1)
                country = strings.Replace(country, ".", "{dot}", -1)
                key := fmt.Sprintf("%s.%s.%s.%s", domainname, url.QueryEscape(name), country, dnstype)
                if *verbose {
                    fmt.Println(key)
                }
                countermap.Lock()
                countermap.m[key]++
                countermap.Unlock()
                bytesmap.Lock()
                bytesmap.m[key] += pkt.Len
                bytesmap.Unlock()
            }
        }
    }
    fmt.Fprintln(os.Stderr, "tcpdump:", h.Geterror())

}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"code.google.com/p/log4go"
	"fmt"
	"github.com/howeyc/fsnotify"
	"github.com/limetext/lime/backend/loaders"
	. "github.com/limetext/lime/backend/util"
	. "github.com/quarnster/util/text"
	"runtime"
	"runtime/debug"
	"sync"
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	GetEditor()
}

type (
	Editor struct {
		HasSettings
		windows       []*Window
		active_window *Window
		loginput      bool
		cmdhandler    commandHandler
		keyBindings   KeyBindings
		console       *View
		frontend      Frontend
		clipboard     string
		keyInput      chan (KeyPress)
		watcher       *fsnotify.Watcher
		watchedFiles  map[string]Watched
		watchLock     sync.Mutex
	}

	// The Frontend interface defines the API
	// for functionality that is frontend specific.
	Frontend interface {
		// Probe the frontend for the currently
		// visible region of the given view.
		VisibleRegion(v *View) Region

		// Make the frontend show the specified region of the
		// given view.
		Show(v *View, r Region)

		// Sets the status message shown in the status bar
		StatusMessage(string)

		// Displays an error message to the usser
		ErrorMessage(string)

		// Displays a message dialog to the user
		MessageDialog(string)

		// Displays an ok / cancel dialog to the user.
		// "okname" if provided will be used as the text
		// instead of "Ok" for the ok button.
		// Returns true when ok was pressed, and false when
		// cancel was pressed.
		OkCancelDialog(msg string, okname string) bool
	}
	myLogWriter struct {
		log chan string
	}
	DummyFrontend struct {
		m sync.Mutex
		// Default return value for OkCancelDialog
		defaultAction bool
	}
)

const (
	LIME_USER_PACKAGES_PATH = "../../3rdparty/bundles/"
	LIME_USER_PACKETS_PATH  = "../../3rdparty/bundles/User/"
	LIME_DEFAULTS_PATH      = "../../backend/packages/Default/"
)

func (h *DummyFrontend) SetDefaultAction(action bool) {
	h.m.Lock()
	defer h.m.Unlock()
	h.defaultAction = action
}
func (h *DummyFrontend) StatusMessage(msg string) { log4go.Info(msg) }
func (h *DummyFrontend) ErrorMessage(msg string)  { log4go.Error(msg) }
func (h *DummyFrontend) MessageDialog(msg string) { log4go.Info(msg) }
func (h *DummyFrontend) OkCancelDialog(msg string, button string) bool {
	log4go.Info(msg)
	h.m.Lock()
	defer h.m.Unlock()
	return h.defaultAction
}
func (h *DummyFrontend) Show(v *View, r Region)       {}
func (h *DummyFrontend) VisibleRegion(v *View) Region { return Region{} }

func newMyLogWriter() *myLogWriter {
	ret := &myLogWriter{make(chan string, 100)}
	go ret.handle()
	return ret
}

func (m *myLogWriter) handle() {
	for fl := range m.log {
		c := GetEditor().Console()
		f := fmt.Sprintf("%08d %d %s", c.Buffer().Size(), len(fl), fl)
		e := c.BeginEdit()
		c.Insert(e, c.Buffer().Size(), f)
		c.EndEdit(e)
	}
}

func (m *myLogWriter) LogWrite(rec *log4go.LogRecord) {
	p := Prof.Enter("log")
	defer p.Exit()
	fl := log4go.FormatLogRecord(log4go.FORMAT_DEFAULT, rec)
	m.log <- fl
}

func (m *myLogWriter) Close() {
	fmt.Println("Closing...")
	close(m.log)
}

var (
	ed  *Editor
	edl sync.Mutex
)

func GetEditor() *Editor {
	edl.Lock()
	defer edl.Unlock()
	if ed == nil {
		ed = &Editor{
			cmdhandler: commandHandler{
				ApplicationCommands: make(appcmd),
				TextCommands:        make(textcmd),
				WindowCommands:      make(wndcmd),
				verbose:             true,
			},
			frontend: &DummyFrontend{},
			console: &View{
				buffer:  NewBuffer(),
				scratch: true,
			},
			keyInput:     make(chan KeyPress, 32),
			watcher:      newWatcher(),
			watchedFiles: make(map[string]Watched),
		}
		ed.console.Settings().Set("is_widget", true)
		ed.Settings() // Just to initialize it
		log4go.Global.Close()
		log4go.Global.AddFilter("console", log4go.DEBUG, newMyLogWriter())
		go ed.inputthread()
		go ed.observeFiles()
	}
	return ed
}

func (e *Editor) Frontend() Frontend {
	return e.frontend
}

func (e *Editor) SetFrontend(f Frontend) {
	e.frontend = f
}

func (e *Editor) Init() {
	ed.loadKeybindings()
	ed.loadSettings()
}

func (e *Editor) loadKeybinding(pkg *packet) {
	var bindings KeyBindings
	if err := loaders.LoadJSON(pkg.Get().([]byte), &bindings); err != nil {
		log4go.Error(err)
	} else {
		log4go.Info("Loaded %s", pkg.Name())
		e.Watch(NewWatchedPackage(pkg))
	}
	e.keyBindings.merge(&bindings)
}

func (e *Editor) loadKeybindings() {
	for _, p := range packets.filter("keymap") {
		e.loadKeybinding(p)
	}
}

func (e *Editor) loadSetting(pkg *packet) {
	if err := loaders.LoadJSON(pkg.Get().([]byte), e.Settings()); err != nil {
		log4go.Error(err)
	} else {
		log4go.Info("Loaded %s", pkg.Name())
		e.Watch(NewWatchedPackage(pkg))
	}
}

func (e *Editor) loadSettings() {
	for _, p := range packets.filter("setting") {
		e.loadSetting(p)
	}
}

func (e *Editor) PackagesPath() string {
	return "../../3rdparty/bundles/"
}

func (e *Editor) Console() *View {
	return e.console
}

func (e *Editor) Windows() []*Window {
	edl.Lock()
	defer edl.Unlock()
	ret := make([]*Window, len(e.windows))
	copy(ret, e.windows)
	return ret
}

func (e *Editor) SetActiveWindow(w *Window) {
	e.active_window = w
}

func (e *Editor) ActiveWindow() *Window {
	return e.active_window
}

func (e *Editor) Watcher() *fsnotify.Watcher {
	return e.watcher
}

func (e *Editor) NewWindow() *Window {
	edl.Lock()
	e.windows = append(e.windows, &Window{})
	w := e.windows[len(e.windows)-1]
	edl.Unlock()
	w.Settings().SetParent(e)
	ed.SetActiveWindow(w)
	OnNewWindow.Call(w)
	return w
}

func (e *Editor) remove(w *Window) {
	edl.Lock()
	defer edl.Unlock()
	for i, ww := range e.windows {
		if w == ww {
			end := len(e.windows) - 1
			if i != end {
				copy(e.windows[i:], e.windows[i+1:])
			}
			e.windows = e.windows[:end]
			return
		}
	}
	log4go.Error("Wanted to remove window %+v, but it doesn't appear to be a child of this editor", w)
}

func (e *Editor) Arch() string {
	return runtime.GOARCH
}

func (e *Editor) Platform() string {
	return runtime.GOOS
}

func (e *Editor) Version() string {
	return "0"
}

func (e *Editor) CommandHandler() CommandHandler {
	return &e.cmdhandler
}

func (e *Editor) HandleInput(kp KeyPress) {
	e.keyInput <- kp
}

func (e *Editor) inputthread() {
	pc := 0
	var lastBindings KeyBindings
	doinput := func(kp KeyPress) {
		defer func() {
			if r := recover(); r != nil {
				log4go.Error("Panic in inputthread: %v\n%s", r, string(debug.Stack()))
				if pc > 0 {
					panic(r)
				}
				pc++
			}
		}()
		p := Prof.Enter("hi")
		defer p.Exit()

		lvl := log4go.FINE
		if e.loginput {
			lvl++
		}
		log4go.Logf(lvl, "Key: %v", kp)
		if lastBindings.keyOff == 0 {
			lastBindings = e.keyBindings
		}
	try_again:
		possible_actions := lastBindings.Filter(kp)
		lastBindings = possible_actions

		// TODO?
		var (
			wnd *Window
			v   *View
		)
		if wnd = e.ActiveWindow(); wnd != nil {
			v = wnd.ActiveView()
		}

		if action := possible_actions.Action(v); action != nil {
			p2 := Prof.Enter("hi.perform")
			// TODO: what's the command precedence?
			if c := e.cmdhandler.TextCommands[action.Command]; c != nil {
				if err := e.CommandHandler().RunTextCommand(v, action.Command, action.Args); err != nil {
					log4go.Debug("Couldn't run textcommand: %s", err)
				}
			} else if c := e.cmdhandler.WindowCommands[action.Command]; c != nil {
				if err := e.CommandHandler().RunWindowCommand(wnd, action.Command, action.Args); err != nil {
					log4go.Debug("Couldn't run windowcommand: %s", err)
				}
			} else if err := e.CommandHandler().RunApplicationCommand(action.Command, action.Args); err != nil {
				log4go.Debug("Couldn't run applicationcommand: %s", err)
			}
			p2.Exit()
		} else if possible_actions.keyOff > 1 {
			lastBindings = e.keyBindings
			goto try_again
		} else if kp.IsCharacter() {
			p2 := Prof.Enter("hi.character")
			log4go.Finest("kp: %v, pos: %v", kp, possible_actions)
			if err := e.CommandHandler().RunTextCommand(v, "insert", Args{"characters": string(rune(kp.Key))}); err != nil {
				log4go.Debug("Couldn't run textcommand: %s", err)
			}
			p2.Exit()
		}
	}
	for kp := range e.keyInput {
		doinput(kp)
	}
}

func (e *Editor) LogInput(l bool) {
	e.loginput = l
}

func (e *Editor) LogCommands(l bool) {
	e.cmdhandler.log = l
}

func (e *Editor) RunCommand(name string, args Args) {
	e.CommandHandler().RunApplicationCommand(name, args)
}

func (e *Editor) SetClipboard(n string) {
	e.clipboard = n
}

func (e *Editor) GetClipboard() string {
	return e.clipboard
}

func (e *Editor) Watch(file Watched) {
	log4go.Finest("Watch(%v)", file)
	if err := e.watcher.Watch(file.Name()); err != nil {
		log4go.Error("Could not watch file: %v", err)
	} else {
		e.watchLock.Lock()
		e.watchedFiles[file.Name()] = file
		e.watchLock.Unlock()
	}
}

func (e *Editor) UnWatch(name string) {
	if err := e.watcher.RemoveWatch(name); err != nil {
		log4go.Error("Couldn't unwatch file: %v", err)
	}
	log4go.Finest("UnWatch(%s)", name)
	delete(e.watchedFiles, name)
}

func (e *Editor) observeFiles() {
	for {
		select {
		case ev := <-e.watcher.Event:
			if ev.IsModify() {
				e.watchLock.Lock()
				f, exist := e.watchedFiles[ev.Name]
				e.watchLock.Unlock()
				if exist {
					f.Reload()
				}
			}
		case err := <-e.watcher.Error:
			log4go.Error("error:", err)
		}
	}
}

func newWatcher() (w *fsnotify.Watcher) {
	w, err := fsnotify.NewWatcher()
	if err != nil {
		log4go.Error("Could not create watcher due to: %v", err)
	}
	return
}
<commit_msg>backend: oops, need to lock/unlock watchLock to avoid race condition<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"code.google.com/p/log4go"
	"fmt"
	"github.com/howeyc/fsnotify"
	"github.com/limetext/lime/backend/loaders"
	. "github.com/limetext/lime/backend/util"
	. "github.com/quarnster/util/text"
	"runtime"
	"runtime/debug"
	"sync"
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	GetEditor()
}

type (
	Editor struct {
		HasSettings
		windows       []*Window
		active_window *Window
		loginput      bool
		cmdhandler    commandHandler
		keyBindings   KeyBindings
		console       *View
		frontend      Frontend
		clipboard     string
		keyInput      chan (KeyPress)
		watcher       *fsnotify.Watcher
		watchedFiles  map[string]Watched
		watchLock     sync.Mutex
	}

	// The Frontend interface defines the API
	// for functionality that is frontend specific.
	Frontend interface {
		// Probe the frontend for the currently
		// visible region of the given view.
		VisibleRegion(v *View) Region

		// Make the frontend show the specified region of the
		// given view.
		Show(v *View, r Region)

		// Sets the status message shown in the status bar
		StatusMessage(string)

		// Displays an error message to the usser
		ErrorMessage(string)

		// Displays a message dialog to the user
		MessageDialog(string)

		// Displays an ok / cancel dialog to the user.
		// "okname" if provided will be used as the text
		// instead of "Ok" for the ok button.
		// Returns true when ok was pressed, and false when
		// cancel was pressed.
		OkCancelDialog(msg string, okname string) bool
	}
	myLogWriter struct {
		log chan string
	}
	DummyFrontend struct {
		m sync.Mutex
		// Default return value for OkCancelDialog
		defaultAction bool
	}
)

const (
	LIME_USER_PACKAGES_PATH = "../../3rdparty/bundles/"
	LIME_USER_PACKETS_PATH  = "../../3rdparty/bundles/User/"
	LIME_DEFAULTS_PATH      = "../../backend/packages/Default/"
)

func (h *DummyFrontend) SetDefaultAction(action bool) {
	h.m.Lock()
	defer h.m.Unlock()
	h.defaultAction = action
}
func (h *DummyFrontend) StatusMessage(msg string) { log4go.Info(msg) }
func (h *DummyFrontend) ErrorMessage(msg string)  { log4go.Error(msg) }
func (h *DummyFrontend) MessageDialog(msg string) { log4go.Info(msg) }
func (h *DummyFrontend) OkCancelDialog(msg string, button string) bool {
	log4go.Info(msg)
	h.m.Lock()
	defer h.m.Unlock()
	return h.defaultAction
}
func (h *DummyFrontend) Show(v *View, r Region)       {}
func (h *DummyFrontend) VisibleRegion(v *View) Region { return Region{} }

func newMyLogWriter() *myLogWriter {
	ret := &myLogWriter{make(chan string, 100)}
	go ret.handle()
	return ret
}

func (m *myLogWriter) handle() {
	for fl := range m.log {
		c := GetEditor().Console()
		f := fmt.Sprintf("%08d %d %s", c.Buffer().Size(), len(fl), fl)
		e := c.BeginEdit()
		c.Insert(e, c.Buffer().Size(), f)
		c.EndEdit(e)
	}
}

func (m *myLogWriter) LogWrite(rec *log4go.LogRecord) {
	p := Prof.Enter("log")
	defer p.Exit()
	fl := log4go.FormatLogRecord(log4go.FORMAT_DEFAULT, rec)
	m.log <- fl
}

func (m *myLogWriter) Close() {
	fmt.Println("Closing...")
	close(m.log)
}

var (
	ed  *Editor
	edl sync.Mutex
)

func GetEditor() *Editor {
	edl.Lock()
	defer edl.Unlock()
	if ed == nil {
		ed = &Editor{
			cmdhandler: commandHandler{
				ApplicationCommands: make(appcmd),
				TextCommands:        make(textcmd),
				WindowCommands:      make(wndcmd),
				verbose:             true,
			},
			frontend: &DummyFrontend{},
			console: &View{
				buffer:  NewBuffer(),
				scratch: true,
			},
			keyInput:     make(chan KeyPress, 32),
			watcher:      newWatcher(),
			watchedFiles: make(map[string]Watched),
		}
		ed.console.Settings().Set("is_widget", true)
		ed.Settings() // Just to initialize it
		log4go.Global.Close()
		log4go.Global.AddFilter("console", log4go.DEBUG, newMyLogWriter())
		go ed.inputthread()
		go ed.observeFiles()
	}
	return ed
}

func (e *Editor) Frontend() Frontend {
	return e.frontend
}

func (e *Editor) SetFrontend(f Frontend) {
	e.frontend = f
}

func (e *Editor) Init() {
	ed.loadKeybindings()
	ed.loadSettings()
}

func (e *Editor) loadKeybinding(pkg *packet) {
	var bindings KeyBindings
	if err := loaders.LoadJSON(pkg.Get().([]byte), &bindings); err != nil {
		log4go.Error(err)
	} else {
		log4go.Info("Loaded %s", pkg.Name())
		e.Watch(NewWatchedPackage(pkg))
	}
	e.keyBindings.merge(&bindings)
}

func (e *Editor) loadKeybindings() {
	for _, p := range packets.filter("keymap") {
		e.loadKeybinding(p)
	}
}

func (e *Editor) loadSetting(pkg *packet) {
	if err := loaders.LoadJSON(pkg.Get().([]byte), e.Settings()); err != nil {
		log4go.Error(err)
	} else {
		log4go.Info("Loaded %s", pkg.Name())
		e.Watch(NewWatchedPackage(pkg))
	}
}

func (e *Editor) loadSettings() {
	for _, p := range packets.filter("setting") {
		e.loadSetting(p)
	}
}

func (e *Editor) PackagesPath() string {
	return "../../3rdparty/bundles/"
}

func (e *Editor) Console() *View {
	return e.console
}

func (e *Editor) Windows() []*Window {
	edl.Lock()
	defer edl.Unlock()
	ret := make([]*Window, len(e.windows))
	copy(ret, e.windows)
	return ret
}

func (e *Editor) SetActiveWindow(w *Window) {
	e.active_window = w
}

func (e *Editor) ActiveWindow() *Window {
	return e.active_window
}

func (e *Editor) Watcher() *fsnotify.Watcher {
	return e.watcher
}

func (e *Editor) NewWindow() *Window {
	edl.Lock()
	e.windows = append(e.windows, &Window{})
	w := e.windows[len(e.windows)-1]
	edl.Unlock()
	w.Settings().SetParent(e)
	ed.SetActiveWindow(w)
	OnNewWindow.Call(w)
	return w
}

func (e *Editor) remove(w *Window) {
	edl.Lock()
	defer edl.Unlock()
	for i, ww := range e.windows {
		if w == ww {
			end := len(e.windows) - 1
			if i != end {
				copy(e.windows[i:], e.windows[i+1:])
			}
			e.windows = e.windows[:end]
			return
		}
	}
	log4go.Error("Wanted to remove window %+v, but it doesn't appear to be a child of this editor", w)
}

func (e *Editor) Arch() string {
	return runtime.GOARCH
}

func (e *Editor) Platform() string {
	return runtime.GOOS
}

func (e *Editor) Version() string {
	return "0"
}

func (e *Editor) CommandHandler() CommandHandler {
	return &e.cmdhandler
}

func (e *Editor) HandleInput(kp KeyPress) {
	e.keyInput <- kp
}

func (e *Editor) inputthread() {
	pc := 0
	var lastBindings KeyBindings
	doinput := func(kp KeyPress) {
		defer func() {
			if r := recover(); r != nil {
				log4go.Error("Panic in inputthread: %v\n%s", r, string(debug.Stack()))
				if pc > 0 {
					panic(r)
				}
				pc++
			}
		}()
		p := Prof.Enter("hi")
		defer p.Exit()

		lvl := log4go.FINE
		if e.loginput {
			lvl++
		}
		log4go.Logf(lvl, "Key: %v", kp)
		if lastBindings.keyOff == 0 {
			lastBindings = e.keyBindings
		}
	try_again:
		possible_actions := lastBindings.Filter(kp)
		lastBindings = possible_actions

		// TODO?
		var (
			wnd *Window
			v   *View
		)
		if wnd = e.ActiveWindow(); wnd != nil {
			v = wnd.ActiveView()
		}

		if action := possible_actions.Action(v); action != nil {
			p2 := Prof.Enter("hi.perform")
			// TODO: what's the command precedence?
			if c := e.cmdhandler.TextCommands[action.Command]; c != nil {
				if err := e.CommandHandler().RunTextCommand(v, action.Command, action.Args); err != nil {
					log4go.Debug("Couldn't run textcommand: %s", err)
				}
			} else if c := e.cmdhandler.WindowCommands[action.Command]; c != nil {
				if err := e.CommandHandler().RunWindowCommand(wnd, action.Command, action.Args); err != nil {
					log4go.Debug("Couldn't run windowcommand: %s", err)
				}
			} else if err := e.CommandHandler().RunApplicationCommand(action.Command, action.Args); err != nil {
				log4go.Debug("Couldn't run applicationcommand: %s", err)
			}
			p2.Exit()
		} else if possible_actions.keyOff > 1 {
			lastBindings = e.keyBindings
			goto try_again
		} else if kp.IsCharacter() {
			p2 := Prof.Enter("hi.character")
			log4go.Finest("kp: %v, pos: %v", kp, possible_actions)
			if err := e.CommandHandler().RunTextCommand(v, "insert", Args{"characters": string(rune(kp.Key))}); err != nil {
				log4go.Debug("Couldn't run textcommand: %s", err)
			}
			p2.Exit()
		}
	}
	for kp := range e.keyInput {
		doinput(kp)
	}
}

func (e *Editor) LogInput(l bool) {
	e.loginput = l
}

func (e *Editor) LogCommands(l bool) {
	e.cmdhandler.log = l
}

func (e *Editor) RunCommand(name string, args Args) {
	e.CommandHandler().RunApplicationCommand(name, args)
}

func (e *Editor) SetClipboard(n string) {
	e.clipboard = n
}

func (e *Editor) GetClipboard() string {
	return e.clipboard
}

func (e *Editor) Watch(file Watched) {
	log4go.Finest("Watch(%v)", file)
	if err := e.watcher.Watch(file.Name()); err != nil {
		log4go.Error("Could not watch file: %v", err)
	} else {
		e.watchLock.Lock()
		defer e.watchLock.Unlock()
		e.watchedFiles[file.Name()] = file
	}
}

func (e *Editor) UnWatch(name string) {
	if err := e.watcher.RemoveWatch(name); err != nil {
		log4go.Error("Couldn't unwatch file: %v", err)
	}
	log4go.Finest("UnWatch(%s)", name)
	e.watchLock.Lock()
	defer e.watchLock.Unlock()
	delete(e.watchedFiles, name)
}

func (e *Editor) observeFiles() {
	for {
		select {
		case ev := <-e.watcher.Event:
			if ev.IsModify() {
				e.watchLock.Lock()
				f, exist := e.watchedFiles[ev.Name]
				e.watchLock.Unlock()
				if exist {
					f.Reload()
				}
			}
		case err := <-e.watcher.Error:
			log4go.Error("error:", err)
		}
	}
}

func newWatcher() (w *fsnotify.Watcher) {
	w, err := fsnotify.NewWatcher()
	if err != nil {
		log4go.Error("Could not create watcher due to: %v", err)
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package dashboard contains shared configuration and logic used by various
// pieces of the Go continuous build system.
package dashboard

import "strings"

// Builders are the different build configurations.
// The keys are like "darwin-amd64" or "linux-386-387".
// This map should not be modified by other packages.
var Builders = map[string]BuildConfig{}

// A BuildConfig describes how to run a VM-based builder.
type BuildConfig struct {
	// Name is the unique name of the builder, in the form of
	// "darwin-386" or "linux-amd64-race".
	Name string

	VMImage     string // e.g. "openbsd-amd64-56"
	machineType string // optional GCE instance type
	Go14URL     string // URL to built Go 1.4 tar.gz
	buildletURL string // optional override buildlet URL

	RegularDisk bool // if true, use spinning disk instead of SSD

	env []string // extra environment ("key=value") pairs
}

func (c *BuildConfig) Env() []string { return append([]string(nil), c.env...) }

func (c *BuildConfig) GOOS() string { return c.Name[:strings.Index(c.Name, "-")] }

func (c *BuildConfig) GOARCH() string {
	arch := c.Name[strings.Index(c.Name, "-")+1:]
	i := strings.Index(arch, "-")
	if i == -1 {
		return arch
	}
	return arch[:i]
}

// BuildletBinaryURL returns the public URL of this builder's buildlet.
func (c *BuildConfig) BuildletBinaryURL() string {
	if c.buildletURL != "" {
		return c.buildletURL
	}
	return "http://storage.googleapis.com/go-builder-data/buildlet." + c.GOOS() + "-" + c.GOARCH()
}

// AllScript returns the relative path to the operating system's script to
// do the build and run its standard set of tests.
// Example values are "src/all.bash", "src/all.bat", "src/all.rc".
func (c *BuildConfig) AllScript() string {
	if strings.HasSuffix(c.Name, "-race") {
		if strings.HasPrefix(c.Name, "windows-") {
			return "src/race.bat"
		}
		return "src/race.bash"
	}
	if strings.HasPrefix(c.Name, "windows-") {
		return "src/all.bat"
	}
	if strings.HasPrefix(c.Name, "plan9-") {
		return "src/all.rc"
	}
	if strings.HasPrefix(c.Name, "nacl-") {
		return "src/nacltest.bash"
	}
	return "src/all.bash"
}

// MakeScript returns the relative path to the operating system's script to
// do the build.
// Example values are "src/make.bash", "src/make.bat", "src/make.rc".
func (c *BuildConfig) MakeScript() string {
	if strings.HasPrefix(c.Name, "windows-") {
		return "src/make.bat"
	}
	if strings.HasPrefix(c.Name, "plan9-") {
		return "src/make.rc"
	}
	return "src/make.bash"
}

// GorootFinal returns the default install location for
// releases for this platform.
func (c *BuildConfig) GorootFinal() string {
	if strings.HasPrefix(c.Name, "windows-") {
		return "c:\\go"
	}
	return "/usr/local/go"
}

// MachineType returns the GCE machine type to use for this builder.
func (c *BuildConfig) MachineType() string {
	if v := c.machineType; v != "" {
		return v
	}
	return "n1-highcpu-2"
}

func init() {
	addBuilder(BuildConfig{
		Name:        "freebsd-amd64-gce93",
		VMImage:     "freebsd-amd64-gce93",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
	})
	addBuilder(BuildConfig{
		Name:        "freebsd-amd64-gce101",
		VMImage:     "freebsd-amd64-gce101",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
		env:         []string{"CC=clang"},
	})
	addBuilder(BuildConfig{
		Name:        "freebsd-amd64-race",
		VMImage:     "freebsd-amd64-gce101",
		machineType: "n1-highcpu-4",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
		env:         []string{"CC=clang"},
	})
	addBuilder(BuildConfig{
		Name:        "freebsd-386-gce101",
		VMImage:     "freebsd-amd64-gce101",
		machineType: "n1-highcpu-2",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.freebsd-amd64",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
		// TODO(bradfitz): setting GOHOSTARCH=386 should work
		// to eliminate some unnecessary work (it works on
		// Linux), but fails on FreeBSD with:
		//   ##### ../misc/cgo/testso
		//   Shared object "libcgosotest.so" not found, required by "main"
		// Maybe this is a clang thing? We'll see when we do linux clang too.
		env: []string{"GOARCH=386", "CC=clang"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-386",
		VMImage:     "linux-buildlet-std",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOARCH=386", "GOHOSTARCH=386"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-386-387",
		VMImage:     "linux-buildlet-std",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOARCH=386", "GOHOSTARCH=386", "GO386=387"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64",
		VMImage: "linux-buildlet-std",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64-nocgo",
		VMImage: "linux-buildlet-std",
		env: []string{
			"GOROOT_BOOTSTRAP=/go1.4",
			"CGO_ENABLED=0",
			// This USER=root was required for Docker-based builds but probably isn't required
			// in the VM anymore, since the buildlet probably already has this in its environment.
			// (It was required because without cgo, it couldn't find the username)
			"USER=root",
		},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64-noopt",
		VMImage: "linux-buildlet-std",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4", "GO_GCFLAGS=-N -l"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-amd64-race",
		VMImage:     "linux-buildlet-std",
		machineType: "n1-highcpu-4",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-386-clang",
		VMImage:     "linux-buildlet-clang",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "CC=/usr/bin/clang", "GOHOSTARCH=386"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64-clang",
		VMImage: "linux-buildlet-clang",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4", "CC=/usr/bin/clang"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-386-sid",
		VMImage:     "linux-buildlet-sid",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOHOSTARCH=386"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64-sid",
		VMImage: "linux-buildlet-sid",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-arm-qemu",
		VMImage: "linux-buildlet-arm",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4", "IN_QEMU=1"},
	})
	addBuilder(BuildConfig{
		Name:        "nacl-386",
		VMImage:     "linux-buildlet-nacl",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOOS=nacl", "GOARCH=386", "GOHOSTOS=linux", "GOHOSTARCH=amd64"},
	})
	addBuilder(BuildConfig{
		Name:        "nacl-amd64p32",
		VMImage:     "linux-buildlet-nacl",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOOS=nacl", "GOARCH=amd64p32", "GOHOSTOS=linux", "GOHOSTARCH=amd64"},
	})
	addBuilder(BuildConfig{
		Name:        "openbsd-amd64-gce56",
		VMImage:     "openbsd-amd64-56",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-openbsd-amd64.tar.gz",
	})
	addBuilder(BuildConfig{
		Name:        "openbsd-386-gce56",
		VMImage:     "openbsd-386-56",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-openbsd-386.tar.gz",
	})
	addBuilder(BuildConfig{
		Name:    "plan9-386-gcepartial",
		VMImage: "plan9-386-v2",
		Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-plan9-386.tar.gz",
		// It's named "partial" because the buildlet sets
		// GOTESTONLY=std to stop after the "go test std"
		// tests because it's so slow otherwise.
		env: []string{"GOTESTONLY=std"},

		// We *were* using n1-standard-1 because Plan 9 can only
		// reliably use a single CPU. Using 2 or 4 and we see
		// test failures. See:
		//    https://golang.org/issue/8393
		//    https://golang.org/issue/9491
		// n1-standard-1 has 3.6 GB of memory which WAS (see below)
		// overkill (userspace probably only sees 2GB anyway),
		// but it's the cheapest option. And plenty to keep
		// our ~250 MB of inputs+outputs in its ramfs.
		//
		// But the docs says "For the n1 series of machine
		// types, a virtual CPU is implemented as a single
		// hyperthread on a 2.6GHz Intel Sandy Bridge Xeon or
		// Intel Ivy Bridge Xeon (or newer) processor. This
		// means that the n1-standard-2 machine type will see
		// a whole physical core."
		//
		// ... so we used n1-highcpu-2 (1.80 RAM, still
		// plenty), just so we can get 1 whole core for the
		// single-core Plan 9. It will see 2 virtual cores and
		// only use 1, but we hope that 1 will be more powerful
		// and we'll stop timing out on tests.
		//
		// But then with the toolchain conversion to Go and
		// using ramfs, it turns out we need more memory
		// anyway, so use n1-highcpu-4.
		machineType: "n1-highcpu-4",
	})
	addBuilder(BuildConfig{
		Name:        "windows-amd64-gce",
		VMImage:     "windows-buildlet-v2",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-windows-amd64.tar.gz",
		RegularDisk: true,
		env:         []string{"GOARCH=amd64", "GOHOSTARCH=amd64"},
	})
	addBuilder(BuildConfig{
		Name:        "windows-amd64-race",
		VMImage:     "windows-buildlet-v2",
		machineType: "n1-highcpu-4",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-windows-amd64.tar.gz",
		RegularDisk: true,
		env:         []string{"GOARCH=amd64", "GOHOSTARCH=amd64"},
	})
	addBuilder(BuildConfig{
		Name:        "windows-386-gce",
		VMImage:     "windows-buildlet-v2",
		machineType: "n1-highcpu-2",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.windows-amd64",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-windows-386.tar.gz",
		RegularDisk: true,
		env:         []string{"GOARCH=386", "GOHOSTARCH=386"},
	})
}

func addBuilder(c BuildConfig) {
	if c.Name == "" {
		panic("empty name")
	}
	if _, dup := Builders[c.Name]; dup {
		panic("dup name")
	}
	if c.VMImage == "" {
		panic("empty VMImage")
	}
	Builders[c.Name] = c
}
<commit_msg>dashboard: switch to -v2 of the nacl image<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package dashboard contains shared configuration and logic used by various
// pieces of the Go continuous build system.
package dashboard

import "strings"

// Builders are the different build configurations.
// The keys are like "darwin-amd64" or "linux-386-387".
// This map should not be modified by other packages.
var Builders = map[string]BuildConfig{}

// A BuildConfig describes how to run a VM-based builder.
type BuildConfig struct {
	// Name is the unique name of the builder, in the form of
	// "darwin-386" or "linux-amd64-race".
	Name string

	VMImage     string // e.g. "openbsd-amd64-56"
	machineType string // optional GCE instance type
	Go14URL     string // URL to built Go 1.4 tar.gz
	buildletURL string // optional override buildlet URL

	RegularDisk bool // if true, use spinning disk instead of SSD

	env []string // extra environment ("key=value") pairs
}

func (c *BuildConfig) Env() []string { return append([]string(nil), c.env...) }

func (c *BuildConfig) GOOS() string { return c.Name[:strings.Index(c.Name, "-")] }

func (c *BuildConfig) GOARCH() string {
	arch := c.Name[strings.Index(c.Name, "-")+1:]
	i := strings.Index(arch, "-")
	if i == -1 {
		return arch
	}
	return arch[:i]
}

// BuildletBinaryURL returns the public URL of this builder's buildlet.
func (c *BuildConfig) BuildletBinaryURL() string {
	if c.buildletURL != "" {
		return c.buildletURL
	}
	return "http://storage.googleapis.com/go-builder-data/buildlet." + c.GOOS() + "-" + c.GOARCH()
}

// AllScript returns the relative path to the operating system's script to
// do the build and run its standard set of tests.
// Example values are "src/all.bash", "src/all.bat", "src/all.rc".
func (c *BuildConfig) AllScript() string {
	if strings.HasSuffix(c.Name, "-race") {
		if strings.HasPrefix(c.Name, "windows-") {
			return "src/race.bat"
		}
		return "src/race.bash"
	}
	if strings.HasPrefix(c.Name, "windows-") {
		return "src/all.bat"
	}
	if strings.HasPrefix(c.Name, "plan9-") {
		return "src/all.rc"
	}
	if strings.HasPrefix(c.Name, "nacl-") {
		return "src/nacltest.bash"
	}
	return "src/all.bash"
}

// MakeScript returns the relative path to the operating system's script to
// do the build.
// Example values are "src/make.bash", "src/make.bat", "src/make.rc".
func (c *BuildConfig) MakeScript() string {
	if strings.HasPrefix(c.Name, "windows-") {
		return "src/make.bat"
	}
	if strings.HasPrefix(c.Name, "plan9-") {
		return "src/make.rc"
	}
	return "src/make.bash"
}

// GorootFinal returns the default install location for
// releases for this platform.
func (c *BuildConfig) GorootFinal() string {
	if strings.HasPrefix(c.Name, "windows-") {
		return "c:\\go"
	}
	return "/usr/local/go"
}

// MachineType returns the GCE machine type to use for this builder.
func (c *BuildConfig) MachineType() string {
	if v := c.machineType; v != "" {
		return v
	}
	return "n1-highcpu-2"
}

func init() {
	addBuilder(BuildConfig{
		Name:        "freebsd-amd64-gce93",
		VMImage:     "freebsd-amd64-gce93",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
	})
	addBuilder(BuildConfig{
		Name:        "freebsd-amd64-gce101",
		VMImage:     "freebsd-amd64-gce101",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
		env:         []string{"CC=clang"},
	})
	addBuilder(BuildConfig{
		Name:        "freebsd-amd64-race",
		VMImage:     "freebsd-amd64-gce101",
		machineType: "n1-highcpu-4",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
		env:         []string{"CC=clang"},
	})
	addBuilder(BuildConfig{
		Name:        "freebsd-386-gce101",
		VMImage:     "freebsd-amd64-gce101",
		machineType: "n1-highcpu-2",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.freebsd-amd64",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
		// TODO(bradfitz): setting GOHOSTARCH=386 should work
		// to eliminate some unnecessary work (it works on
		// Linux), but fails on FreeBSD with:
		//   ##### ../misc/cgo/testso
		//   Shared object "libcgosotest.so" not found, required by "main"
		// Maybe this is a clang thing? We'll see when we do linux clang too.
		env: []string{"GOARCH=386", "CC=clang"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-386",
		VMImage:     "linux-buildlet-std",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOARCH=386", "GOHOSTARCH=386"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-386-387",
		VMImage:     "linux-buildlet-std",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOARCH=386", "GOHOSTARCH=386", "GO386=387"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64",
		VMImage: "linux-buildlet-std",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64-nocgo",
		VMImage: "linux-buildlet-std",
		env: []string{
			"GOROOT_BOOTSTRAP=/go1.4",
			"CGO_ENABLED=0",
			// This USER=root was required for Docker-based builds but probably isn't required
			// in the VM anymore, since the buildlet probably already has this in its environment.
			// (It was required because without cgo, it couldn't find the username)
			"USER=root",
		},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64-noopt",
		VMImage: "linux-buildlet-std",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4", "GO_GCFLAGS=-N -l"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-amd64-race",
		VMImage:     "linux-buildlet-std",
		machineType: "n1-highcpu-4",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-386-clang",
		VMImage:     "linux-buildlet-clang",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "CC=/usr/bin/clang", "GOHOSTARCH=386"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64-clang",
		VMImage: "linux-buildlet-clang",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4", "CC=/usr/bin/clang"},
	})
	addBuilder(BuildConfig{
		Name:        "linux-386-sid",
		VMImage:     "linux-buildlet-sid",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOHOSTARCH=386"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-amd64-sid",
		VMImage: "linux-buildlet-sid",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4"},
	})
	addBuilder(BuildConfig{
		Name:    "linux-arm-qemu",
		VMImage: "linux-buildlet-arm",
		env:     []string{"GOROOT_BOOTSTRAP=/go1.4", "IN_QEMU=1"},
	})
	addBuilder(BuildConfig{
		Name:        "nacl-386",
		VMImage:     "linux-buildlet-nacl-v2",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOOS=nacl", "GOARCH=386", "GOHOSTOS=linux", "GOHOSTARCH=amd64"},
	})
	addBuilder(BuildConfig{
		Name:        "nacl-amd64p32",
		VMImage:     "linux-buildlet-nacl-v2",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
		env:         []string{"GOROOT_BOOTSTRAP=/go1.4", "GOOS=nacl", "GOARCH=amd64p32", "GOHOSTOS=linux", "GOHOSTARCH=amd64"},
	})
	addBuilder(BuildConfig{
		Name:        "openbsd-amd64-gce56",
		VMImage:     "openbsd-amd64-56",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-openbsd-amd64.tar.gz",
	})
	addBuilder(BuildConfig{
		Name:        "openbsd-386-gce56",
		VMImage:     "openbsd-386-56",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-openbsd-386.tar.gz",
	})
	addBuilder(BuildConfig{
		Name:    "plan9-386-gcepartial",
		VMImage: "plan9-386-v2",
		Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-plan9-386.tar.gz",
		// It's named "partial" because the buildlet sets
		// GOTESTONLY=std to stop after the "go test std"
		// tests because it's so slow otherwise.
		env: []string{"GOTESTONLY=std"},

		// We *were* using n1-standard-1 because Plan 9 can only
		// reliably use a single CPU. Using 2 or 4 and we see
		// test failures. See:
		//    https://golang.org/issue/8393
		//    https://golang.org/issue/9491
		// n1-standard-1 has 3.6 GB of memory which WAS (see below)
		// overkill (userspace probably only sees 2GB anyway),
		// but it's the cheapest option. And plenty to keep
		// our ~250 MB of inputs+outputs in its ramfs.
		//
		// But the docs says "For the n1 series of machine
		// types, a virtual CPU is implemented as a single
		// hyperthread on a 2.6GHz Intel Sandy Bridge Xeon or
		// Intel Ivy Bridge Xeon (or newer) processor. This
		// means that the n1-standard-2 machine type will see
		// a whole physical core."
		//
		// ... so we used n1-highcpu-2 (1.80 RAM, still
		// plenty), just so we can get 1 whole core for the
		// single-core Plan 9. It will see 2 virtual cores and
		// only use 1, but we hope that 1 will be more powerful
		// and we'll stop timing out on tests.
		//
		// But then with the toolchain conversion to Go and
		// using ramfs, it turns out we need more memory
		// anyway, so use n1-highcpu-4.
		machineType: "n1-highcpu-4",
	})
	addBuilder(BuildConfig{
		Name:        "windows-amd64-gce",
		VMImage:     "windows-buildlet-v2",
		machineType: "n1-highcpu-2",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-windows-amd64.tar.gz",
		RegularDisk: true,
		env:         []string{"GOARCH=amd64", "GOHOSTARCH=amd64"},
	})
	addBuilder(BuildConfig{
		Name:        "windows-amd64-race",
		VMImage:     "windows-buildlet-v2",
		machineType: "n1-highcpu-4",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-windows-amd64.tar.gz",
		RegularDisk: true,
		env:         []string{"GOARCH=amd64", "GOHOSTARCH=amd64"},
	})
	addBuilder(BuildConfig{
		Name:        "windows-386-gce",
		VMImage:     "windows-buildlet-v2",
		machineType: "n1-highcpu-2",
		buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.windows-amd64",
		Go14URL:     "https://storage.googleapis.com/go-builder-data/go1.4-windows-386.tar.gz",
		RegularDisk: true,
		env:         []string{"GOARCH=386", "GOHOSTARCH=386"},
	})
}

func addBuilder(c BuildConfig) {
	if c.Name == "" {
		panic("empty name")
	}
	if _, dup := Builders[c.Name]; dup {
		panic("dup name")
	}
	if c.VMImage == "" {
		panic("empty VMImage")
	}
	Builders[c.Name] = c
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"

	"github.com/TykTechnologies/tyk/config"
)

type NodeResponseOK struct {
	Status  string
	Message map[string]string
	Nonce   string
}

type DashboardServiceSender interface {
	Init() error
	Register() error
	DeRegister() error
	StartBeating() error
	StopBeating()
}

type HTTPDashboardHandler struct {
	RegistrationEndpoint   string
	DeRegistrationEndpoint string
	HeartBeatEndpoint      string
	Secret                 string

	heartBeatStopSentinel bool
}

func reLogin() {
	if !config.Global.UseDBAppConfigs {
		return
	}

	log.WithFields(logrus.Fields{
		"prefix": "main",
	}).Info("Registering node (again).")
	DashService.StopBeating()
	if err := DashService.DeRegister(); err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Error("Could not deregister: ", err)
	}

	time.Sleep(30 * time.Second)

	if err := DashService.Register(); err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Error("Could not register: ", err)
	} else {
		go DashService.StartBeating()
	}

	log.WithFields(logrus.Fields{
		"prefix": "main",
	}).Info("Recovering configurations, reloading...")
	var wg sync.WaitGroup
	wg.Add(1)
	reloadURLStructure(wg.Done)
	wg.Wait()
}

func (h *HTTPDashboardHandler) Init() error {
	h.RegistrationEndpoint = buildConnStr("/register/node")
	h.DeRegistrationEndpoint = buildConnStr("/system/node")
	h.HeartBeatEndpoint = buildConnStr("/register/ping")

	h.Secret = config.Global.NodeSecret
	return nil
}

func (h *HTTPDashboardHandler) Register() error {
	req := h.newRequest(h.RegistrationEndpoint)

	c := &http.Client{Timeout: 5 * time.Second}
	resp, err := c.Do(req)

	if err != nil {
		log.Errorf("Request failed with error %v; retrying in 5s", err)
		time.Sleep(time.Second * 5)
		return h.Register()
	} else if resp != nil && resp.StatusCode != 200 {
		log.Errorf("Response failed with code %d; retrying in 5s", resp.StatusCode)
		time.Sleep(time.Second * 5)
		return h.Register()
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the NodeID
	var found bool
	NodeID, found = val.Message["NodeID"]
	if !found {
		log.Error("Failed to register node, retrying in 5s")
		time.Sleep(time.Second * 5)
		return h.Register()
	}

	log.WithFields(logrus.Fields{
		"prefix": "dashboard",
		"id":     NodeID,
	}).Info("Node registered")

	// Set the nonce
	ServiceNonce = val.Nonce
	log.Debug("Registration Finished: Nonce Set: ", ServiceNonce)

	return nil
}

func (h *HTTPDashboardHandler) StartBeating() error {
	for !h.heartBeatStopSentinel {
		if err := h.sendHeartBeat(); err != nil {
			log.Warning(err)
		}
		time.Sleep(time.Second * 2)
	}

	log.Info("Stopped Heartbeat")
	h.heartBeatStopSentinel = false
	return nil
}

func (h *HTTPDashboardHandler) StopBeating() {
	h.heartBeatStopSentinel = true
}

func (h *HTTPDashboardHandler) newRequest(endpoint string) *http.Request {
	req, err := http.NewRequest("GET", endpoint, nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("authorization", h.Secret)
	req.Header.Set("x-tyk-hostname", hostDetails.Hostname)
	return req
}

func (h *HTTPDashboardHandler) sendHeartBeat() error {
	req := h.newRequest(h.HeartBeatEndpoint)
	req.Header.Set("x-tyk-nodeid", NodeID)
	req.Header.Set("x-tyk-nonce", ServiceNonce)

	c := &http.Client{Timeout: 5 * time.Second}
	resp, err := c.Do(req)
	if err != nil || resp.StatusCode != 200 {
		return errors.New("dashboard is down? Heartbeat is failing")
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the nonce
	ServiceNonce = val.Nonce
	//log.Debug("Heartbeat Finished: Nonce Set: ", ServiceNonce)

	return nil
}

func (h *HTTPDashboardHandler) DeRegister() error {
	req := h.newRequest(h.DeRegistrationEndpoint)

	req.Header.Set("x-tyk-nodeid", NodeID)
	req.Header.Set("x-tyk-nonce", ServiceNonce)

	c := &http.Client{Timeout: 5 * time.Second}
	resp, err := c.Do(req)
	if err != nil || resp.StatusCode != 200 {
		return fmt.Errorf("request failed with code %d and error %v", resp.StatusCode, err)
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the nonce
	ServiceNonce = val.Nonce
	log.Info("De-registered.")

	return nil
}
<commit_msg>Add warning when node_secret is missing (#1460)<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"

	"github.com/TykTechnologies/tyk/config"
)

type NodeResponseOK struct {
	Status  string
	Message map[string]string
	Nonce   string
}

type DashboardServiceSender interface {
	Init() error
	Register() error
	DeRegister() error
	StartBeating() error
	StopBeating()
}

type HTTPDashboardHandler struct {
	RegistrationEndpoint   string
	DeRegistrationEndpoint string
	HeartBeatEndpoint      string
	Secret                 string

	heartBeatStopSentinel bool
}

func reLogin() {
	if !config.Global.UseDBAppConfigs {
		return
	}

	log.WithFields(logrus.Fields{
		"prefix": "main",
	}).Info("Registering node (again).")
	DashService.StopBeating()
	if err := DashService.DeRegister(); err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Error("Could not deregister: ", err)
	}

	time.Sleep(30 * time.Second)

	if err := DashService.Register(); err != nil {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Error("Could not register: ", err)
	} else {
		go DashService.StartBeating()
	}

	log.WithFields(logrus.Fields{
		"prefix": "main",
	}).Info("Recovering configurations, reloading...")
	var wg sync.WaitGroup
	wg.Add(1)
	reloadURLStructure(wg.Done)
	wg.Wait()
}

func (h *HTTPDashboardHandler) Init() error {
	h.RegistrationEndpoint = buildConnStr("/register/node")
	h.DeRegistrationEndpoint = buildConnStr("/system/node")
	h.HeartBeatEndpoint = buildConnStr("/register/ping")
	if h.Secret = config.Global.NodeSecret; h.Secret == "" {
		log.WithFields(logrus.Fields{
			"prefix": "main",
		}).Fatal("Node secret is not set, required for dashboard connection")
	}
	return nil
}

func (h *HTTPDashboardHandler) Register() error {
	req := h.newRequest(h.RegistrationEndpoint)

	c := &http.Client{Timeout: 5 * time.Second}
	resp, err := c.Do(req)

	if err != nil {
		log.Errorf("Request failed with error %v; retrying in 5s", err)
		time.Sleep(time.Second * 5)
		return h.Register()
	} else if resp != nil && resp.StatusCode != 200 {
		log.Errorf("Response failed with code %d; retrying in 5s", resp.StatusCode)
		time.Sleep(time.Second * 5)
		return h.Register()
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the NodeID
	var found bool
	NodeID, found = val.Message["NodeID"]
	if !found {
		log.Error("Failed to register node, retrying in 5s")
		time.Sleep(time.Second * 5)
		return h.Register()
	}

	log.WithFields(logrus.Fields{
		"prefix": "dashboard",
		"id":     NodeID,
	}).Info("Node registered")

	// Set the nonce
	ServiceNonce = val.Nonce
	log.Debug("Registration Finished: Nonce Set: ", ServiceNonce)

	return nil
}

func (h *HTTPDashboardHandler) StartBeating() error {
	for !h.heartBeatStopSentinel {
		if err := h.sendHeartBeat(); err != nil {
			log.Warning(err)
		}
		time.Sleep(time.Second * 2)
	}

	log.Info("Stopped Heartbeat")
	h.heartBeatStopSentinel = false
	return nil
}

func (h *HTTPDashboardHandler) StopBeating() {
	h.heartBeatStopSentinel = true
}

func (h *HTTPDashboardHandler) newRequest(endpoint string) *http.Request {
	req, err := http.NewRequest("GET", endpoint, nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("authorization", h.Secret)
	req.Header.Set("x-tyk-hostname", hostDetails.Hostname)
	return req
}

func (h *HTTPDashboardHandler) sendHeartBeat() error {
	req := h.newRequest(h.HeartBeatEndpoint)
	req.Header.Set("x-tyk-nodeid", NodeID)
	req.Header.Set("x-tyk-nonce", ServiceNonce)

	c := &http.Client{Timeout: 5 * time.Second}
	resp, err := c.Do(req)
	if err != nil || resp.StatusCode != 200 {
		return errors.New("dashboard is down? Heartbeat is failing")
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the nonce
	ServiceNonce = val.Nonce
	//log.Debug("Heartbeat Finished: Nonce Set: ", ServiceNonce)

	return nil
}

func (h *HTTPDashboardHandler) DeRegister() error {
	req := h.newRequest(h.DeRegistrationEndpoint)

	req.Header.Set("x-tyk-nodeid", NodeID)
	req.Header.Set("x-tyk-nonce", ServiceNonce)

	c := &http.Client{Timeout: 5 * time.Second}
	resp, err := c.Do(req)
	if err != nil || resp.StatusCode != 200 {
		return fmt.Errorf("request failed with code %d and error %v", resp.StatusCode, err)
	}

	defer resp.Body.Close()
	val := NodeResponseOK{}
	if err := json.NewDecoder(resp.Body).Decode(&val); err != nil {
		return err
	}

	// Set the nonce
	ServiceNonce = val.Nonce
	log.Info("De-registered.")

	return nil
}
<|endoftext|>


<commit_before>/*
 *    Copyright (C) 2017 Christian Muehlhaeuser
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 */

// Package bees is Beehive's central module system.
package bees

import (
	"errors"
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

// Placeholders is an array of Placeholder.
type Placeholders []Placeholder

// Placeholder used by ins & outs of a bee.
type Placeholder struct {
	Name  string
	Type  string
	Value interface{}
}

// SetValue sets a value in the Placeholder slice.
func (ph *Placeholders) SetValue(name string, _type string, value interface{}) {
	if ph.Value(name) == nil {
		p := Placeholder{
			Name:  name,
			Type:  _type,
			Value: value,
		}
		*ph = append(*ph, p)
	} else {
		for i := 0; i < len(*ph); i++ {
			if (*ph)[i].Name == name {
				(*ph)[i].Type = _type
				(*ph)[i].Value = value
			}
		}
	}
}

// Value retrieves a value from a Placeholder slice.
func (ph Placeholders) Value(name string) interface{} {
	for _, p := range ph {
		if p.Name == name {
			return p.Value
		}
	}

	return nil
}

// Bind a value from a Placeholder slice.
func (ph Placeholders) Bind(name string, dst interface{}) error {
	v := ph.Value(name)
	if v == nil {
		return errors.New("Placeholder with name " + name + " not found")
	}

	return ConvertValue(v, dst)
}

// ConvertValue tries to convert v to dst.
func ConvertValue(v interface{}, dst interface{}) error {
	switch d := dst.(type) {
	case *string:
		switch vt := v.(type) {
		case string:
			*d = vt
		case []string:
			*d = strings.Join(vt, ",")
		case bool:
			*d = strconv.FormatBool(vt)
		case int64:
			*d = strconv.FormatInt(vt, 10)
		case float64:
			*d = strconv.FormatFloat(vt, 'f', -1, 64)
		case int:
			*d = strconv.FormatInt(int64(vt), 10)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for string conversion", reflect.TypeOf(vt)))
		}

	case *[]string:
		switch vt := v.(type) {
		case []interface{}:
			*d = []string{}
			for _, v := range vt {
				*d = append(*d, v.(string))
			}
		case []string:
			*d = vt
		case string:
			*d = strings.Split(vt, ",")
		default:
			panic(fmt.Sprintf("Unhandled type %+v for []string conversion", reflect.TypeOf(vt)))
		}

	case *bool:
		switch vt := v.(type) {
		case bool:
			*d = vt
		case string:
			vt = strings.ToLower(vt)
			if vt == "true" || vt == "on" || vt == "yes" || vt == "1" || vt == "t" {
				*d = true
			}
		case int64:
			*d = vt > 0
		case int:
			*d = vt > 0
		case uint64:
			*d = vt > 0
		case uint:
			*d = vt > 0
		case float64:
			*d = vt > 0
		default:
			panic(fmt.Sprintf("Unhandled type %+v for bool conversion", reflect.TypeOf(vt)))
		}

	case *float64:
		switch vt := v.(type) {
		case int64:
			*d = float64(vt)
		case int32:
			*d = float64(vt)
		case int16:
			*d = float64(vt)
		case int8:
			*d = float64(vt)
		case int:
			*d = float64(vt)
		case uint64:
			*d = float64(vt)
		case uint32:
			*d = float64(vt)
		case uint16:
			*d = float64(vt)
		case uint8:
			*d = float64(vt)
		case uint:
			*d = float64(vt)
		case float64:
			*d = vt
		case float32:
			*d = float64(vt)
		case string:
			x, _ := strconv.Atoi(vt)
			*d = float64(x)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for float64 conversion", reflect.TypeOf(vt)))
		}

	case *int:
		switch vt := v.(type) {
		case int64:
			*d = int(vt)
		case int32:
			*d = int(vt)
		case int16:
			*d = int(vt)
		case int8:
			*d = int(vt)
		case int:
			*d = vt
		case uint64:
			*d = int(vt)
		case uint32:
			*d = int(vt)
		case uint16:
			*d = int(vt)
		case uint8:
			*d = int(vt)
		case uint:
			*d = int(vt)
		case float64:
			*d = int(vt)
		case float32:
			*d = int(vt)
		case string:
			*d, _ = strconv.Atoi(vt)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for int conversion", reflect.TypeOf(vt)))
		}

	case *url.Values:
		switch vt := v.(type) {
		case string:
			*d, _ = url.ParseQuery(vt)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for url.Values conversion", reflect.TypeOf(vt)))
		}

	default:
		panic(fmt.Sprintf("Unhandled dst type %+v", reflect.TypeOf(dst)))
	}

	return nil
}
<commit_msg>Add support for time.Time conversions<commit_after>/*
 *    Copyright (C) 2017 Christian Muehlhaeuser
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 */

// Package bees is Beehive's central module system.
package bees

import (
	"errors"
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
)

// Placeholders is an array of Placeholder.
type Placeholders []Placeholder

// Placeholder used by ins & outs of a bee.
type Placeholder struct {
	Name  string
	Type  string
	Value interface{}
}

// SetValue sets a value in the Placeholder slice.
func (ph *Placeholders) SetValue(name string, _type string, value interface{}) {
	if ph.Value(name) == nil {
		p := Placeholder{
			Name:  name,
			Type:  _type,
			Value: value,
		}
		*ph = append(*ph, p)
	} else {
		for i := 0; i < len(*ph); i++ {
			if (*ph)[i].Name == name {
				(*ph)[i].Type = _type
				(*ph)[i].Value = value
			}
		}
	}
}

// Value retrieves a value from a Placeholder slice.
func (ph Placeholders) Value(name string) interface{} {
	for _, p := range ph {
		if p.Name == name {
			return p.Value
		}
	}

	return nil
}

// Bind a value from a Placeholder slice.
func (ph Placeholders) Bind(name string, dst interface{}) error {
	v := ph.Value(name)
	if v == nil {
		return errors.New("Placeholder with name " + name + " not found")
	}

	return ConvertValue(v, dst)
}

// ConvertValue tries to convert v to dst.
func ConvertValue(v interface{}, dst interface{}) error {
	switch d := dst.(type) {
	case *string:
		switch vt := v.(type) {
		case string:
			*d = vt
		case []string:
			*d = strings.Join(vt, ",")
		case bool:
			*d = strconv.FormatBool(vt)
		case int64:
			*d = strconv.FormatInt(vt, 10)
		case float64:
			*d = strconv.FormatFloat(vt, 'f', -1, 64)
		case int:
			*d = strconv.FormatInt(int64(vt), 10)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for string conversion", reflect.TypeOf(vt)))
		}

	case *[]string:
		switch vt := v.(type) {
		case []interface{}:
			*d = []string{}
			for _, v := range vt {
				*d = append(*d, v.(string))
			}
		case []string:
			*d = vt
		case string:
			*d = strings.Split(vt, ",")
		default:
			panic(fmt.Sprintf("Unhandled type %+v for []string conversion", reflect.TypeOf(vt)))
		}

	case *bool:
		switch vt := v.(type) {
		case bool:
			*d = vt
		case string:
			vt = strings.ToLower(vt)
			if vt == "true" || vt == "on" || vt == "yes" || vt == "1" || vt == "t" {
				*d = true
			}
		case int64:
			*d = vt > 0
		case int:
			*d = vt > 0
		case uint64:
			*d = vt > 0
		case uint:
			*d = vt > 0
		case float64:
			*d = vt > 0
		default:
			panic(fmt.Sprintf("Unhandled type %+v for bool conversion", reflect.TypeOf(vt)))
		}

	case *float64:
		switch vt := v.(type) {
		case int64:
			*d = float64(vt)
		case int32:
			*d = float64(vt)
		case int16:
			*d = float64(vt)
		case int8:
			*d = float64(vt)
		case int:
			*d = float64(vt)
		case uint64:
			*d = float64(vt)
		case uint32:
			*d = float64(vt)
		case uint16:
			*d = float64(vt)
		case uint8:
			*d = float64(vt)
		case uint:
			*d = float64(vt)
		case float64:
			*d = vt
		case float32:
			*d = float64(vt)
		case string:
			x, _ := strconv.Atoi(vt)
			*d = float64(x)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for float64 conversion", reflect.TypeOf(vt)))
		}

	case *int:
		switch vt := v.(type) {
		case int64:
			*d = int(vt)
		case int32:
			*d = int(vt)
		case int16:
			*d = int(vt)
		case int8:
			*d = int(vt)
		case int:
			*d = vt
		case uint64:
			*d = int(vt)
		case uint32:
			*d = int(vt)
		case uint16:
			*d = int(vt)
		case uint8:
			*d = int(vt)
		case uint:
			*d = int(vt)
		case float64:
			*d = int(vt)
		case float32:
			*d = int(vt)
		case string:
			*d, _ = strconv.Atoi(vt)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for int conversion", reflect.TypeOf(vt)))
		}

	case *time.Time:
		switch vt := v.(type) {
		case time.Time:
			*d = vt
		case int:
			*d = time.Unix(int64(vt), 0)
		case int64:
			*d = time.Unix(vt, 0)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for time.Time conversion", reflect.TypeOf(vt)))
		}

	case *url.Values:
		switch vt := v.(type) {
		case string:
			*d, _ = url.ParseQuery(vt)
		default:
			panic(fmt.Sprintf("Unhandled type %+v for url.Values conversion", reflect.TypeOf(vt)))
		}

	default:
		panic(fmt.Sprintf("Unhandled dst type %+v", reflect.TypeOf(dst)))
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/newrelic/go-agent"
	"github.com/op/go-logging"
)

var logger = logging.MustGetLogger("hashi-ui")
var defaultConfig = DefaultConfig()

func startLogging(logLevel string) {
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)

	format := logging.MustStringFormatter(
		`%{color}%{time:15:04:05.000} %{shortfile} ▶ %{level:.5s} %{color:reset} %{message}`,
	)
	logBackendFormatted := logging.NewBackendFormatter(logBackend, format)

	logBackendFormattedAndLeveled := logging.AddModuleLevel(logBackendFormatted)

	realLogLevel, err := logging.LogLevel(strings.ToUpper(logLevel))
	if err != nil {
		fmt.Printf("%s (%s)", err, logLevel)
		os.Exit(1)
	}

	logBackendFormattedAndLeveled.SetLevel(realLogLevel, "")

	logging.SetBackend(logBackendFormattedAndLeveled)
}

// Parse the env and cli flags and store the outcome in a Config struct
func (c *Config) Parse() {
	flag.Parse()

	ParseAppEnvConfig(c)
	ParseAppFlagConfig(c)

	ParseNomadEnvConfig(c)
	ParseNomadFlagConfig(c)

	ParseConsulEnvConfig(c)
	ParseConsulFlagConfig(c)

	ParseNewRelicConfig(c)
}

func main() {
	cfg := DefaultConfig()
	cfg.Parse()

	config := newrelic.NewConfig(cfg.NewRelicAppName, cfg.NewRelicLicense)
	config.Logger = newrelic.NewLogger(os.Stdout)

	if cfg.NewRelicAppName == "" || cfg.NewRelicLicense == "" {
		config.Enabled = false
	}

	_, err := newrelic.NewApplication(config)
	if err != nil {
		logger.Error(err)
		os.Exit(1)
	}

	startLogging(cfg.LogLevel)

	logger.Infof("-----------------------------------------------------------------------------")
	logger.Infof("|                             NOMAD UI                                      |")
	logger.Infof("-----------------------------------------------------------------------------")
	logger.Infof("| listen-address  	: http://%-43s |", cfg.ListenAddress)
	logger.Infof("| proxy-address   	: %-50s |", cfg.ProxyAddress)
	logger.Infof("| log-level       	: %-50s |", cfg.LogLevel)

	if cfg.NewRelicAppName != "" && cfg.NewRelicLicense != "" {
		logger.Infof("| newrelic-app-name   : %-50s |", cfg.NewRelicAppName)
		logger.Infof("| newrelic-license    : %-50s |", strings.Repeat("*", len(cfg.NewRelicLicense)))
	}

	// Nomad
	logger.Infof("| nomad-enable     	: %-50t |", cfg.NomadEnable)
	if cfg.NomadReadOnly {
		logger.Infof("| nomad-read-only       : %-50s |", "Yes")
	} else {
		logger.Infof("| nomad-read-only       : %-50s |", "No (Hashi-UI can change Nomad state)")
	}
	logger.Infof("| nomad-address         : %-50s |", cfg.NomadAddress)
	logger.Infof("| nomad-ca-cert         : %-50s |", cfg.NomadCACert)
	logger.Infof("| nomad-client-cert     : %-50s |", cfg.NomadClientCert)
	logger.Infof("| nomad-client-key      : %-50s |", cfg.NomadClientKey)

	// Consul
	logger.Infof("| consul-enable     	: %-50t |", cfg.ConsulEnable)
	if cfg.ConsulReadOnly {
		logger.Infof("| consul-read-only     : %-50s |", "Yes")
	} else {
		logger.Infof("| consul-read-only     : %-50s |", "No (Hashi-UI can change Consul state)")
	}
	logger.Infof("| consul-address       : %-50s |", cfg.ConsulAddress)

	logger.Infof("-----------------------------------------------------------------------------")
	logger.Infof("")

	if !cfg.NomadEnable && !cfg.ConsulEnable {
		logger.Fatal("Please enable at least Consul (--consul-enable) or Nomad (--nomad-enable)")
	}

	myAssetFS := assetFS()
	router := mux.NewRouter()

	if cfg.NomadEnable {
		nomadHub, nomadSuccess := InitializeNomad(cfg)
		if !nomadSuccess {
			logger.Fatalf("Failed to start Nomad hub, please check your configuration")
		}
		logger.Infof("Nomad client successfully initialized")

		router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			logger.Infof("Redirecting / to /nomad")
			http.Redirect(w, r, "/nomad", 302)
		})

		router.HandleFunc("/ws/nomad", nomadHub.Handler)
		router.HandleFunc("/ws/nomad/{region}", nomadHub.Handler)
		router.HandleFunc("/nomad/{region}/download/{path:.*}", nomadHub.downloadFile)
	}

	if cfg.ConsulEnable {
		consulHub, consulSuccess := InitializeConsul(cfg)
		if !consulSuccess {
			logger.Fatalf("Failed to start Consul hub, please check your configuration")
		}

		if !cfg.NomadEnable {
			router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
				logger.Infof("Redirecting / to /consul")
				http.Redirect(w, r, "/consul", 302)
			})
		}

		logger.Infof("Consul client successfully initialized")
		router.HandleFunc("/ws/consul", consulHub.Handler)
		router.HandleFunc("/ws/consul/{region}", consulHub.Handler)
	}

	router.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		responseFile := "/index.html"

		if idx := strings.Index(r.URL.Path, "static/"); idx != -1 {
			responseFile = r.URL.Path[idx:]
		}

		if idx := strings.Index(r.URL.Path, "favicon.png"); idx != -1 {
			responseFile = "/favicon.png"
		}

		if idx := strings.Index(r.URL.Path, "config.js"); idx != -1 {
			response := make([]string, 0)
			response = append(response, fmt.Sprintf("window.CONSUL_ENABLED=%s", strconv.FormatBool(cfg.ConsulEnable)))
			response = append(response, fmt.Sprintf("window.CONSUL_READ_ONLY=%s", strconv.FormatBool(cfg.ConsulReadOnly)))

			response = append(response, fmt.Sprintf("window.NOMAD_ENABLED=%s", strconv.FormatBool(cfg.NomadEnable)))
			response = append(response, fmt.Sprintf("window.NOMAD_READ_ONLY=%s", strconv.FormatBool(cfg.NomadReadOnly)))

			enabledServices := make([]string, 0)
			if cfg.ConsulEnable {
				enabledServices = append(enabledServices, "'consul'")
			}
			if cfg.NomadEnable {
				enabledServices = append(enabledServices, "'nomad'")
			}

			response = append(response, fmt.Sprintf("window.ENABLED_SERVICES=[%s]", strings.Join(enabledServices, ",")))
			response = append(response, fmt.Sprintf("window.NOMAD_ADDR=\"%s\"", cfg.NomadAddress))

			var endpointURL string
			if cfg.ProxyAddress != "" {
				endpointURL = fmt.Sprintf("\"%s\"", strings.TrimSuffix(cfg.ProxyAddress, "/"))
			} else {
				endpointURL = "document.location.hostname + ':' + (window.NOMAD_ENDPOINT_PORT || document.location.port)"
			}

			response = append(response, fmt.Sprintf("window.NOMAD_ENDPOINT=%s", endpointURL))

			w.Header().Set("Content-Type", "application/javascript")
			w.Write([]byte(strings.Join(response, "\n")))
			return
		}

		if bs, assetErr := myAssetFS.Open(responseFile); err != nil {
			logger.Errorf("%s: %s", responseFile, assetErr)
		} else {
			http.ServeContent(w, r, responseFile[1:], time.Now(), bs)
		}
	})

	logger.Infof("Listening ...")
	err = http.ListenAndServe(cfg.ListenAddress, router)
	if err != nil {
		logger.Fatal(err)
	}
}
<commit_msg>Adding startLogging for nomad-skip-verify<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/newrelic/go-agent"
	"github.com/op/go-logging"
)

var logger = logging.MustGetLogger("hashi-ui")
var defaultConfig = DefaultConfig()

func startLogging(logLevel string) {
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)

	format := logging.MustStringFormatter(
		`%{color}%{time:15:04:05.000} %{shortfile} ▶ %{level:.5s} %{color:reset} %{message}`,
	)
	logBackendFormatted := logging.NewBackendFormatter(logBackend, format)

	logBackendFormattedAndLeveled := logging.AddModuleLevel(logBackendFormatted)

	realLogLevel, err := logging.LogLevel(strings.ToUpper(logLevel))
	if err != nil {
		fmt.Printf("%s (%s)", err, logLevel)
		os.Exit(1)
	}

	logBackendFormattedAndLeveled.SetLevel(realLogLevel, "")

	logging.SetBackend(logBackendFormattedAndLeveled)
}

// Parse the env and cli flags and store the outcome in a Config struct
func (c *Config) Parse() {
	flag.Parse()

	ParseAppEnvConfig(c)
	ParseAppFlagConfig(c)

	ParseNomadEnvConfig(c)
	ParseNomadFlagConfig(c)

	ParseConsulEnvConfig(c)
	ParseConsulFlagConfig(c)

	ParseNewRelicConfig(c)
}

func main() {
	cfg := DefaultConfig()
	cfg.Parse()

	config := newrelic.NewConfig(cfg.NewRelicAppName, cfg.NewRelicLicense)
	config.Logger = newrelic.NewLogger(os.Stdout)

	if cfg.NewRelicAppName == "" || cfg.NewRelicLicense == "" {
		config.Enabled = false
	}

	_, err := newrelic.NewApplication(config)
	if err != nil {
		logger.Error(err)
		os.Exit(1)
	}

	startLogging(cfg.LogLevel)

	logger.Infof("-----------------------------------------------------------------------------")
	logger.Infof("|                             NOMAD UI                                      |")
	logger.Infof("-----------------------------------------------------------------------------")
	logger.Infof("| listen-address  	: http://%-43s |", cfg.ListenAddress)
	logger.Infof("| proxy-address   	: %-50s |", cfg.ProxyAddress)
	logger.Infof("| log-level       	: %-50s |", cfg.LogLevel)

	if cfg.NewRelicAppName != "" && cfg.NewRelicLicense != "" {
		logger.Infof("| newrelic-app-name   : %-50s |", cfg.NewRelicAppName)
		logger.Infof("| newrelic-license    : %-50s |", strings.Repeat("*", len(cfg.NewRelicLicense)))
	}

	// Nomad
	logger.Infof("| nomad-enable     	: %-50t |", cfg.NomadEnable)
	if cfg.NomadReadOnly {
		logger.Infof("| nomad-read-only       : %-50s |", "Yes")
	} else {
		logger.Infof("| nomad-read-only       : %-50s |", "No (Hashi-UI can change Nomad state)")
	}
	logger.Infof("| nomad-address         : %-50s |", cfg.NomadAddress)
	logger.Infof("| nomad-ca-cert         : %-50s |", cfg.NomadCACert)
	logger.Infof("| nomad-client-cert     : %-50s |", cfg.NomadClientCert)
	logger.Infof("| nomad-client-key      : %-50s |", cfg.NomadClientKey)
	logger.Infof("| nomad-skip-verify   	: %-50t |", cfg.NomadSkipVerify)
	if cfg.NomadSkipVerify {
		logger.Infof("| nomad-skip-verify     : %-50s |", "Yes")
	} else {
		logger.Infof("| nomad-skip-verify     : %-50s |", "No")
	}

	// Consul
	logger.Infof("| consul-enable     	: %-50t |", cfg.ConsulEnable)
	if cfg.ConsulReadOnly {
		logger.Infof("| consul-read-only     : %-50s |", "Yes")
	} else {
		logger.Infof("| consul-read-only     : %-50s |", "No (Hashi-UI can change Consul state)")
	}
	logger.Infof("| consul-address       : %-50s |", cfg.ConsulAddress)

	logger.Infof("-----------------------------------------------------------------------------")
	logger.Infof("")

	if !cfg.NomadEnable && !cfg.ConsulEnable {
		logger.Fatal("Please enable at least Consul (--consul-enable) or Nomad (--nomad-enable)")
	}

	myAssetFS := assetFS()
	router := mux.NewRouter()

	if cfg.NomadEnable {
		nomadHub, nomadSuccess := InitializeNomad(cfg)
		if !nomadSuccess {
			logger.Fatalf("Failed to start Nomad hub, please check your configuration")
		}
		logger.Infof("Nomad client successfully initialized")

		router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			logger.Infof("Redirecting / to /nomad")
			http.Redirect(w, r, "/nomad", 302)
		})

		router.HandleFunc("/ws/nomad", nomadHub.Handler)
		router.HandleFunc("/ws/nomad/{region}", nomadHub.Handler)
		router.HandleFunc("/nomad/{region}/download/{path:.*}", nomadHub.downloadFile)
	}

	if cfg.ConsulEnable {
		consulHub, consulSuccess := InitializeConsul(cfg)
		if !consulSuccess {
			logger.Fatalf("Failed to start Consul hub, please check your configuration")
		}

		if !cfg.NomadEnable {
			router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
				logger.Infof("Redirecting / to /consul")
				http.Redirect(w, r, "/consul", 302)
			})
		}

		logger.Infof("Consul client successfully initialized")
		router.HandleFunc("/ws/consul", consulHub.Handler)
		router.HandleFunc("/ws/consul/{region}", consulHub.Handler)
	}

	router.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		responseFile := "/index.html"

		if idx := strings.Index(r.URL.Path, "static/"); idx != -1 {
			responseFile = r.URL.Path[idx:]
		}

		if idx := strings.Index(r.URL.Path, "favicon.png"); idx != -1 {
			responseFile = "/favicon.png"
		}

		if idx := strings.Index(r.URL.Path, "config.js"); idx != -1 {
			response := make([]string, 0)
			response = append(response, fmt.Sprintf("window.CONSUL_ENABLED=%s", strconv.FormatBool(cfg.ConsulEnable)))
			response = append(response, fmt.Sprintf("window.CONSUL_READ_ONLY=%s", strconv.FormatBool(cfg.ConsulReadOnly)))

			response = append(response, fmt.Sprintf("window.NOMAD_ENABLED=%s", strconv.FormatBool(cfg.NomadEnable)))
			response = append(response, fmt.Sprintf("window.NOMAD_READ_ONLY=%s", strconv.FormatBool(cfg.NomadReadOnly)))

			enabledServices := make([]string, 0)
			if cfg.ConsulEnable {
				enabledServices = append(enabledServices, "'consul'")
			}
			if cfg.NomadEnable {
				enabledServices = append(enabledServices, "'nomad'")
			}

			response = append(response, fmt.Sprintf("window.ENABLED_SERVICES=[%s]", strings.Join(enabledServices, ",")))
			response = append(response, fmt.Sprintf("window.NOMAD_ADDR=\"%s\"", cfg.NomadAddress))

			var endpointURL string
			if cfg.ProxyAddress != "" {
				endpointURL = fmt.Sprintf("\"%s\"", strings.TrimSuffix(cfg.ProxyAddress, "/"))
			} else {
				endpointURL = "document.location.hostname + ':' + (window.NOMAD_ENDPOINT_PORT || document.location.port)"
			}

			response = append(response, fmt.Sprintf("window.NOMAD_ENDPOINT=%s", endpointURL))

			w.Header().Set("Content-Type", "application/javascript")
			w.Write([]byte(strings.Join(response, "\n")))
			return
		}

		if bs, assetErr := myAssetFS.Open(responseFile); err != nil {
			logger.Errorf("%s: %s", responseFile, assetErr)
		} else {
			http.ServeContent(w, r, responseFile[1:], time.Now(), bs)
		}
	})

	logger.Infof("Listening ...")
	err = http.ListenAndServe(cfg.ListenAddress, router)
	if err != nil {
		logger.Fatal(err)
	}
}
<|endoftext|>