<commit_before>package common_test

import (
	"strings"

	"github.com/apcera/nats"
	. "github.com/cloudfoundry/gorouter/common"
	"github.com/cloudfoundry/gorouter/test_util"
	"github.com/cloudfoundry/gosteno"
	"github.com/cloudfoundry/yagnats"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/pivotal-golang/localip"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	"github.com/cloudfoundry/gunk/natsrunner"
)

type MarshalableValue struct {
	Value map[string]string
}

func (m *MarshalableValue) MarshalJSON() ([]byte, error) {
	return json.Marshal(m.Value)
}

var _ = Describe("Component", func() {
	var (
		component *VcapComponent
		varz      *Varz
	)

	BeforeEach(func() {
		port, err := localip.LocalPort()
		Expect(err).ToNot(HaveOccurred())

		varz = &Varz{
			GenericVarz: GenericVarz{
				Host:        fmt.Sprintf("127.0.0.1:%d", port),
				Credentials: []string{"username", "password"},
			},
		}
		component = &VcapComponent{
			Varz: varz,
		}
	})

	It("prevents unauthorized access", func() {
		path := "/test"

		component.InfoRoutes = map[string]json.Marshaler{
			path: &MarshalableValue{Value: map[string]string{"key": "value"}},
		}
		serveComponent(component)

		req := buildGetRequest(component, path)
		code, _, _ := doGetRequest(req)
		Expect(code).To(Equal(401))

		req = buildGetRequest(component, path)
		req.SetBasicAuth("username", "incorrect-password")
		code, _, _ = doGetRequest(req)
		Expect(code).To(Equal(401))

		req = buildGetRequest(component, path)
		req.SetBasicAuth("incorrect-username", "password")
		code, _, _ = doGetRequest(req)
		Expect(code).To(Equal(401))
	})

	It("allows multiple info routes", func() {
		path1 := "/test1"
		path2 := "/test2"

		component.InfoRoutes = map[string]json.Marshaler{
			path1: &MarshalableValue{Value: map[string]string{"key": "value1"}},
			path2: &MarshalableValue{Value: map[string]string{"key": "value2"}},
		}
		serveComponent(component)

		//access path1
		req := buildGetRequest(component, path1)
		req.SetBasicAuth("username", "password")

		code, header, body := doGetRequest(req)
		Expect(code).To(Equal(200))
		Expect(header.Get("Content-Type")).To(Equal("application/json"))
		Expect(body).To(Equal(`{"key":"value1"}` + "\n"))

		//access path2
		req = buildGetRequest(component, path2)
		req.SetBasicAuth("username", "password")

		code, header, body = doGetRequest(req)
		Expect(code).To(Equal(200))
		Expect(header.Get("Content-Type")).To(Equal("application/json"))
		Expect(body).To(Equal(`{"key":"value2"}` + "\n"))
	})

	It("allows authorized access", func() {
		path := "/test"

		component.InfoRoutes = map[string]json.Marshaler{
			path: &MarshalableValue{Value: map[string]string{"key": "value"}},
		}
		serveComponent(component)

		req := buildGetRequest(component, path)
		req.SetBasicAuth("username", "password")

		code, header, body := doGetRequest(req)
		Expect(code).To(Equal(200))
		Expect(header.Get("Content-Type")).To(Equal("application/json"))
		Expect(body).To(Equal(`{"key":"value"}` + "\n"))
	})

	It("updates the uptime statistic", func() {
		stringMap := make(map[string]interface{})
		path := "/varz"
		component.Varz.Type = "Router"
		startComponent(component)

		time.Sleep(2 * time.Second)
		req := buildGetRequest(component, path)
		req.SetBasicAuth("username", "password")

		code, header, body := doGetRequest(req)
		Expect(code).To(Equal(200))
		Expect(header.Get("Content-Type")).To(Equal("application/json"))

		err := json.Unmarshal([]byte(body), &stringMap)
		Expect(err).NotTo(HaveOccurred())

		duration := stringMap["uptime"].(string)
		Expect(duration).NotTo(Equal(`"uptime":"0d:0h:0m:0s"`))
	})

	It("returns 404 for non existent paths", func() {
		serveComponent(component)

		req := buildGetRequest(component, "/non-existent-path")
		req.SetBasicAuth("username", "password")

		code, _, _ := doGetRequest(req)
		Expect(code).To(Equal(404))
	})

	Describe("Register", func() {
		var mbusClient yagnats.NATSConn
		var natsRunner *natsrunner.NATSRunner
		var logger *gosteno.Logger
		var sink *gosteno.TestingSink
		BeforeEach(func() {
			natsPort := test_util.NextAvailPort()
			natsRunner = natsrunner.NewNATSRunner(int(natsPort))
			natsRunner.Start()
			mbusClient = natsRunner.MessageBus

			sink = gosteno.NewTestingSink()
			c := &gosteno.Config{
				Sinks: []gosteno.Sink{
					sink,
				},
				Level:     gosteno.LOG_INFO,
				Codec:     gosteno.NewJsonCodec(),
				EnableLOC: true,
			}
			gosteno.Init(c)
			logger = gosteno.NewLogger("test")
		})

		AfterEach(func() {
			natsRunner.Stop()
		})

		It("subscribes to vcap.component.discover", func() {
			var done bool
			members := []string{
				"type",
				"index",
				"host",
				"credentials",
				"start",
				"uuid",
				"uptime",
				"num_cores",
				"mem",
				"cpu",
				"log_counts",
			}

			component.Varz.Type = "TestType"
			component.Logger = logger

			err := component.Start()
			Expect(err).ToNot(HaveOccurred())

			err = component.Register(mbusClient)
			Expect(err).ToNot(HaveOccurred())

			_, err = mbusClient.Subscribe("subject", func(msg *nats.Msg) {
				defer GinkgoRecover()
				data := make(map[string]interface{})
				err := json.Unmarshal(msg.Data, &data)
				Expect(err).ToNot(HaveOccurred())

				for _, key := range members {
					_, ok := data[key]
					Expect(ok).To(BeTrue())
				}

				done = true
			})
			Expect(err).ToNot(HaveOccurred())

			err = mbusClient.PublishRequest("vcap.component.discover", "subject", []byte(""))
			Expect(err).ToNot(HaveOccurred())

			Eventually(func() bool {
				return done
			}).Should(BeTrue())
		})

		It("publishes to vcap.component.announce on start-up", func() {
			var done bool
			members := []string{
				"type",
				"index",
				"host",
				"credentials",
				"start",
				"uuid",
				"uptime",
				"num_cores",
				"mem",
				"cpu",
				"log_counts",
			}

			component.Varz.Type = "TestType"
			component.Logger = logger

			err := component.Start()
			Expect(err).ToNot(HaveOccurred())

			_, err = mbusClient.Subscribe("vcap.component.announce", func(msg *nats.Msg) {
				defer GinkgoRecover()
				data := make(map[string]interface{})
				err := json.Unmarshal(msg.Data, &data)
				Expect(err).ToNot(HaveOccurred())

				for _, key := range members {
					_, ok := data[key]
					Expect(ok).To(BeTrue())
				}

				done = true
			})
			Expect(err).ToNot(HaveOccurred())

			err = component.Register(mbusClient)
			Expect(err).ToNot(HaveOccurred())

			Eventually(func() bool {
				return done
			}).Should(BeTrue())
		})

		It("can handle an empty reply in the subject", func() {
			component.Varz.Type = "TestType"
			component.Logger = logger

			err := component.Start()
			Expect(err).ToNot(HaveOccurred())

			err = component.Register(mbusClient)
			Expect(err).ToNot(HaveOccurred())

			err = mbusClient.PublishRequest("vcap.component.discover", "", []byte(""))
			Expect(err).ToNot(HaveOccurred())

			Eventually(func() bool {
				found := false
				for _, r := range sink.Records() {
					if strings.Contains(r.Message, "Received message with empty reply on subject") {
						found = true
					}
				}
				return found
			}).Should(BeTrue())

			err = mbusClient.PublishRequest("vcap.component.discover", "reply", []byte("hi"))
			Expect(err).ToNot(HaveOccurred())
		})
	})
})

func startComponent(component *VcapComponent) {
	err := component.Start()
	Expect(err).ToNot(HaveOccurred())

	for i := 0; i < 5; i++ {
		conn, err := net.DialTimeout("tcp", component.Varz.Host, 1*time.Second)
		if err == nil {
			conn.Close()
			return
		}
		time.Sleep(100 * time.Millisecond)
	}

	Expect(true).ToNot(BeTrue(), "Could not connect to vcap.Component")
}

func serveComponent(component *VcapComponent) {
	component.ListenAndServe()

	for i := 0; i < 5; i++ {
		conn, err := net.DialTimeout("tcp", component.Varz.Host, 1*time.Second)
		if err == nil {
			conn.Close()
			return
		}
		time.Sleep(100 * time.Millisecond)
	}

	Expect(true).ToNot(BeTrue(), "Could not connect to vcap.Component")
}

func buildGetRequest(component *VcapComponent, path string) *http.Request {
	req, err := http.NewRequest("GET", "http://"+component.Varz.Host+path, nil)
	Expect(err).ToNot(HaveOccurred())
	return req
}

func doGetRequest(req *http.Request) (int, http.Header, string) {
	var client http.Client
	var resp *http.Response
	var err error

	resp, err = client.Do(req)
	Expect(err).ToNot(HaveOccurred())
	Expect(resp).ToNot(BeNil())

	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	Expect(err).ToNot(HaveOccurred())

	return resp.StatusCode, resp.Header, string(body)
}
<commit_msg>Fix data race in test setup<commit_after>package common_test

import (
	"strings"

	"github.com/apcera/nats"
	. "github.com/cloudfoundry/gorouter/common"
	"github.com/cloudfoundry/gorouter/test_util"
	"github.com/cloudfoundry/gosteno"
	"github.com/cloudfoundry/yagnats"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/pivotal-golang/localip"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	"github.com/cloudfoundry/gunk/natsrunner"
)

type MarshalableValue struct {
	Value map[string]string
}

func (m *MarshalableValue) MarshalJSON() ([]byte, error) {
	return json.Marshal(m.Value)
}

var _ = Describe("Component", func() {
	var (
		component *VcapComponent
		varz      *Varz
	)

	BeforeEach(func() {
		port, err := localip.LocalPort()
		Expect(err).ToNot(HaveOccurred())

		varz = &Varz{
			GenericVarz: GenericVarz{
				Host:        fmt.Sprintf("127.0.0.1:%d", port),
				Credentials: []string{"username", "password"},
			},
		}
		component = &VcapComponent{
			Varz: varz,
		}
	})

	It("prevents unauthorized access", func() {
		path := "/test"

		component.InfoRoutes = map[string]json.Marshaler{
			path: &MarshalableValue{Value: map[string]string{"key": "value"}},
		}
		serveComponent(component)

		req := buildGetRequest(component, path)
		code, _, _ := doGetRequest(req)
		Expect(code).To(Equal(401))

		req = buildGetRequest(component, path)
		req.SetBasicAuth("username", "incorrect-password")
		code, _, _ = doGetRequest(req)
		Expect(code).To(Equal(401))

		req = buildGetRequest(component, path)
		req.SetBasicAuth("incorrect-username", "password")
		code, _, _ = doGetRequest(req)
		Expect(code).To(Equal(401))
	})

	It("allows multiple info routes", func() {
		path1 := "/test1"
		path2 := "/test2"

		component.InfoRoutes = map[string]json.Marshaler{
			path1: &MarshalableValue{Value: map[string]string{"key": "value1"}},
			path2: &MarshalableValue{Value: map[string]string{"key": "value2"}},
		}
		serveComponent(component)

		//access path1
		req := buildGetRequest(component, path1)
		req.SetBasicAuth("username", "password")

		code, header, body := doGetRequest(req)
		Expect(code).To(Equal(200))
		Expect(header.Get("Content-Type")).To(Equal("application/json"))
		Expect(body).To(Equal(`{"key":"value1"}` + "\n"))

		//access path2
		req = buildGetRequest(component, path2)
		req.SetBasicAuth("username", "password")

		code, header, body = doGetRequest(req)
		Expect(code).To(Equal(200))
		Expect(header.Get("Content-Type")).To(Equal("application/json"))
		Expect(body).To(Equal(`{"key":"value2"}` + "\n"))
	})

	It("allows authorized access", func() {
		path := "/test"

		component.InfoRoutes = map[string]json.Marshaler{
			path: &MarshalableValue{Value: map[string]string{"key": "value"}},
		}
		serveComponent(component)

		req := buildGetRequest(component, path)
		req.SetBasicAuth("username", "password")

		code, header, body := doGetRequest(req)
		Expect(code).To(Equal(200))
		Expect(header.Get("Content-Type")).To(Equal("application/json"))
		Expect(body).To(Equal(`{"key":"value"}` + "\n"))
	})

	It("updates the uptime statistic", func() {
		stringMap := make(map[string]interface{})
		path := "/varz"
		component.Varz.Type = "Router"
		startComponent(component)

		time.Sleep(2 * time.Second)
		req := buildGetRequest(component, path)
		req.SetBasicAuth("username", "password")

		code, header, body := doGetRequest(req)
		Expect(code).To(Equal(200))
		Expect(header.Get("Content-Type")).To(Equal("application/json"))

		err := json.Unmarshal([]byte(body), &stringMap)
		Expect(err).NotTo(HaveOccurred())

		duration := stringMap["uptime"].(string)
		Expect(duration).NotTo(Equal(`"uptime":"0d:0h:0m:0s"`))
	})

	It("returns 404 for non existent paths", func() {
		serveComponent(component)

		req := buildGetRequest(component, "/non-existent-path")
		req.SetBasicAuth("username", "password")

		code, _, _ := doGetRequest(req)
		Expect(code).To(Equal(404))
	})

	Describe("Register", func() {
		var mbusClient yagnats.NATSConn
		var natsRunner *natsrunner.NATSRunner
		var logger *gosteno.Logger
		var sink *gosteno.TestingSink
		BeforeEach(func() {
			natsPort := test_util.NextAvailPort()
			natsRunner = natsrunner.NewNATSRunner(int(natsPort))
			natsRunner.Start()
			mbusClient = natsRunner.MessageBus

			sink = gosteno.NewTestingSink()
			c := &gosteno.Config{
				Sinks: []gosteno.Sink{
					sink,
				},
				Level:     gosteno.LOG_INFO,
				Codec:     gosteno.NewJsonCodec(),
				EnableLOC: true,
			}
			gosteno.Init(c)
			logger = gosteno.NewLogger("test")
		})

		AfterEach(func() {
			natsRunner.Stop()
		})

		It("subscribes to vcap.component.discover", func() {
			done := make(chan struct{})
			members := []string{
				"type",
				"index",
				"host",
				"credentials",
				"start",
				"uuid",
				"uptime",
				"num_cores",
				"mem",
				"cpu",
				"log_counts",
			}

			component.Varz.Type = "TestType"
			component.Logger = logger

			err := component.Start()
			Expect(err).ToNot(HaveOccurred())

			err = component.Register(mbusClient)
			Expect(err).ToNot(HaveOccurred())

			_, err = mbusClient.Subscribe("subject", func(msg *nats.Msg) {
				defer GinkgoRecover()
				data := make(map[string]interface{})
				err := json.Unmarshal(msg.Data, &data)
				Expect(err).ToNot(HaveOccurred())

				for _, key := range members {
					_, ok := data[key]
					Expect(ok).To(BeTrue())
				}

				close(done)
			})
			Expect(err).ToNot(HaveOccurred())

			err = mbusClient.PublishRequest("vcap.component.discover", "subject", []byte(""))
			Expect(err).ToNot(HaveOccurred())

			Eventually(done).Should(BeClosed())
		})

		It("publishes to vcap.component.announce on start-up", func() {
			done := make(chan struct{})
			members := []string{
				"type",
				"index",
				"host",
				"credentials",
				"start",
				"uuid",
				"uptime",
				"num_cores",
				"mem",
				"cpu",
				"log_counts",
			}

			component.Varz.Type = "TestType"
			component.Logger = logger

			err := component.Start()
			Expect(err).ToNot(HaveOccurred())

			_, err = mbusClient.Subscribe("vcap.component.announce", func(msg *nats.Msg) {
				defer GinkgoRecover()
				data := make(map[string]interface{})
				err := json.Unmarshal(msg.Data, &data)
				Expect(err).ToNot(HaveOccurred())

				for _, key := range members {
					_, ok := data[key]
					Expect(ok).To(BeTrue())
				}

				close(done)
			})
			Expect(err).ToNot(HaveOccurred())

			err = component.Register(mbusClient)
			Expect(err).ToNot(HaveOccurred())

			Eventually(done).Should(BeClosed())
		})

		It("can handle an empty reply in the subject", func() {
			component.Varz.Type = "TestType"
			component.Logger = logger

			err := component.Start()
			Expect(err).ToNot(HaveOccurred())

			err = component.Register(mbusClient)
			Expect(err).ToNot(HaveOccurred())

			err = mbusClient.PublishRequest("vcap.component.discover", "", []byte(""))
			Expect(err).ToNot(HaveOccurred())

			Eventually(func() bool {
				found := false
				for _, r := range sink.Records() {
					if strings.Contains(r.Message, "Received message with empty reply on subject") {
						found = true
					}
				}
				return found
			}).Should(BeTrue())

			err = mbusClient.PublishRequest("vcap.component.discover", "reply", []byte("hi"))
			Expect(err).ToNot(HaveOccurred())
		})
	})
})

func startComponent(component *VcapComponent) {
	err := component.Start()
	Expect(err).ToNot(HaveOccurred())

	for i := 0; i < 5; i++ {
		conn, err := net.DialTimeout("tcp", component.Varz.Host, 1*time.Second)
		if err == nil {
			conn.Close()
			return
		}
		time.Sleep(100 * time.Millisecond)
	}

	Expect(true).ToNot(BeTrue(), "Could not connect to vcap.Component")
}

func serveComponent(component *VcapComponent) {
	component.ListenAndServe()

	for i := 0; i < 5; i++ {
		conn, err := net.DialTimeout("tcp", component.Varz.Host, 1*time.Second)
		if err == nil {
			conn.Close()
			return
		}
		time.Sleep(100 * time.Millisecond)
	}

	Expect(true).ToNot(BeTrue(), "Could not connect to vcap.Component")
}

func buildGetRequest(component *VcapComponent, path string) *http.Request {
	req, err := http.NewRequest("GET", "http://"+component.Varz.Host+path, nil)
	Expect(err).ToNot(HaveOccurred())
	return req
}

func doGetRequest(req *http.Request) (int, http.Header, string) {
	var client http.Client
	var resp *http.Response
	var err error

	resp, err = client.Do(req)
	Expect(err).ToNot(HaveOccurred())
	Expect(resp).ToNot(BeNil())

	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	Expect(err).ToNot(HaveOccurred())

	return resp.StatusCode, resp.Header, string(body)
}
<|endoftext|>


<commit_before>package logdb

import (
	"fmt"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

const (
	chunkSize  = 256
	firstID    = uint64(1)
	numEntries = 255
)

func TestOneIndexed(t *testing.T) {
	db := assertCreate(t, "one_indexed", chunkSize)
	defer assertClose(t, db)

	assertAppend(t, db, []byte{42})
	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(2), db.NextID())
	assertGet(t, db, 1)
}

func TestAppend(t *testing.T) {
	db := assertCreate(t, "append", chunkSize)
	defer assertClose(t, db)

	vs := make([][]byte, numEntries)
	for i := 0; i < len(vs); i++ {
		vs[i] = []byte(fmt.Sprintf("entry-%v", i))
	}

	for _, v := range vs {
		assertAppend(t, db, v)
	}

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	for i, v := range vs {
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestAppendEntries(t *testing.T) {
	db := assertCreate(t, "append_entries", chunkSize)
	defer assertClose(t, db)

	vs := make([][]byte, numEntries)
	for i := 0; i < len(vs); i++ {
		vs[i] = []byte(fmt.Sprintf("entry-%v", i))
	}

	assertAppendEntries(t, db, vs)

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	for i, v := range vs {
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestAppendValue(t *testing.T) {
	db := assertCreate(t, "append_value", chunkSize)
	defer assertClose(t, db)

	vs := make([]uint64, numEntries)
	for i := 0; i < len(vs); i++ {
		vs[i] = uint64(0x0FDCBA9876543210 | i)
	}

	for _, v := range vs {
		assertAppendValue(t, db, v)
	}

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	for i, v := range vs {
		var v2 uint64
		assertGetValue(t, db, uint64(i+1), &v2)
		assert.Equal(t, v, v2)
	}
}

func TestAppendValues(t *testing.T) {
	db := assertCreate(t, "append_values", chunkSize)
	defer assertClose(t, db)

	vs := make([]interface{}, numEntries)
	for i := 0; i < len(vs); i++ {
		vs[i] = uint64(0x0FDCBA9876543210 | i)
	}

	assertAppendValues(t, db, vs)

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	for i, v := range vs {
		var v2 uint64
		assertGetValue(t, db, uint64(i+1), &v2)
		assert.Equal(t, v, v2)
	}
}

func TestPersist(t *testing.T) {
	db := assertCreate(t, "persist", chunkSize)

	vs := filldb(t, db, numEntries)

	assertClose(t, db)

	db2 := assertOpen(t, "persist")
	defer assertClose(t, db2)

	for i, v := range vs {
		bs := assertGet(t, db2, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestForget(t *testing.T) {
	db := assertCreate(t, "forget", chunkSize)
	defer assertClose(t, db)

	vs := filldb(t, db, numEntries)

	toForget := uint64(50)
	newOldestID := db.OldestID() + toForget
	assertForget(t, db, newOldestID)
	assert.Equal(t, newOldestID, db.OldestID())

	for i, v := range vs {
		if uint64(i)+1 < newOldestID {
			continue
		}
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestRollback(t *testing.T) {
	db := assertCreate(t, "rollback", chunkSize)
	defer assertClose(t, db)

	vs := filldb(t, db, numEntries)

	toRollback := uint64(50)
	newNextID := db.NextID() - toRollback
	assertRollback(t, db, newNextID)
	assert.Equal(t, newNextID, db.NextID())

	for i, v := range vs {
		if uint64(i)+1 == newNextID {
			break
		}
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestTruncate(t *testing.T) {
	db := assertCreate(t, "truncate", chunkSize)
	defer assertClose(t, db)

	vs := filldb(t, db, numEntries)

	toForget := uint64(20)
	toRollback := uint64(30)
	newOldestID := db.OldestID() + toForget
	newNextID := db.NextID() - toRollback
	assertTruncate(t, db, newOldestID, newNextID)
	assert.Equal(t, newOldestID, db.OldestID())
	assert.Equal(t, newNextID, db.NextID())

	for i, v := range vs {
		if uint64(i)+1 < newOldestID {
			continue
		}
		if uint64(i)+1 == newNextID {
			break
		}
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestPersistTruncate(t *testing.T) {
	db := assertCreate(t, "persist_truncate", chunkSize)

	vs := filldb(t, db, numEntries)

	toForget := uint64(20)
	toRollback := uint64(30)
	newOldestID := db.OldestID() + toForget
	newNextID := db.NextID() - toRollback
	assertTruncate(t, db, newOldestID, newNextID)

	assertClose(t, db)

	db2 := assertOpen(t, "persist_truncate")
	defer assertClose(t, db2)

	assert.Equal(t, newOldestID, db2.OldestID())
	assert.Equal(t, newNextID, db2.NextID())

	for i, v := range vs {
		if uint64(i)+1 < newOldestID {
			continue
		}
		if uint64(i)+1 == newNextID {
			break
		}
		bs := assertGet(t, db2, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

/// ASSERTIONS

func assertCreate(t *testing.T, testName string, cSize uint32) LogDB {
	_ = os.RemoveAll("test_db/" + testName)
	db, err := Create("test_db/"+testName, 0, cSize)
	if err != nil {
		t.Fatal(err)
	}
	return db
}

func assertOpen(t *testing.T, testName string) LogDB {
	db, err := Open("test_db/" + testName)
	if err != nil {
		t.Fatal(err)
	}
	return db
}

func assertClose(t *testing.T, db LogDB) {
	if err := db.Close(); err != nil {
		t.Fatal(err)
	}
}

func assertAppend(t *testing.T, db LogDB, entry []byte) {
	if err := db.Append(entry); err != nil {
		t.Fatal(err)
	}
}

func assertAppendValue(t *testing.T, db LogDB, value interface{}) {
	if err := db.AppendValue(value); err != nil {
		t.Fatal(err)
	}
}

func assertAppendEntries(t *testing.T, db LogDB, entries [][]byte) {
	if err := db.AppendEntries(entries); err != nil {
		t.Fatal(err)
	}
}

func assertAppendValues(t *testing.T, db LogDB, values []interface{}) {
	if err := db.AppendValues(values); err != nil {
		t.Fatal(err)
	}
}

func assertGet(t *testing.T, db LogDB, id uint64) []byte {
	b, err := db.Get(id)
	if err != nil {
		t.Fatal(err)
	}
	return b
}

func assertGetValue(t *testing.T, db LogDB, id uint64, data interface{}) {
	if err := db.GetValue(id, data); err != nil {
		t.Fatal(err)
	}
}

func assertForget(t *testing.T, db LogDB, newOldestID uint64) {
	if err := db.Forget(newOldestID); err != nil {
		t.Fatal(err)
	}
}

func assertRollback(t *testing.T, db LogDB, newNextID uint64) {
	if err := db.Rollback(newNextID); err != nil {
		t.Fatal(err)
	}
}

func assertTruncate(t *testing.T, db LogDB, newOldestID, newNextID uint64) {
	if err := db.Truncate(newOldestID, newNextID); err != nil {
		t.Fatal(err)
	}
}

/// HELPERS

func filldb(t *testing.T, db LogDB, num int) [][]byte {
	vs := make([][]byte, num)
	for i := 0; i < num; i++ {
		vs[i] = []byte(fmt.Sprintf("entry-%v", i))
	}

	assertAppendEntries(t, db, vs)

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	return vs
}
<commit_msg>Pick awkward chunk size in tests.<commit_after>package logdb

import (
	"fmt"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

const (
	chunkSize  = 113
	firstID    = uint64(1)
	numEntries = 255
)

func TestOneIndexed(t *testing.T) {
	db := assertCreate(t, "one_indexed", chunkSize)
	defer assertClose(t, db)

	assertAppend(t, db, []byte{42})
	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(2), db.NextID())
	assertGet(t, db, 1)
}

func TestAppend(t *testing.T) {
	db := assertCreate(t, "append", chunkSize)
	defer assertClose(t, db)

	vs := make([][]byte, numEntries)
	for i := 0; i < len(vs); i++ {
		vs[i] = []byte(fmt.Sprintf("entry-%v", i))
	}

	for _, v := range vs {
		assertAppend(t, db, v)
	}

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	for i, v := range vs {
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestAppendEntries(t *testing.T) {
	db := assertCreate(t, "append_entries", chunkSize)
	defer assertClose(t, db)

	vs := make([][]byte, numEntries)
	for i := 0; i < len(vs); i++ {
		vs[i] = []byte(fmt.Sprintf("entry-%v", i))
	}

	assertAppendEntries(t, db, vs)

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	for i, v := range vs {
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestAppendValue(t *testing.T) {
	db := assertCreate(t, "append_value", chunkSize)
	defer assertClose(t, db)

	vs := make([]uint64, numEntries)
	for i := 0; i < len(vs); i++ {
		vs[i] = uint64(0x0FDCBA9876543210 | i)
	}

	for _, v := range vs {
		assertAppendValue(t, db, v)
	}

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	for i, v := range vs {
		var v2 uint64
		assertGetValue(t, db, uint64(i+1), &v2)
		assert.Equal(t, v, v2)
	}
}

func TestAppendValues(t *testing.T) {
	db := assertCreate(t, "append_values", chunkSize)
	defer assertClose(t, db)

	vs := make([]interface{}, numEntries)
	for i := 0; i < len(vs); i++ {
		vs[i] = uint64(0x0FDCBA9876543210 | i)
	}

	assertAppendValues(t, db, vs)

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	for i, v := range vs {
		var v2 uint64
		assertGetValue(t, db, uint64(i+1), &v2)
		assert.Equal(t, v, v2)
	}
}

func TestPersist(t *testing.T) {
	db := assertCreate(t, "persist", chunkSize)

	vs := filldb(t, db, numEntries)

	assertClose(t, db)

	db2 := assertOpen(t, "persist")
	defer assertClose(t, db2)

	for i, v := range vs {
		bs := assertGet(t, db2, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestForget(t *testing.T) {
	db := assertCreate(t, "forget", chunkSize)
	defer assertClose(t, db)

	vs := filldb(t, db, numEntries)

	toForget := uint64(50)
	newOldestID := db.OldestID() + toForget
	assertForget(t, db, newOldestID)
	assert.Equal(t, newOldestID, db.OldestID())

	for i, v := range vs {
		if uint64(i)+1 < newOldestID {
			continue
		}
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestRollback(t *testing.T) {
	db := assertCreate(t, "rollback", chunkSize)
	defer assertClose(t, db)

	vs := filldb(t, db, numEntries)

	toRollback := uint64(50)
	newNextID := db.NextID() - toRollback
	assertRollback(t, db, newNextID)
	assert.Equal(t, newNextID, db.NextID())

	for i, v := range vs {
		if uint64(i)+1 == newNextID {
			break
		}
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestTruncate(t *testing.T) {
	db := assertCreate(t, "truncate", chunkSize)
	defer assertClose(t, db)

	vs := filldb(t, db, numEntries)

	toForget := uint64(20)
	toRollback := uint64(30)
	newOldestID := db.OldestID() + toForget
	newNextID := db.NextID() - toRollback
	assertTruncate(t, db, newOldestID, newNextID)
	assert.Equal(t, newOldestID, db.OldestID())
	assert.Equal(t, newNextID, db.NextID())

	for i, v := range vs {
		if uint64(i)+1 < newOldestID {
			continue
		}
		if uint64(i)+1 == newNextID {
			break
		}
		bs := assertGet(t, db, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

func TestPersistTruncate(t *testing.T) {
	db := assertCreate(t, "persist_truncate", chunkSize)

	vs := filldb(t, db, numEntries)

	toForget := uint64(20)
	toRollback := uint64(30)
	newOldestID := db.OldestID() + toForget
	newNextID := db.NextID() - toRollback
	assertTruncate(t, db, newOldestID, newNextID)

	assertClose(t, db)

	db2 := assertOpen(t, "persist_truncate")
	defer assertClose(t, db2)

	assert.Equal(t, newOldestID, db2.OldestID())
	assert.Equal(t, newNextID, db2.NextID())

	for i, v := range vs {
		if uint64(i)+1 < newOldestID {
			continue
		}
		if uint64(i)+1 == newNextID {
			break
		}
		bs := assertGet(t, db2, uint64(i+1))
		assert.Equal(t, v, bs)
	}
}

/// ASSERTIONS

func assertCreate(t *testing.T, testName string, cSize uint32) LogDB {
	_ = os.RemoveAll("test_db/" + testName)
	db, err := Create("test_db/"+testName, 0, cSize)
	if err != nil {
		t.Fatal(err)
	}
	return db
}

func assertOpen(t *testing.T, testName string) LogDB {
	db, err := Open("test_db/" + testName)
	if err != nil {
		t.Fatal(err)
	}
	return db
}

func assertClose(t *testing.T, db LogDB) {
	if err := db.Close(); err != nil {
		t.Fatal(err)
	}
}

func assertAppend(t *testing.T, db LogDB, entry []byte) {
	if err := db.Append(entry); err != nil {
		t.Fatal(err)
	}
}

func assertAppendValue(t *testing.T, db LogDB, value interface{}) {
	if err := db.AppendValue(value); err != nil {
		t.Fatal(err)
	}
}

func assertAppendEntries(t *testing.T, db LogDB, entries [][]byte) {
	if err := db.AppendEntries(entries); err != nil {
		t.Fatal(err)
	}
}

func assertAppendValues(t *testing.T, db LogDB, values []interface{}) {
	if err := db.AppendValues(values); err != nil {
		t.Fatal(err)
	}
}

func assertGet(t *testing.T, db LogDB, id uint64) []byte {
	b, err := db.Get(id)
	if err != nil {
		t.Fatal(err)
	}
	return b
}

func assertGetValue(t *testing.T, db LogDB, id uint64, data interface{}) {
	if err := db.GetValue(id, data); err != nil {
		t.Fatal(err)
	}
}

func assertForget(t *testing.T, db LogDB, newOldestID uint64) {
	if err := db.Forget(newOldestID); err != nil {
		t.Fatal(err)
	}
}

func assertRollback(t *testing.T, db LogDB, newNextID uint64) {
	if err := db.Rollback(newNextID); err != nil {
		t.Fatal(err)
	}
}

func assertTruncate(t *testing.T, db LogDB, newOldestID, newNextID uint64) {
	if err := db.Truncate(newOldestID, newNextID); err != nil {
		t.Fatal(err)
	}
}

/// HELPERS

func filldb(t *testing.T, db LogDB, num int) [][]byte {
	vs := make([][]byte, num)
	for i := 0; i < num; i++ {
		vs[i] = []byte(fmt.Sprintf("entry-%v", i))
	}

	assertAppendEntries(t, db, vs)

	assert.Equal(t, firstID, db.OldestID())
	assert.Equal(t, uint64(len(vs))+1, db.NextID())

	return vs
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/slugalisk/overrustlelogs/common"
)

// Logger logger
type Logger struct {
	logs *ChatLogs
}

// NewLogger instantiates destiny chat logger
func NewLogger(logs *ChatLogs) *Logger {
	return &Logger{
		logs: logs,
	}
}

// DestinyLog starts logging loop
func (l *Logger) DestinyLog(mc <-chan *common.Message) {
	var subTrigger bool
	giftRegex := regexp.MustCompile("^[a-zA-Z0-9_]+ gifted [a-zA-Z0-9_]+ a Tier (I|II|II|IV) subscription!")
	for m := range mc {
		switch m.Type {
		case "BAN":
			l.writeLine(m.Time, m.Channel, "Ban", fmt.Sprintf("%s banned by %s", m.Data, m.Nick))
		case "UNBAN":
			l.writeLine(m.Time, m.Channel, "Ban", fmt.Sprintf("%s unbanned by %s", m.Data, m.Nick))
		case "MUTE":
			l.writeLine(m.Time, m.Channel, "Ban", fmt.Sprintf("%s muted by %s", m.Data, m.Nick))
		case "UNMUTE":
			l.writeLine(m.Time, m.Channel, "Ban", fmt.Sprintf("%s unmuted by %s", m.Data, m.Nick))
		case "BROADCAST":
			if strings.Contains(m.Data, "subscriber!") ||
				strings.Contains(m.Data, "subscribed on Twitch!") ||
				strings.Contains(m.Data, "has resubscribed! Active for") ||
				giftRegex.MatchString(m.Data) {
				l.writeLine(m.Time, m.Channel, "Subscriber", m.Data)
				subTrigger = !subTrigger
				continue
			}
			if subTrigger {
				l.writeLine(m.Time, m.Channel, "SubscriberMessage", m.Data)
				subTrigger = !subTrigger
				continue
			}
			l.writeLine(m.Time, m.Channel, "Broadcast", m.Data)
		case "MSG":
			l.writeLine(m.Time, m.Channel, m.Nick, m.Data)
			subTrigger = false
		}
	}
}

// TwitchLog starts logging loop
func (l *Logger) TwitchLog(mc <-chan *common.Message) {
	for m := range mc {
		if m.Type == "MSG" {
			l.writeLine(m.Time, m.Channel, m.Nick, m.Data)
		}
	}
}

func (l *Logger) writeLine(timestamp time.Time, channel, nick, message string) {
	logs, err := l.logs.Get(filepath.Join(common.GetConfig().LogPath, strings.Title(channel)+" chatlog", timestamp.Format("January 2006"), timestamp.Format("2006-01-02")+".txt"))
	if err != nil {
		log.Printf("error opening log %s", err)
		return
	}
	logs.Write(timestamp, nick, message)
}
<commit_msg>logge: small refactor<commit_after>package main

import (
	"fmt"
	"log"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/slugalisk/overrustlelogs/common"
)

// Logger logger
type Logger struct {
	logs *ChatLogs
}

// NewLogger instantiates destiny chat logger
func NewLogger(logs *ChatLogs) *Logger {
	return &Logger{
		logs: logs,
	}
}

// DestinyLog starts logging loop
func (l *Logger) DestinyLog(mc <-chan *common.Message) {
	var subTrigger bool
	giftRegex := regexp.MustCompile("^[a-zA-Z0-9_]+ gifted [a-zA-Z0-9_]+ a Tier (I|II|II|IV) subscription!")

loop:
	for m := range mc {
		switch m.Type {
		case "BAN":
			l.writeLine(m.Time, m.Channel, "Ban", fmt.Sprintf("%s banned by %s", m.Data, m.Nick))
		case "UNBAN":
			l.writeLine(m.Time, m.Channel, "Ban", fmt.Sprintf("%s unbanned by %s", m.Data, m.Nick))
		case "MUTE":
			l.writeLine(m.Time, m.Channel, "Ban", fmt.Sprintf("%s muted by %s", m.Data, m.Nick))
		case "UNMUTE":
			l.writeLine(m.Time, m.Channel, "Ban", fmt.Sprintf("%s unmuted by %s", m.Data, m.Nick))
		case "BROADCAST":
			subMessages := []string{"subscriber!", "subscribed on Twitch!", "has resubscribed! Active for"}

			for _, smsg := range subMessages {
				if strings.Contains(m.Data, smsg) {
					l.writeLine(m.Time, m.Channel, "Subscriber", m.Data)
					subTrigger = !subTrigger
					continue loop
				}
			}
			if giftRegex.MatchString(m.Data) {
				l.writeLine(m.Time, m.Channel, "Subscriber", m.Data)
				subTrigger = !subTrigger
				continue loop
			}

			if subTrigger {
				l.writeLine(m.Time, m.Channel, "SubscriberMessage", m.Data)
				subTrigger = !subTrigger
				continue
			}
			l.writeLine(m.Time, m.Channel, "Broadcast", m.Data)
		case "MSG":
			l.writeLine(m.Time, m.Channel, m.Nick, m.Data)
			subTrigger = false
		}
	}
}

// TwitchLog starts logging loop
func (l *Logger) TwitchLog(mc <-chan *common.Message) {
	for m := range mc {
		if m.Type == "MSG" {
			l.writeLine(m.Time, m.Channel, m.Nick, m.Data)
		}
	}
}

func (l *Logger) writeLine(timestamp time.Time, channel, nick, message string) {
	logs, err := l.logs.Get(filepath.Join(common.GetConfig().LogPath, strings.Title(channel)+" chatlog", timestamp.Format("January 2006"), timestamp.Format("2006-01-02")+".txt"))
	if err != nil {
		log.Printf("error opening log %s", err)
		return
	}
	logs.Write(timestamp, nick, message)
}
<|endoftext|>


<commit_before>package bothandlers

import (
	"fmt"
	"github.com/djosephsen/hal"
	"math/rand"
	"time"
	"net/http"
	"encoding/json"
)

type insult struct{
   Insult   string
   Severity string
}

var LoveAndWar = &hal.Handler{
	Method:  hal.RESPOND,
	Pattern: `(love|insult) (\w+)`,
	Run: func(res *hal.Response) error {
		act:=res.Match[1]
		user:=res.Match[2]
		if user==`me`{ user = res.Envelope.User.Name}
		now:=time.Now()
		rand.Seed(int64(now.Unix()))

		var reply string
		if act == `love`{
			reply=makeLove(user)
		}else if act == `insult`{
			reply=makeWar(user)
		}
				return res.Respond(reply)
	},
}

func makeWar(user string) string{
	it1:=[]string{
	`lazy`,
	`stupid`,
	`californian`,
	`slimy`,
	`smelly`,
	`slutty`,
	`pompous`,
	`communist`,
	`wangnose`,
	`pie-eating`,
	`racist`,
	`eliteist`,
	`fascist`,
	`drug-snarfing`,
	`slovenly`,
	`tone-deaf`,
	`ugly`,
	`buck-toothed`,
	`creepy`,
	`goat-faced`,
	}

	it3:=[]string{
	`spaz`,
	`douche`,
	`turd`,
	`ass`,
	`rectum`,
	`butt`,
	`poop`,
	`armpit`,
	`crotch`,
	`bitch`,
	`slime`,
	`prick`,
	`slut`,
	`taint`,
	`roach`,
	`snot`,
	`boner`,
	`shart`,
	`nut`,
	`sphincter`,
	}

	it2:=[]string{
	`pilot`,
	`canoe`,
	`captain`,
	`pirate`,
	`hammer`,
	`knob`,
	`box`,
	`jockey`,
	`nazi`,
	`waffle`,
	`goblin`,
	`nazi`,
	`biscuit`,
	`clown`,
	`socket`,
	`monster`,
	`clown`,
	`hound`,
	`recepticle`,
	`balloon`,
	}

	n:=rand.Intn(2)
	switch n {
		case 1:
	   	i:=new(insult)
			resp,_:=http.Get(`http://pleaseinsult.me/api`)
			dec := json.NewDecoder(resp.Body)
			dec.Decode(i)
			return fmt.Sprintf("Hey %s... %s",user, i.Insult)
		case 2:
			return fmt.Sprintf("%s is a %s %s %s",user, it1[rand.Intn(len(it1))], it2[rand.Intn(len(it2))],it3[rand.Intn(len(it3))])
	}
	return fmt.Sprintf("... derp, excuse me I have a bug: %s",n)
}

func makeLove(user string) string{
	love:=[]string{
		`You deserve a promotion.`,
		`I appreciate all of your opinions.`,
		`I like your style.`,
		`Your T-shirt smells fresh.`,
		`You are like a spring flower; beautiful and vivacious.`,
		`I am utterly disarmed by your wit.`,
		`I really enjoy the way you pronounce the word 'ruby'.`,
		`That looks nice on you.`,
		`I like those shoes more than mine.`,
		`Nice motor control!`,
		`You have a good taste in websites.`,
		`Your mouse told me that you have very soft hands.`,
		`You are full of youth.`,
		`I like your jacket.`,
		`You have a good web-surfing stance.`,
		`You should be a poster child for poster children.`,
		`I appreciate you more than Santa appreciates chimney grease.`,
		`I wish I was your mirror.`,
		`I find you to be a fountain of inspiration.`,
		`You have perfect bone structure.`,
		`I disagree with anyone who disagrees with you.`,
		`Have you been working out?`,
		`With your creative wit, I'm sure you could come up with better compliments than me.`,
		`I like your socks.`,
		`You are so charming.`,
		`You're tremendous!`,
		`Your smile is breath taking.`,
		`How do you get your hair to look that great?`,
		`I am grateful to be blessed by your presence.`,
		`Say, aren't you that famous model from TV?`,
		`Take a break; you've earned it.`,
		`Your life is so interesting!`,
		`The sound of your voice sends tingles of joy down my back.`,
		`I enjoy spending time with you.`,
		`I would share my dessert with you.`,
		`You can have the last bite.`,
		`May I have this dance?`,
		`I would love to visit you, but I live on the Internet.`,
		`I love the way you click.`,
		`You're invited to my birthday party.`,
		`All of your ideas are brilliant!`,
		`If I freeze, it's not a computer virus.  I was just stunned by your intellect.`,
		`You're spontaneous, and I love it!`,
		`You should try out for everything.`,
		`You make my data circuits skip a beat.`,
		`You are the gravy to my mashed potatoes.`,
		`You get an A+ in the rollbook of my heart!`,
		`I'm jealous of the other websites you visit, because I enjoy seeing you so much!`,
		`I would enjoy a roadtrip with you.`,
		`If I had to choose between you or 17lbs of chocolate, I would choose you`,
		`I like you more than the smell of Grandma's home-made apple pies.`,
		`You would look good in glasses OR contacts.`,
		`Let's do this again sometime.`,
		`You could go longer without a shower than most people.`,
		`I feel the need to impress you.`,
		`I would trust you to pick out a pet fish for me.`,
		`I'm glad we met.`,
		`Do that again!`,
		`Will you sign my yearbook?`,
		`You're so smart!`,
		`We should start a band.`,
		`You're cooler than ice-skating Fonzi.`,
		`I heard you make really good French Toast.`,
		`You're cooler than Pirates and Ninjas combined.`,
		`I like your pants.`,
		`You're pretty groovy, dude.`,
		`When I grow up, I want to be just like you.`,
		`I told all my friends about how cool you are.`,
		`You can play any prank, and get away with it.`,
		`I can tell that we are gonna be friends.`,
		`I just want to gobble you up!`,
		`You're sweeter than than a bucket of bon-bons!`,
		`Treat yourself to another compliment!`,
		`You're pretty high on my list of people with whom I would want to be stranded on an island.`,
		`You could probably lead a rebellion.`,
		`Is it hot in here or is it just you?`,
		`:heart: :heart: :heart:`,
		`You are more fun than a Japanese steakhouse.`,
		`Your voice is more soothing than Morgan Freeman's.`,
		`You could be drinking whole milk if you wanted to.`,
		`I support all of your decisions.`,
		`You are as fun as a hot tub full of chocolate pudding.`,
		`I usually don't say this on a first date, but will you marry me?`,
		`Being awesome is hard, but you'll manage.`,
		`Your skin is radiant.`,
		`You will still be beautiful when you get older.`,
		`You could survive a zombie apocalypse.`,
		`I wish I could move your furniture.`,
		`I think about you while I'm on the toilet.`,
		`You're so rad.`,
		`Your glass is the fullest.`,
		`I find you very relevant.`,
		`The only difference between exceptional and amazing is you.`,
		`Shall I compare thee to a summer's day?  Thou art more lovely and more temperate.`,
		`I love you more than bacon!`,
		`You make me think of beautiful things, like strawberries.`,
		`I would share my fruit Gushers with you.`,
		`You're more aesthetically pleasant to look at than that one green color on this website.`,
		`You're more fun than bubble wrap.`,
		`You make babies smile.`,
		`You make the gloomy days a little less gloomy.`,
		`You are warmer than a Snuggie.`,
		`You make me feel like I am on top of the world.`,
		`You remind me of my woobie`,
		`Let's never stop hanging out.`,
		`You're more cuddly than the Downy Bear.`,
		`You're so hot I'd do your taxes`,
		`You are a foamy bucket of awesome.`,
		`If you really wanted to, you could probably get a bird to land on your shoulder and hang out with you.`,
		`My mom always asks me why I can't be more like you.`,
		`You look great in this or any other light.`,
		`You know all the coolest music.`,
		`Chuck Norris told me he finds you midly intimidating.`,
		`Your body fat percentage is perfectly suited for your height.`,
		`I am having trouble coming up with a compliment worthy enough for you.`,
		`If we were playing kickball, I'd pick you first.`,
		`You're cooler than ice on the rocks.`,
		`You're the bee's knees.`,
		`I wish I could choose your handwriting as a font.`,
		`You definitely know the difference between your and you're.`,
		`You have good taste.`,
		`I named all my appliances after you.`,
		`Don't worry about procrastinating on your studies, I know you'll do great!`,
		`I think about your for entire seconds (which is 2e^6543212345 in computer years)`,
		`If you were in a chemistry class with me, it would be 10x less boring.`,
		`If you broke your arm, I would carry your books for you.`,
		`I love the way your eyes crinkle at the corners when you smile.`,
		`You make me want to be the person I am capable of being.`,
		`You're a skilled driver.`,
		`You are the rare catalyst to my volatile compound.`,
		`Looking at you makes my foot cramps go away instantaneously.`,
		`I like your face.`,
		`Cats like you.`,
		`You're so cool, that on a scale of from 1-10, you're elevendyseven.`,
		`You have the best laugh ever.`,
		`Your name is fun to say.`,
		`I love you more than a drunk college student loves tacos.`,
		`My camera isn't worthy to take your picture.`,
		`You are the sugar on my rice krispies.`,
		`You're real happening in a far out way, can you dig it?`,
		`Can you teach me how to be as awesome as you?`,
		`Our awkward silences aren't even awkward.`,
		`I enjoy you more than a good sneeze. A GOOD one.`,
		`You could invent words and people would use them.`,
		`You have powerful sweaters.`,
		`The other day I looked at you and I was all like.. "I'd tap that".`,
		`You are better than unicorns and sparkles combined!`,
		`You are the watermelon in my fruit salad. `,
		`I bet even your farts smell good.`,
		`I would trust my children with you.`,
		`You make me forget what I was going to...`,
		`I'd wake up for an 8 a.m. class just so I could sit next to you.`,
		`You have the moves like Jagger.`,
		`You're so hot that you denature my proteins.`,
		`All I want for Christmas is you!`,
		`You are the world's greatest hugger.`,
		`If you were a black guy in a movie, they wouldn't kill you off.`,
		`They should name an ice cream flavor after you.`,
		`Me without you is like a nerd without braces, a shoe with out laces, asentencewithoutspaces.`,
		`Just knowing someone as cool as you will read this makes me smile.`,
		`I would volunteer to take your place in the Hunger Games.`,
		`If I had a thousand bucks for every time you did something stupid, I'd be broke!`,
		`I'd let you steal the white part of my Oreo.`,
		`The Force is strong with you.`,
		`I like the way your nostrils are placed on your nose.`,
		`I would hold the elevator doors open for you if they were closing.`,
		`You make me want to frolic in a field.`,
	}
	return fmt.Sprintf("Hey %s, %s",user, love[rand.Intn(len(love))])
}
<commit_msg>Send not Respond<commit_after>package bothandlers

import (
	"fmt"
	"github.com/djosephsen/hal"
	"math/rand"
	"time"
	"net/http"
	"encoding/json"
)

type insult struct{
   Insult   string
   Severity string
}

var LoveAndWar = &hal.Handler{
	Method:  hal.RESPOND,
	Pattern: `(love|insult) (\w+)`,
	Run: func(res *hal.Response) error {
		act:=res.Match[1]
		user:=res.Match[2]
		if user==`me`{ user = res.Envelope.User.Name}
		now:=time.Now()
		rand.Seed(int64(now.Unix()))

		var reply string
		if act == `love`{
			reply=makeLove(user)
		}else if act == `insult`{
			reply=makeWar(user)
		}
				return res.Send(reply)
	},
}

func makeWar(user string) string{
	it1:=[]string{
	`lazy`,
	`stupid`,
	`californian`,
	`slimy`,
	`smelly`,
	`slutty`,
	`pompous`,
	`communist`,
	`wangnose`,
	`pie-eating`,
	`racist`,
	`eliteist`,
	`fascist`,
	`drug-snarfing`,
	`slovenly`,
	`tone-deaf`,
	`ugly`,
	`buck-toothed`,
	`creepy`,
	`goat-faced`,
	}

	it3:=[]string{
	`spaz`,
	`douche`,
	`turd`,
	`ass`,
	`rectum`,
	`butt`,
	`poop`,
	`armpit`,
	`crotch`,
	`bitch`,
	`slime`,
	`prick`,
	`slut`,
	`taint`,
	`roach`,
	`snot`,
	`boner`,
	`shart`,
	`nut`,
	`sphincter`,
	}

	it2:=[]string{
	`pilot`,
	`canoe`,
	`captain`,
	`pirate`,
	`hammer`,
	`knob`,
	`box`,
	`jockey`,
	`nazi`,
	`waffle`,
	`goblin`,
	`nazi`,
	`biscuit`,
	`clown`,
	`socket`,
	`monster`,
	`clown`,
	`hound`,
	`recepticle`,
	`balloon`,
	}

	n:=rand.Intn(2)
	switch n {
		case 1:
	   	i:=new(insult)
			resp,_:=http.Get(`http://pleaseinsult.me/api`)
			dec := json.NewDecoder(resp.Body)
			dec.Decode(i)
			return fmt.Sprintf("Hey %s... %s",user, i.Insult)
		case 2:
			return fmt.Sprintf("%s is a %s %s %s",user, it1[rand.Intn(len(it1))], it2[rand.Intn(len(it2))],it3[rand.Intn(len(it3))])
	}
	return fmt.Sprintf("... derp, excuse me I have a bug: %s",n)
}

func makeLove(user string) string{
	love:=[]string{
		`You deserve a promotion.`,
		`I appreciate all of your opinions.`,
		`I like your style.`,
		`Your T-shirt smells fresh.`,
		`You are like a spring flower; beautiful and vivacious.`,
		`I am utterly disarmed by your wit.`,
		`I really enjoy the way you pronounce the word 'ruby'.`,
		`That looks nice on you.`,
		`I like those shoes more than mine.`,
		`Nice motor control!`,
		`You have a good taste in websites.`,
		`Your mouse told me that you have very soft hands.`,
		`You are full of youth.`,
		`I like your jacket.`,
		`You have a good web-surfing stance.`,
		`You should be a poster child for poster children.`,
		`I appreciate you more than Santa appreciates chimney grease.`,
		`I wish I was your mirror.`,
		`I find you to be a fountain of inspiration.`,
		`You have perfect bone structure.`,
		`I disagree with anyone who disagrees with you.`,
		`Have you been working out?`,
		`With your creative wit, I'm sure you could come up with better compliments than me.`,
		`I like your socks.`,
		`You are so charming.`,
		`You're tremendous!`,
		`Your smile is breath taking.`,
		`How do you get your hair to look that great?`,
		`I am grateful to be blessed by your presence.`,
		`Say, aren't you that famous model from TV?`,
		`Take a break; you've earned it.`,
		`Your life is so interesting!`,
		`The sound of your voice sends tingles of joy down my back.`,
		`I enjoy spending time with you.`,
		`I would share my dessert with you.`,
		`You can have the last bite.`,
		`May I have this dance?`,
		`I would love to visit you, but I live on the Internet.`,
		`I love the way you click.`,
		`You're invited to my birthday party.`,
		`All of your ideas are brilliant!`,
		`If I freeze, it's not a computer virus.  I was just stunned by your intellect.`,
		`You're spontaneous, and I love it!`,
		`You should try out for everything.`,
		`You make my data circuits skip a beat.`,
		`You are the gravy to my mashed potatoes.`,
		`You get an A+ in the rollbook of my heart!`,
		`I'm jealous of the other websites you visit, because I enjoy seeing you so much!`,
		`I would enjoy a roadtrip with you.`,
		`If I had to choose between you or 17lbs of chocolate, I would choose you`,
		`I like you more than the smell of Grandma's home-made apple pies.`,
		`You would look good in glasses OR contacts.`,
		`Let's do this again sometime.`,
		`You could go longer without a shower than most people.`,
		`I feel the need to impress you.`,
		`I would trust you to pick out a pet fish for me.`,
		`I'm glad we met.`,
		`Do that again!`,
		`Will you sign my yearbook?`,
		`You're so smart!`,
		`We should start a band.`,
		`You're cooler than ice-skating Fonzi.`,
		`I heard you make really good French Toast.`,
		`You're cooler than Pirates and Ninjas combined.`,
		`I like your pants.`,
		`You're pretty groovy, dude.`,
		`When I grow up, I want to be just like you.`,
		`I told all my friends about how cool you are.`,
		`You can play any prank, and get away with it.`,
		`I can tell that we are gonna be friends.`,
		`I just want to gobble you up!`,
		`You're sweeter than than a bucket of bon-bons!`,
		`Treat yourself to another compliment!`,
		`You're pretty high on my list of people with whom I would want to be stranded on an island.`,
		`You could probably lead a rebellion.`,
		`Is it hot in here or is it just you?`,
		`:heart: :heart: :heart:`,
		`You are more fun than a Japanese steakhouse.`,
		`Your voice is more soothing than Morgan Freeman's.`,
		`You could be drinking whole milk if you wanted to.`,
		`I support all of your decisions.`,
		`You are as fun as a hot tub full of chocolate pudding.`,
		`I usually don't say this on a first date, but will you marry me?`,
		`Being awesome is hard, but you'll manage.`,
		`Your skin is radiant.`,
		`You will still be beautiful when you get older.`,
		`You could survive a zombie apocalypse.`,
		`I wish I could move your furniture.`,
		`I think about you while I'm on the toilet.`,
		`You're so rad.`,
		`Your glass is the fullest.`,
		`I find you very relevant.`,
		`The only difference between exceptional and amazing is you.`,
		`Shall I compare thee to a summer's day?  Thou art more lovely and more temperate.`,
		`I love you more than bacon!`,
		`You make me think of beautiful things, like strawberries.`,
		`I would share my fruit Gushers with you.`,
		`You're more aesthetically pleasant to look at than that one green color on this website.`,
		`You're more fun than bubble wrap.`,
		`You make babies smile.`,
		`You make the gloomy days a little less gloomy.`,
		`You are warmer than a Snuggie.`,
		`You make me feel like I am on top of the world.`,
		`You remind me of my woobie`,
		`Let's never stop hanging out.`,
		`You're more cuddly than the Downy Bear.`,
		`You're so hot I'd do your taxes`,
		`You are a foamy bucket of awesome.`,
		`If you really wanted to, you could probably get a bird to land on your shoulder and hang out with you.`,
		`My mom always asks me why I can't be more like you.`,
		`You look great in this or any other light.`,
		`You know all the coolest music.`,
		`Chuck Norris told me he finds you midly intimidating.`,
		`Your body fat percentage is perfectly suited for your height.`,
		`I am having trouble coming up with a compliment worthy enough for you.`,
		`If we were playing kickball, I'd pick you first.`,
		`You're cooler than ice on the rocks.`,
		`You're the bee's knees.`,
		`I wish I could choose your handwriting as a font.`,
		`You definitely know the difference between your and you're.`,
		`You have good taste.`,
		`I named all my appliances after you.`,
		`Don't worry about procrastinating on your studies, I know you'll do great!`,
		`I think about your for entire seconds (which is 2e^6543212345 in computer years)`,
		`If you were in a chemistry class with me, it would be 10x less boring.`,
		`If you broke your arm, I would carry your books for you.`,
		`I love the way your eyes crinkle at the corners when you smile.`,
		`You make me want to be the person I am capable of being.`,
		`You're a skilled driver.`,
		`You are the rare catalyst to my volatile compound.`,
		`Looking at you makes my foot cramps go away instantaneously.`,
		`I like your face.`,
		`Cats like you.`,
		`You're so cool, that on a scale of from 1-10, you're elevendyseven.`,
		`You have the best laugh ever.`,
		`Your name is fun to say.`,
		`I love you more than a drunk college student loves tacos.`,
		`My camera isn't worthy to take your picture.`,
		`You are the sugar on my rice krispies.`,
		`You're real happening in a far out way, can you dig it?`,
		`Can you teach me how to be as awesome as you?`,
		`Our awkward silences aren't even awkward.`,
		`I enjoy you more than a good sneeze. A GOOD one.`,
		`You could invent words and people would use them.`,
		`You have powerful sweaters.`,
		`The other day I looked at you and I was all like.. "I'd tap that".`,
		`You are better than unicorns and sparkles combined!`,
		`You are the watermelon in my fruit salad. `,
		`I bet even your farts smell good.`,
		`I would trust my children with you.`,
		`You make me forget what I was going to...`,
		`I'd wake up for an 8 a.m. class just so I could sit next to you.`,
		`You have the moves like Jagger.`,
		`You're so hot that you denature my proteins.`,
		`All I want for Christmas is you!`,
		`You are the world's greatest hugger.`,
		`If you were a black guy in a movie, they wouldn't kill you off.`,
		`They should name an ice cream flavor after you.`,
		`Me without you is like a nerd without braces, a shoe with out laces, asentencewithoutspaces.`,
		`Just knowing someone as cool as you will read this makes me smile.`,
		`I would volunteer to take your place in the Hunger Games.`,
		`If I had a thousand bucks for every time you did something stupid, I'd be broke!`,
		`I'd let you steal the white part of my Oreo.`,
		`The Force is strong with you.`,
		`I like the way your nostrils are placed on your nose.`,
		`I would hold the elevator doors open for you if they were closing.`,
		`You make me want to frolic in a field.`,
	}
	return fmt.Sprintf("Hey %s, %s",user, love[rand.Intn(len(love))])
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"unsafe"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
)

type devLxdResponse struct {
	content interface{}
	code    int
	ctype   string
}

func okResponse(ct interface{}, ctype string) *devLxdResponse {
	return &devLxdResponse{ct, http.StatusOK, ctype}
}

type devLxdHandler struct {
	path string

	/*
	 * This API will have to be changed slightly when we decide to support
	 * websocket events upgrading, but since we don't have events on the
	 * server side right now either, I went the simple route to avoid
	 * needless noise.
	 */
	f func(c container, r *http.Request) *devLxdResponse
}

var configGet = devLxdHandler{"/1.0/config", func(c container, r *http.Request) *devLxdResponse {
	filtered := []string{}
	for k, _ := range c.ExpandedConfig() {
		if strings.HasPrefix(k, "user.") {
			filtered = append(filtered, fmt.Sprintf("/1.0/config/%s", k))
		}
	}
	return okResponse(filtered, "json")
}}

var configKeyGet = devLxdHandler{"/1.0/config/{key}", func(c container, r *http.Request) *devLxdResponse {
	key := mux.Vars(r)["key"]
	if !strings.HasPrefix(key, "user.") {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	value, ok := c.ExpandedConfig()[key]
	if !ok {
		return &devLxdResponse{"not found", http.StatusNotFound, "raw"}
	}

	return okResponse(value, "raw")
}}

var metadataGet = devLxdHandler{"/1.0/meta-data", func(c container, r *http.Request) *devLxdResponse {
	value := c.ExpandedConfig()["user.meta-data"]
	return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: %s\nlocal-hostname: %s\n%s", c.Name(), c.Name(), value), "raw")
}}

var handlers = []devLxdHandler{
	devLxdHandler{"/", func(c container, r *http.Request) *devLxdResponse {
		return okResponse([]string{"/1.0"}, "json")
	}},
	devLxdHandler{"/1.0", func(c container, r *http.Request) *devLxdResponse {
		return okResponse(shared.Jmap{"api_version": shared.APIVersion}, "json")
	}},
	configGet,
	configKeyGet,
	metadataGet,
	/* TODO: events */
}

func hoistReq(f func(container, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		conn := extractUnderlyingConn(w)
		pid, ok := pidMapper.m[conn]
		if !ok {
			http.Error(w, pidNotInContainerErr.Error(), 500)
			return
		}

		c, err := findContainerForPid(pid, d)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		resp := f(c, r)
		if resp.code != http.StatusOK {
			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
		} else if resp.ctype == "json" {
			w.Header().Set("Content-Type", "application/json")
			WriteJSON(w, resp.content)
		} else {
			w.Header().Set("Content-Type", "application/octet-stream")
			fmt.Fprintf(w, resp.content.(string))
		}
	}
}

func createAndBindDevLxd() (*net.UnixListener, error) {
	sockFile := path.Join(shared.VarPath("devlxd"), "sock")

	/*
	 * If this socket exists, that means a previous lxd died and didn't
	 * clean up after itself. We assume that the LXD is actually dead if we
	 * get this far, since StartDaemon() tries to connect to the actual lxd
	 * socket to make sure that it is actually dead. So, it is safe to
	 * remove it here without any checks.
	 *
	 * Also, it would be nice to SO_REUSEADDR here so we don't have to
	 * delete the socket, but we can't:
	 *   http://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
	 *
	 * Note that this will force clients to reconnect when LXD is restarted.
	 */
	if err := os.Remove(sockFile); err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	unixAddr, err := net.ResolveUnixAddr("unix", sockFile)
	if err != nil {
		return nil, err
	}

	unixl, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		return nil, err
	}

	if err := os.Chmod(sockFile, 0666); err != nil {
		return nil, err
	}

	return unixl, nil
}

func devLxdServer(d *Daemon) *http.Server {
	m := mux.NewRouter()

	for _, handler := range handlers {
		m.HandleFunc(handler.path, hoistReq(handler.f, d))
	}

	return &http.Server{
		Handler:   m,
		ConnState: pidMapper.ConnStateHandler,
	}
}

/*
 * Everything below here is the guts of the unix socket bits. Unfortunately,
 * golang's API does not make this easy. What happens is:
 *
 * 1. We install a ConnState listener on the http.Server, which does the
 *    initial unix socket credential exchange. When we get a connection started
 *    event, we use SO_PEERCRED to extract the creds for the socket.
 *
 * 2. We store a map from the connection pointer to the pid for that
 *    connection, so that once the HTTP negotiation occurrs and we get a
 *    ResponseWriter, we know (because we negotiated on the first byte) which
 *    pid the connection belogs to.
 *
 * 3. Regular HTTP negotiation and dispatch occurs via net/http.
 *
 * 4. When rendering the response via ResponseWriter, we match its underlying
 *    connection against what we stored in step (2) to figure out which container
 *    it came from.
 */

/*
 * We keep this in a global so that we can reference it from the server and
 * from our http handlers, since there appears to be no way to pass information
 * around here.
 */
var pidMapper = ConnPidMapper{m: map[*net.UnixConn]int32{}}

type ConnPidMapper struct {
	m map[*net.UnixConn]int32
}

func (m *ConnPidMapper) ConnStateHandler(conn net.Conn, state http.ConnState) {
	unixConn := conn.(*net.UnixConn)
	switch state {
	case http.StateNew:
		pid, err := getPid(unixConn)
		if err != nil {
			shared.Debugf("Error getting pid for conn %s", err)
		} else {
			m.m[unixConn] = pid
		}
	case http.StateActive:
		return
	case http.StateIdle:
		return
	case http.StateHijacked:
		/*
		 * The "Hijacked" state indicates that the connection has been
		 * taken over from net/http. This is useful for things like
		 * developing websocket libraries, who want to upgrade the
		 * connection to a websocket one, and not use net/http any
		 * more. Whatever the case, we want to forget about it since we
		 * won't see it either.
		 */
		delete(m.m, unixConn)
	case http.StateClosed:
		delete(m.m, unixConn)
	default:
		shared.Debugf("Unknown state for connection %s", state)
	}
}

/*
 * I also don't see that golang exports an API to get at the underlying FD, but
 * we need it to get at SO_PEERCRED, so let's grab it.
 */
func extractUnderlyingFd(unixConnPtr *net.UnixConn) int {
	conn := reflect.Indirect(reflect.ValueOf(unixConnPtr))
	netFdPtr := conn.FieldByName("fd")
	netFd := reflect.Indirect(netFdPtr)
	fd := netFd.FieldByName("sysfd")
	return int(fd.Int())
}

func getPid(conn *net.UnixConn) (int32, error) {
	fd := extractUnderlyingFd(conn)

	_, _, pid, err := getUcred(fd)
	if err != nil {
		return 0, err
	}

	return pid, nil
}

/*
 * As near as I can tell, there is no nice way of extracting an underlying
 * net.Conn (or in our case, net.UnixConn) from an http.Request or
 * ResponseWriter without hijacking it [1]. Since we want to send and recieve
 * unix creds to figure out which container this request came from, we need to
 * do this.
 *
 * [1]: https://groups.google.com/forum/#!topic/golang-nuts/_FWdFXJa6QA
 */
func extractUnderlyingConn(w http.ResponseWriter) *net.UnixConn {
	v := reflect.Indirect(reflect.ValueOf(w))
	connPtr := v.FieldByName("conn")
	conn := reflect.Indirect(connPtr)
	rwc := conn.FieldByName("rwc")

	netConnPtr := (*net.Conn)(unsafe.Pointer(rwc.UnsafeAddr()))
	unixConnPtr := (*netConnPtr).(*net.UnixConn)

	return unixConnPtr
}

var pidNotInContainerErr = fmt.Errorf("pid not in container?")

func findContainerForPid(pid int32, d *Daemon) (container, error) {
	/*
	 * Try and figure out which container a pid is in. There is probably a
	 * better way to do this. Based on rharper's initial performance
	 * metrics, looping over every container and calling newLxdContainer is
	 * expensive, so I wanted to avoid that if possible, so this happens in
	 * a two step process:
	 *
	 * 1. Walk up the process tree until you see something that looks like
	 *    an lxc monitor process and extract its name from there.
	 *
	 * 2. If this fails, it may be that someone did an `lxc exec foo bash`,
	 *    so the process isn't actually a decendant of the container's
	 *    init. In this case we just look through all the containers until
	 *    we find an init with a matching pid namespace. This is probably
	 *    uncommon, so hopefully the slowness won't hurt us.
	 */

	origpid := pid

	for pid > 1 {
		cmdline, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
		if err != nil {
			return nil, err
		}

		if strings.HasPrefix(string(cmdline), "[lxc monitor]") {
			// container names can't have spaces
			parts := strings.Split(string(cmdline), " ")
			name := parts[len(parts)-1]

			return containerLoadByName(d, name)
		}

		status, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
		if err != nil {
			return nil, err
		}

		re := regexp.MustCompile("PPid:\\s*([0-9]*)")
		for _, line := range strings.Split(string(status), "\n") {
			m := re.FindStringSubmatch(line)
			if m != nil && len(m) > 1 {
				result, err := strconv.Atoi(m[1])
				if err != nil {
					return nil, err
				}

				pid = int32(result)
				break
			}
		}
	}

	origPidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", origpid))
	if err != nil {
		return nil, err
	}

	containers, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return nil, err
	}

	for _, container := range containers {
		c, err := containerLoadByName(d, container)
		if err != nil {
			return nil, err
		}

		if !c.IsRunning() {
			continue
		}

		initpid := c.InitPID()
		pidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", initpid))
		if err != nil {
			return nil, err
		}

		if origPidNs == pidNs {
			return c, nil
		}
	}

	return nil, pidNotInContainerErr
}
<commit_msg>Fix devlxd access outside of an exec session<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"unsafe"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
)

type devLxdResponse struct {
	content interface{}
	code    int
	ctype   string
}

func okResponse(ct interface{}, ctype string) *devLxdResponse {
	return &devLxdResponse{ct, http.StatusOK, ctype}
}

type devLxdHandler struct {
	path string

	/*
	 * This API will have to be changed slightly when we decide to support
	 * websocket events upgrading, but since we don't have events on the
	 * server side right now either, I went the simple route to avoid
	 * needless noise.
	 */
	f func(c container, r *http.Request) *devLxdResponse
}

var configGet = devLxdHandler{"/1.0/config", func(c container, r *http.Request) *devLxdResponse {
	filtered := []string{}
	for k, _ := range c.ExpandedConfig() {
		if strings.HasPrefix(k, "user.") {
			filtered = append(filtered, fmt.Sprintf("/1.0/config/%s", k))
		}
	}
	return okResponse(filtered, "json")
}}

var configKeyGet = devLxdHandler{"/1.0/config/{key}", func(c container, r *http.Request) *devLxdResponse {
	key := mux.Vars(r)["key"]
	if !strings.HasPrefix(key, "user.") {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	value, ok := c.ExpandedConfig()[key]
	if !ok {
		return &devLxdResponse{"not found", http.StatusNotFound, "raw"}
	}

	return okResponse(value, "raw")
}}

var metadataGet = devLxdHandler{"/1.0/meta-data", func(c container, r *http.Request) *devLxdResponse {
	value := c.ExpandedConfig()["user.meta-data"]
	return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: %s\nlocal-hostname: %s\n%s", c.Name(), c.Name(), value), "raw")
}}

var handlers = []devLxdHandler{
	devLxdHandler{"/", func(c container, r *http.Request) *devLxdResponse {
		return okResponse([]string{"/1.0"}, "json")
	}},
	devLxdHandler{"/1.0", func(c container, r *http.Request) *devLxdResponse {
		return okResponse(shared.Jmap{"api_version": shared.APIVersion}, "json")
	}},
	configGet,
	configKeyGet,
	metadataGet,
	/* TODO: events */
}

func hoistReq(f func(container, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		conn := extractUnderlyingConn(w)
		pid, ok := pidMapper.m[conn]
		if !ok {
			http.Error(w, pidNotInContainerErr.Error(), 500)
			return
		}

		c, err := findContainerForPid(pid, d)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		resp := f(c, r)
		if resp.code != http.StatusOK {
			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
		} else if resp.ctype == "json" {
			w.Header().Set("Content-Type", "application/json")
			WriteJSON(w, resp.content)
		} else {
			w.Header().Set("Content-Type", "application/octet-stream")
			fmt.Fprintf(w, resp.content.(string))
		}
	}
}

func createAndBindDevLxd() (*net.UnixListener, error) {
	sockFile := path.Join(shared.VarPath("devlxd"), "sock")

	/*
	 * If this socket exists, that means a previous lxd died and didn't
	 * clean up after itself. We assume that the LXD is actually dead if we
	 * get this far, since StartDaemon() tries to connect to the actual lxd
	 * socket to make sure that it is actually dead. So, it is safe to
	 * remove it here without any checks.
	 *
	 * Also, it would be nice to SO_REUSEADDR here so we don't have to
	 * delete the socket, but we can't:
	 *   http://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
	 *
	 * Note that this will force clients to reconnect when LXD is restarted.
	 */
	if err := os.Remove(sockFile); err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	unixAddr, err := net.ResolveUnixAddr("unix", sockFile)
	if err != nil {
		return nil, err
	}

	unixl, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		return nil, err
	}

	if err := os.Chmod(sockFile, 0666); err != nil {
		return nil, err
	}

	return unixl, nil
}

func devLxdServer(d *Daemon) *http.Server {
	m := mux.NewRouter()

	for _, handler := range handlers {
		m.HandleFunc(handler.path, hoistReq(handler.f, d))
	}

	return &http.Server{
		Handler:   m,
		ConnState: pidMapper.ConnStateHandler,
	}
}

/*
 * Everything below here is the guts of the unix socket bits. Unfortunately,
 * golang's API does not make this easy. What happens is:
 *
 * 1. We install a ConnState listener on the http.Server, which does the
 *    initial unix socket credential exchange. When we get a connection started
 *    event, we use SO_PEERCRED to extract the creds for the socket.
 *
 * 2. We store a map from the connection pointer to the pid for that
 *    connection, so that once the HTTP negotiation occurrs and we get a
 *    ResponseWriter, we know (because we negotiated on the first byte) which
 *    pid the connection belogs to.
 *
 * 3. Regular HTTP negotiation and dispatch occurs via net/http.
 *
 * 4. When rendering the response via ResponseWriter, we match its underlying
 *    connection against what we stored in step (2) to figure out which container
 *    it came from.
 */

/*
 * We keep this in a global so that we can reference it from the server and
 * from our http handlers, since there appears to be no way to pass information
 * around here.
 */
var pidMapper = ConnPidMapper{m: map[*net.UnixConn]int32{}}

type ConnPidMapper struct {
	m map[*net.UnixConn]int32
}

func (m *ConnPidMapper) ConnStateHandler(conn net.Conn, state http.ConnState) {
	unixConn := conn.(*net.UnixConn)
	switch state {
	case http.StateNew:
		pid, err := getPid(unixConn)
		if err != nil {
			shared.Debugf("Error getting pid for conn %s", err)
		} else {
			m.m[unixConn] = pid
		}
	case http.StateActive:
		return
	case http.StateIdle:
		return
	case http.StateHijacked:
		/*
		 * The "Hijacked" state indicates that the connection has been
		 * taken over from net/http. This is useful for things like
		 * developing websocket libraries, who want to upgrade the
		 * connection to a websocket one, and not use net/http any
		 * more. Whatever the case, we want to forget about it since we
		 * won't see it either.
		 */
		delete(m.m, unixConn)
	case http.StateClosed:
		delete(m.m, unixConn)
	default:
		shared.Debugf("Unknown state for connection %s", state)
	}
}

/*
 * I also don't see that golang exports an API to get at the underlying FD, but
 * we need it to get at SO_PEERCRED, so let's grab it.
 */
func extractUnderlyingFd(unixConnPtr *net.UnixConn) int {
	conn := reflect.Indirect(reflect.ValueOf(unixConnPtr))
	netFdPtr := conn.FieldByName("fd")
	netFd := reflect.Indirect(netFdPtr)
	fd := netFd.FieldByName("sysfd")
	return int(fd.Int())
}

func getPid(conn *net.UnixConn) (int32, error) {
	fd := extractUnderlyingFd(conn)

	_, _, pid, err := getUcred(fd)
	if err != nil {
		return 0, err
	}

	return pid, nil
}

/*
 * As near as I can tell, there is no nice way of extracting an underlying
 * net.Conn (or in our case, net.UnixConn) from an http.Request or
 * ResponseWriter without hijacking it [1]. Since we want to send and recieve
 * unix creds to figure out which container this request came from, we need to
 * do this.
 *
 * [1]: https://groups.google.com/forum/#!topic/golang-nuts/_FWdFXJa6QA
 */
func extractUnderlyingConn(w http.ResponseWriter) *net.UnixConn {
	v := reflect.Indirect(reflect.ValueOf(w))
	connPtr := v.FieldByName("conn")
	conn := reflect.Indirect(connPtr)
	rwc := conn.FieldByName("rwc")

	netConnPtr := (*net.Conn)(unsafe.Pointer(rwc.UnsafeAddr()))
	unixConnPtr := (*netConnPtr).(*net.UnixConn)

	return unixConnPtr
}

var pidNotInContainerErr = fmt.Errorf("pid not in container?")

func findContainerForPid(pid int32, d *Daemon) (container, error) {
	/*
	 * Try and figure out which container a pid is in. There is probably a
	 * better way to do this. Based on rharper's initial performance
	 * metrics, looping over every container and calling newLxdContainer is
	 * expensive, so I wanted to avoid that if possible, so this happens in
	 * a two step process:
	 *
	 * 1. Walk up the process tree until you see something that looks like
	 *    an lxc monitor process and extract its name from there.
	 *
	 * 2. If this fails, it may be that someone did an `lxc exec foo bash`,
	 *    so the process isn't actually a decendant of the container's
	 *    init. In this case we just look through all the containers until
	 *    we find an init with a matching pid namespace. This is probably
	 *    uncommon, so hopefully the slowness won't hurt us.
	 */

	origpid := pid

	for pid > 1 {
		cmdline, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
		if err != nil {
			return nil, err
		}

		if strings.HasPrefix(string(cmdline), "[lxc monitor]") {
			// container names can't have spaces
			parts := strings.Split(string(cmdline), " ")
			name := strings.TrimSuffix(parts[len(parts)-1], "\x00")

			return containerLoadByName(d, name)
		}

		status, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
		if err != nil {
			return nil, err
		}

		re := regexp.MustCompile("PPid:\\s*([0-9]*)")
		for _, line := range strings.Split(string(status), "\n") {
			m := re.FindStringSubmatch(line)
			if m != nil && len(m) > 1 {
				result, err := strconv.Atoi(m[1])
				if err != nil {
					return nil, err
				}

				pid = int32(result)
				break
			}
		}
	}

	origPidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", origpid))
	if err != nil {
		return nil, err
	}

	containers, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return nil, err
	}

	for _, container := range containers {
		c, err := containerLoadByName(d, container)
		if err != nil {
			return nil, err
		}

		if !c.IsRunning() {
			continue
		}

		initpid := c.InitPID()
		pidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", initpid))
		if err != nil {
			return nil, err
		}

		if origPidNs == pidNs {
			return c, nil
		}
	}

	return nil, pidNotInContainerErr
}
<|endoftext|>


<commit_before>// A module for syncing secrets with the server, such as SKB PGP keys,
// and server-halves of our various secret keys.
package libkb

import (
	"encoding/hex"
	"fmt"
	"sync"
)

type ServerPrivateKey struct {
	Kid     string `json:"kid"`
	KeyType int    `json:"key_type"`
	Bundle  string `json:"bundle"`
	Mtime   int    `json:"mtime"`
	Ctime   int    `json:"ctime"`
	KeyBits int    `json:"key_bits"`
	KeyAlgo int    `json:"key_algo"`
}

type ServerPrivateKeyMap map[string]ServerPrivateKey

type DeviceKey struct {
	Type          int    `json:"type"`
	CTime         int64  `json:"ctime"`
	MTime         int64  `json:"mtime"`
	Description   string `json:"description"`
	Status        int    `json:"status"`
	LksServerHalf string `json:"lks_server_half"`
}

type DeviceKeyMap map[string]DeviceKey

type ServerPrivateKeys struct {
	Status      ApiStatus           `json:"status"`
	Version     int                 `json:"version"`
	Mtime       *int                `json:"mtime"`
	PrivateKeys ServerPrivateKeyMap `json:"private_keys"`
	Devices     DeviceKeyMap        `json:"devices"`
}

type SecretSyncer struct {
	// Locks the whole object
	sync.Mutex
	Uid   *UID
	dirty bool
	keys  *ServerPrivateKeys
}

func (ss *SecretSyncer) Clear() error {
	ss.Lock()
	defer ss.Unlock()

	err := ss.store()
	ss.Uid = nil
	ss.keys = nil

	return err
}

// Load loads a set of secret keys from storage and then checks if there are
// updates on the server.  If there are, it will sync and store them.
func (ss *SecretSyncer) Load(uid UID) (err error) {

	ss.Lock()
	defer ss.Unlock()

	uid_s := uid.String()

	G.Log.Debug("+ SecretSyncer.Load(%s)", uid_s)
	defer func() {
		G.Log.Debug("- SecretSyncer.Load(%s) -> %s", uid_s, ErrToOk(err))
	}()

	if ss.Uid != nil && !ss.Uid.Eq(uid) {
		err = UidMismatchError{fmt.Sprintf("%s != %s", ss.Uid, uid)}
		return
	}
	ss.Uid = &uid

	if err = ss.loadFromStorage(); err != nil {
		return
	}
	if !G.Session.IsLoggedIn() {
		G.Log.Debug("| Won't sync with server since we're not logged in")
		return
	}
	if err = ss.syncFromServer(); err != nil {
		return
	}
	if err = ss.store(); err != nil {
		return
	}
	return
}

func (ss *SecretSyncer) loadFromStorage() (err error) {
	var tmp ServerPrivateKeys
	var found bool
	found, err = G.LocalDb.GetInto(&tmp, ss.dbKey())
	G.Log.Debug("| loadFromStorage -> %v, %s", found, ErrToOk(err))
	if found {
		G.Log.Debug("| Loaded version %d", tmp.Version)
	} else if err == nil {
		G.Log.Debug("| Loaded empty record set")
	}
	if err == nil {
		ss.keys = &tmp
	}
	return
}

func (ss *SecretSyncer) syncFromServer() (err error) {
	hargs := HttpArgs{}

	// Load the session for the following API request.
	if err = G.Session.Load(); err != nil {
		return
	}

	if ss.keys != nil {
		hargs.Add("version", I{ss.keys.Version})
	}
	var res *ApiRes
	res, err = G.API.Get(ApiArg{
		Endpoint:    "key/fetch_private",
		Args:        hargs,
		NeedSession: true,
	})
	G.Log.Debug("| syncFromServer -> %s", ErrToOk(err))
	if err != nil {
		return
	}

	var obj ServerPrivateKeys
	if err = res.Body.UnmarshalAgain(&obj); err != nil {
		return
	}

	if ss.keys == nil || obj.Version > ss.keys.Version {
		G.Log.Debug("| upgrade to version -> %d", obj.Version)
		ss.keys = &obj
		ss.dirty = true
	}

	return
}

func (ss *SecretSyncer) dbKey() DbKey {
	return DbKey{Typ: DB_USER_SECRET_KEYS, Key: ss.Uid.String()}
}

func (ss *SecretSyncer) store() (err error) {
	if !ss.dirty {
		return
	}
	if err = G.LocalDb.PutObj(ss.dbKey(), nil, ss.keys); err != nil {
		return
	}
	ss.dirty = false
	return
}

// FindActiveKey examines the synced keys, looking for one that's currently active.
// Returns ret=nil if none was found.
func (ss *SecretSyncer) FindActiveKey(ckf *ComputedKeyFamily) (ret *SKB, err error) {
	for _, key := range ss.keys.PrivateKeys {
		if ret, _ = key.FindActiveKey(ckf); ret != nil {
			return
		}
	}
	return
}

func (k *ServerPrivateKey) FindActiveKey(ckf *ComputedKeyFamily) (ret *SKB, err error) {
	var kid KID
	var packet *KeybasePacket

	if kid, err = ImportKID(k.Kid); err != nil {
		return
	}
	if ckf.IsKidActive(kid) != DLG_SIBKEY {
		return
	}
	if packet, err = DecodeArmoredPacket(k.Bundle); err != nil && packet == nil {
		return
	}
	return packet.ToSKB()
}

func (ss *SecretSyncer) FindDevice(id *DeviceID) (DeviceKey, error) {
	dev, ok := ss.keys.Devices[id.String()]
	if !ok {
		return DeviceKey{}, fmt.Errorf("No device found for ID = %s", id)
	}
	return dev, nil
}

// FindDetKeySrvHalf locates the detkey matching kt and returns
// the bundle, which is the server half of the detkey.
func (ss *SecretSyncer) FindDetKeySrvHalf(kt KeyType) ([]byte, error) {
	if kt != KEY_TYPE_KB_NACL_EDDSA_SERVER_HALF && kt != KEY_TYPE_KB_NACL_DH_SERVER_HALF {
		return nil, fmt.Errorf("invalid key type")
	}
	if ss.keys == nil {
		return nil, fmt.Errorf("no keys")
	}
	for _, key := range ss.keys.PrivateKeys {
		if KeyType(key.KeyType) != kt {
			continue
		}
		return hex.DecodeString(key.Bundle)
	}
	return nil, NotFoundError{msg: "detkey not found"}
}
<commit_msg>device types as strings, everwhere..<commit_after>// A module for syncing secrets with the server, such as SKB PGP keys,
// and server-halves of our various secret keys.
package libkb

import (
	"encoding/hex"
	"fmt"
	"sync"
)

type ServerPrivateKey struct {
	Kid     string `json:"kid"`
	KeyType int    `json:"key_type"`
	Bundle  string `json:"bundle"`
	Mtime   int    `json:"mtime"`
	Ctime   int    `json:"ctime"`
	KeyBits int    `json:"key_bits"`
	KeyAlgo int    `json:"key_algo"`
}

type ServerPrivateKeyMap map[string]ServerPrivateKey

type DeviceKey struct {
	Type          string `json:"type"`
	CTime         int64  `json:"ctime"`
	MTime         int64  `json:"mtime"`
	Description   string `json:"description"`
	Status        int    `json:"status"`
	LksServerHalf string `json:"lks_server_half"`
}

type DeviceKeyMap map[string]DeviceKey

type ServerPrivateKeys struct {
	Status      ApiStatus           `json:"status"`
	Version     int                 `json:"version"`
	Mtime       *int                `json:"mtime"`
	PrivateKeys ServerPrivateKeyMap `json:"private_keys"`
	Devices     DeviceKeyMap        `json:"devices"`
}

type SecretSyncer struct {
	// Locks the whole object
	sync.Mutex
	Uid   *UID
	dirty bool
	keys  *ServerPrivateKeys
}

func (ss *SecretSyncer) Clear() error {
	ss.Lock()
	defer ss.Unlock()

	err := ss.store()
	ss.Uid = nil
	ss.keys = nil

	return err
}

// Load loads a set of secret keys from storage and then checks if there are
// updates on the server.  If there are, it will sync and store them.
func (ss *SecretSyncer) Load(uid UID) (err error) {

	ss.Lock()
	defer ss.Unlock()

	uid_s := uid.String()

	G.Log.Debug("+ SecretSyncer.Load(%s)", uid_s)
	defer func() {
		G.Log.Debug("- SecretSyncer.Load(%s) -> %s", uid_s, ErrToOk(err))
	}()

	if ss.Uid != nil && !ss.Uid.Eq(uid) {
		err = UidMismatchError{fmt.Sprintf("%s != %s", ss.Uid, uid)}
		return
	}
	ss.Uid = &uid

	if err = ss.loadFromStorage(); err != nil {
		return
	}
	if !G.Session.IsLoggedIn() {
		G.Log.Debug("| Won't sync with server since we're not logged in")
		return
	}
	if err = ss.syncFromServer(); err != nil {
		return
	}
	if err = ss.store(); err != nil {
		return
	}
	return
}

func (ss *SecretSyncer) loadFromStorage() (err error) {
	var tmp ServerPrivateKeys
	var found bool
	found, err = G.LocalDb.GetInto(&tmp, ss.dbKey())
	G.Log.Debug("| loadFromStorage -> %v, %s", found, ErrToOk(err))
	if found {
		G.Log.Debug("| Loaded version %d", tmp.Version)
	} else if err == nil {
		G.Log.Debug("| Loaded empty record set")
	}
	if err == nil {
		ss.keys = &tmp
	}
	return
}

func (ss *SecretSyncer) syncFromServer() (err error) {
	hargs := HttpArgs{}

	// Load the session for the following API request.
	if err = G.Session.Load(); err != nil {
		return
	}

	if ss.keys != nil {
		hargs.Add("version", I{ss.keys.Version})
	}
	var res *ApiRes
	res, err = G.API.Get(ApiArg{
		Endpoint:    "key/fetch_private",
		Args:        hargs,
		NeedSession: true,
	})
	G.Log.Debug("| syncFromServer -> %s", ErrToOk(err))
	if err != nil {
		return
	}

	var obj ServerPrivateKeys
	if err = res.Body.UnmarshalAgain(&obj); err != nil {
		return
	}

	if ss.keys == nil || obj.Version > ss.keys.Version {
		G.Log.Debug("| upgrade to version -> %d", obj.Version)
		ss.keys = &obj
		ss.dirty = true
	}

	return
}

func (ss *SecretSyncer) dbKey() DbKey {
	return DbKey{Typ: DB_USER_SECRET_KEYS, Key: ss.Uid.String()}
}

func (ss *SecretSyncer) store() (err error) {
	if !ss.dirty {
		return
	}
	if err = G.LocalDb.PutObj(ss.dbKey(), nil, ss.keys); err != nil {
		return
	}
	ss.dirty = false
	return
}

// FindActiveKey examines the synced keys, looking for one that's currently active.
// Returns ret=nil if none was found.
func (ss *SecretSyncer) FindActiveKey(ckf *ComputedKeyFamily) (ret *SKB, err error) {
	for _, key := range ss.keys.PrivateKeys {
		if ret, _ = key.FindActiveKey(ckf); ret != nil {
			return
		}
	}
	return
}

func (k *ServerPrivateKey) FindActiveKey(ckf *ComputedKeyFamily) (ret *SKB, err error) {
	var kid KID
	var packet *KeybasePacket

	if kid, err = ImportKID(k.Kid); err != nil {
		return
	}
	if ckf.IsKidActive(kid) != DLG_SIBKEY {
		return
	}
	if packet, err = DecodeArmoredPacket(k.Bundle); err != nil && packet == nil {
		return
	}
	return packet.ToSKB()
}

func (ss *SecretSyncer) FindDevice(id *DeviceID) (DeviceKey, error) {
	dev, ok := ss.keys.Devices[id.String()]
	if !ok {
		return DeviceKey{}, fmt.Errorf("No device found for ID = %s", id)
	}
	return dev, nil
}

// FindDetKeySrvHalf locates the detkey matching kt and returns
// the bundle, which is the server half of the detkey.
func (ss *SecretSyncer) FindDetKeySrvHalf(kt KeyType) ([]byte, error) {
	if kt != KEY_TYPE_KB_NACL_EDDSA_SERVER_HALF && kt != KEY_TYPE_KB_NACL_DH_SERVER_HALF {
		return nil, fmt.Errorf("invalid key type")
	}
	if ss.keys == nil {
		return nil, fmt.Errorf("no keys")
	}
	for _, key := range ss.keys.PrivateKeys {
		if KeyType(key.KeyType) != kt {
			continue
		}
		return hex.DecodeString(key.Bundle)
	}
	return nil, NotFoundError{msg: "detkey not found"}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/lomik/elock"
)

const APP = "elock"
const VERSION = "0.1"

type Config struct {
	EtcdEndpoints []string `json:"etcd-endpoints"`
	EtcdRoot      string   `json:"etcd-root"`
	EtcdTTL       string   `json:"etcd-default-ttl"`
	EtcdRefresh   string   `json:"etcd-deafult-refresh"`
}

func main() {
	configFile := flag.String("config", fmt.Sprintf("/usr/local/etc/%s.json", APP), "Config file in json format")
	printConfig := flag.Bool("config-print-default", false, "Print default config")
	nowait := flag.Bool("n", false, "Fail rather than wait")
	slots := flag.Int("s", 1, "Available slots count for lock")
	timeout := flag.Duration("w", 0, "Wait for a limited amount of time")
	version := flag.Bool("V", false, "Display version")
	debug := flag.Bool("debug", false, "Enable debug log")
	ttl := flag.Duration("ttl", 0, "Lock ttl (default from config)")
	refresh := flag.Duration("refresh", 0, "Refresh interval (default from config)")
	minLockTime := flag.Duration("min", 0, "Minimum lock time")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s %s
Usage: %s [options] etcd_key command

`, APP, VERSION, APP)
		flag.PrintDefaults()
	}

	flag.Parse()

	if *version {
		fmt.Println(APP, VERSION)
		return
	}

	config := &Config{
		EtcdEndpoints: []string{"http://localhost:2379"},
		EtcdRoot:      "/elock",
		EtcdTTL:       "1m",
		EtcdRefresh:   "10s",
	}

	if *printConfig {
		b, _ := json.MarshalIndent(config, "", "\t")
		fmt.Println(string(b))
		return
	}

	cfgData, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(cfgData, config)
	if err != nil {
		log.Fatal(err)
	}

	lockTLL, err := time.ParseDuration(config.EtcdTTL)
	if err != nil {
		log.Fatal(err)
	}

	lockRefresh, err := time.ParseDuration(config.EtcdRefresh)
	if err != nil {
		log.Fatal(err)
	}

	if *ttl != 0 {
		lockTLL = *ttl
	}

	if *refresh != 0 {
		lockRefresh = *refresh
	}

	args := flag.Args()

	if len(args) < 2 || *slots < 1 {
		flag.Usage()
		return
	}

	x, err := elock.New(elock.Options{
		EtcdEndpoints: config.EtcdEndpoints,
		Path:          filepath.Join(config.EtcdRoot, args[0]),
		Slots:         *slots,
		TTL:           lockTLL,
		Refresh:       lockRefresh,
		Debug:         *debug,
		MinLockTime:   *minLockTime,
	})

	if err != nil {
		log.Fatal(err)
	}

	if *nowait {
		err = x.LockNoWait()
	} else if *timeout != 0 {
		err = x.LockTimeout(*timeout)
	} else {
		err = x.Lock()
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGSTOP, syscall.SIGTERM)

	go func() {
		for _ = range c {
			// sig is a ^C, handle it
			x.Unlock()
			os.Exit(1)
		}
	}()

	if err != nil {
		log.Fatal(err)
	}

	cmd := exec.Command(args[1], args[2:]...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err = cmd.Run()

	x.Unlock()

	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>default config path<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/lomik/elock"
)

const APP = "elock"
const VERSION = "0.1"

type Config struct {
	EtcdEndpoints []string `json:"etcd-endpoints"`
	EtcdRoot      string   `json:"etcd-root"`
	EtcdTTL       string   `json:"etcd-default-ttl"`
	EtcdRefresh   string   `json:"etcd-deafult-refresh"`
}

func main() {
	configFile := flag.String("config", fmt.Sprintf("/etc/%s/%s.json", APP, APP), "Config file in json format")
	printConfig := flag.Bool("config-print-default", false, "Print default config")
	nowait := flag.Bool("n", false, "Fail rather than wait")
	slots := flag.Int("s", 1, "Available slots count for lock")
	timeout := flag.Duration("w", 0, "Wait for a limited amount of time")
	version := flag.Bool("V", false, "Display version")
	debug := flag.Bool("debug", false, "Enable debug log")
	ttl := flag.Duration("ttl", 0, "Lock ttl (default from config)")
	refresh := flag.Duration("refresh", 0, "Refresh interval (default from config)")
	minLockTime := flag.Duration("min", 0, "Minimum lock time")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s %s
Usage: %s [options] etcd_key command

`, APP, VERSION, APP)
		flag.PrintDefaults()
	}

	flag.Parse()

	if *version {
		fmt.Println(APP, VERSION)
		return
	}

	config := &Config{
		EtcdEndpoints: []string{"http://localhost:2379"},
		EtcdRoot:      "/elock",
		EtcdTTL:       "1m",
		EtcdRefresh:   "10s",
	}

	if *printConfig {
		b, _ := json.MarshalIndent(config, "", "\t")
		fmt.Println(string(b))
		return
	}

	cfgData, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(cfgData, config)
	if err != nil {
		log.Fatal(err)
	}

	lockTLL, err := time.ParseDuration(config.EtcdTTL)
	if err != nil {
		log.Fatal(err)
	}

	lockRefresh, err := time.ParseDuration(config.EtcdRefresh)
	if err != nil {
		log.Fatal(err)
	}

	if *ttl != 0 {
		lockTLL = *ttl
	}

	if *refresh != 0 {
		lockRefresh = *refresh
	}

	args := flag.Args()

	if len(args) < 2 || *slots < 1 {
		flag.Usage()
		return
	}

	x, err := elock.New(elock.Options{
		EtcdEndpoints: config.EtcdEndpoints,
		Path:          filepath.Join(config.EtcdRoot, args[0]),
		Slots:         *slots,
		TTL:           lockTLL,
		Refresh:       lockRefresh,
		Debug:         *debug,
		MinLockTime:   *minLockTime,
	})

	if err != nil {
		log.Fatal(err)
	}

	if *nowait {
		err = x.LockNoWait()
	} else if *timeout != 0 {
		err = x.LockTimeout(*timeout)
	} else {
		err = x.Lock()
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGSTOP, syscall.SIGTERM)

	go func() {
		for _ = range c {
			// sig is a ^C, handle it
			x.Unlock()
			os.Exit(1)
		}
	}()

	if err != nil {
		log.Fatal(err)
	}

	cmd := exec.Command(args[1], args[2:]...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err = cmd.Run()

	x.Unlock()

	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package tests

import (
	"testing"

	"github.com/open-policy-agent/frameworks/constraint/pkg/client"
	"github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/remote"
)

func TestRemoteClientE2E(t *testing.T) {
	d, err := remote.New(remote.URL("http://localhost:8181"), remote.Tracing(false))
	if err != nil {
		t.Fatal(err)
	}
	p, err := client.NewProbe(d)
	if err != nil {
		t.Fatal(err)
	}
	for name, f := range p.TestFuncs() {
		t.Run(name, func(t *testing.T) {
			if err := f(); err != nil {
				t.Fatal(err)
			}
		})
	}
}
<commit_msg>Skip broken test TestRemoteClientE2E (#144)<commit_after>package tests

import (
	"testing"

	"github.com/open-policy-agent/frameworks/constraint/pkg/client"
	"github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/remote"
)

func TestRemoteClientE2E(t *testing.T) {
	t.Skip("test does not work yet")

	d, err := remote.New(remote.URL("http://localhost:8181"), remote.Tracing(false))
	if err != nil {
		t.Fatal(err)
	}
	p, err := client.NewProbe(d)
	if err != nil {
		t.Fatal(err)
	}

	for name, f := range p.TestFuncs() {
		t.Run(name, func(t *testing.T) {
			if err := f(); err != nil {
				t.Fatal(err)
			}
		})
	}
}
<|endoftext|>