<commit_before>package lifted

// Quantifier removal

func (d *Domain) ExpandQuants(objs []TypedName) {
	f := newExpandFrame(objs)
	acts := make([]Action, 0, len(d.Actions))
	for i, _ := range d.Actions {
		a := &d.Actions[i]
		a.Precondition = a.Precondition.ExpandQuants(f)
		a.Effect = a.Effect.ExpandQuants(f)
		acts = append(acts, expandParams(f, a, a.Parameters)...)
	}
	d.Actions = acts
}

func expandParams(f *expandFrame, a *Action, ps []TypedName) (acts []Action) {
	if len(ps) == 0 {
		act := Action{
			Name:         a.Name,
			Parameters:   make([]TypedName, len(a.Parameters)),
			Precondition: a.Precondition.ExpandQuants(f),
			Effect:       a.Effect.ExpandQuants(f),
		}
		copy(act.Parameters, a.Parameters)
		return []Action{act}
	}

	pnum := len(a.Parameters) - len(ps)
	saved := a.Parameters[pnum]

	objs := objsOfType(f, saved.Type)
	for _, obj := range objs {
		a.Parameters[pnum].Name = obj
		g := f.push(saved.Name, obj)
		acts = append(acts, expandParams(g, a, ps[1:])...)
	}
	a.Parameters[pnum] = saved

	return
}

func (l *Literal) ExpandQuants(f *expandFrame) *Literal {
	res := &Literal{
		Positive:   l.Positive,
		Name:       l.Name,
		Parameters: make([]Term, len(l.Parameters)),
	}
	for i, t := range l.Parameters {
		if t.Kind == TermConstant {
			res.Parameters[i] = t
			continue
		}
		o, ok := f.lookup(t.Name)
		if !ok {
			res.Parameters[i] = t
			continue
		}
		res.Parameters[i] = Term{
			Kind: TermConstant,
			Name: o,
			Loc:  t.Loc,
		}
	}
	return res
}

func (e ExprTrue) ExpandQuants(*expandFrame) Expr {
	return e
}

func (e ExprFalse) ExpandQuants(*expandFrame) Expr {
	return e
}

func (e *ExprAnd) ExpandQuants(f *expandFrame) Expr {
	l := e.Left.ExpandQuants(f)
	if _, ok := l.(ExprFalse); ok {
		return ExprFalse(0)
	}
	return ExprConj(l, e.Right.ExpandQuants(f))
}

func (e *ExprOr) ExpandQuants(f *expandFrame) Expr {
	l := e.Left.ExpandQuants(f)
	if _, ok := l.(ExprTrue); ok {
		return ExprTrue(1)
	}
	return ExprDisj(l, e.Right.ExpandQuants(f))
}

func (e *ExprNot) ExpandQuants(f *expandFrame) Expr {
	return ExprNeg(e.Expr.ExpandQuants(f))
}

func (e *ExprQuant) ExpandQuants(f *expandFrame, base Expr,
comb func(Expr, Expr) Expr) Expr {
	objs := objsOfType(f, e.Variable.Type)
	seq := base
	for _, obj := range objs {
		g := f.push(e.Variable.Name, obj)
		seq = comb(e.Expr.ExpandQuants(g), seq)
	}
	return seq
}

func (e *ExprForall) ExpandQuants(f *expandFrame) Expr {
	return (*ExprQuant)(e).ExpandQuants(f, ExprTrue(1), ExprConj)
}

func (e *ExprExists) ExpandQuants(f *expandFrame) Expr {
	return (*ExprQuant)(e).ExpandQuants(f, ExprFalse(0), ExprDisj)
}

func (e *ExprLiteral) ExpandQuants(f *expandFrame) Expr {
	return (*ExprLiteral)((*Literal)(e).ExpandQuants(f))
}

func (EffectNone) ExpandQuants(*expandFrame) Effect {
	return EffectNone(0)
}

func (e *EffectAnd) ExpandQuants(f *expandFrame) Effect {
	return EffectConj(e.Left.ExpandQuants(f), e.Right.ExpandQuants(f))
}

func (e *EffectForall) ExpandQuants(f *expandFrame) Effect {
	objs := objsOfType(f, e.Variable.Type)
	seq := Effect(EffectNone(0))
	for _, obj := range objs {
		g := f.push(e.Variable.Name, obj)
		seq = EffectConj(e.Effect.ExpandQuants(g), seq)
	}
	return seq
}

func (e *EffectWhen) ExpandQuants(f *expandFrame) Effect {
	return &EffectWhen{
		Condition:   e.Condition.ExpandQuants(f),
		EffectUnary: EffectUnary{e.Effect.ExpandQuants(f)},
	}
}

func (e *EffectLiteral) ExpandQuants(f *expandFrame) Effect {
	return (*EffectLiteral)((*Literal)(e).ExpandQuants(f))
}

func (e *EffectAssign) ExpandQuants(f *expandFrame) Effect {
	return &EffectAssign{
		Op:   e.Op,
		Lval: e.Lval,
		Rval: e.Rval,
	}
}

type expandFrame struct {
	objsByType map[string][]string
	variable   string
	object     string
	up         *expandFrame
}

func newExpandFrame(objs []TypedName) *expandFrame {
	objsByType := make(map[string][]string)

	for _, obj := range objs {
		for _, t := range obj.Type {
			lst, _ := objsByType[t]
			objsByType[t] = append(lst, obj.Name)
		}
	}

	return &expandFrame{objsByType: objsByType}
}

func (f *expandFrame) push(vr string, obj string) *expandFrame {
	return &expandFrame{
		objsByType: f.objsByType,
		variable:   vr,
		object:     obj,
		up:         f,
	}
}

func (f *expandFrame) lookup(vr string) (string, bool) {
	if f == nil {
		return "", false
	}
	if f.variable == vr {
		return f.object, true
	}
	return f.up.lookup(vr)
}

func objsOfType(f *expandFrame, typ []string) (objs []string) {
	seen := make(map[string]bool)
	for _, t := range typ {
		for _, o := range f.objsByType[t] {
			if _, ok := seen[o]; ok {
				continue
			}
			seen[o] = true
			objs = append(objs, o)
		}
	}
	return
}
<commit_msg>Drop actions with false preconditions or no effect.<commit_after>package lifted

// Quantifier removal

func (d *Domain) ExpandQuants(objs []TypedName) {
	f := newExpandFrame(objs)
	acts := make([]Action, 0, len(d.Actions))
	for i, _ := range d.Actions {
		a := &d.Actions[i]
		a.Precondition = a.Precondition.ExpandQuants(f)
		a.Effect = a.Effect.ExpandQuants(f)
		acts = append(acts, expandParams(f, a, a.Parameters)...)
	}
	d.Actions = acts
}

func expandParams(f *expandFrame, a *Action, ps []TypedName) (acts []Action) {
	if len(ps) == 0 {
		prec := a.Precondition.ExpandQuants(f)
		if _, ok := prec.(ExprFalse); ok {
			return
		}
		eff := a.Effect.ExpandQuants(f)
		if _, ok := eff.(EffectNone); ok {
			return
		}
		act := Action{
			Name:         a.Name,
			Parameters:   make([]TypedName, len(a.Parameters)),
			Precondition: prec,
			Effect:       eff,
		}
		copy(act.Parameters, a.Parameters)
		return []Action{act}
	}

	pnum := len(a.Parameters) - len(ps)
	saved := a.Parameters[pnum]

	objs := objsOfType(f, saved.Type)
	for _, obj := range objs {
		a.Parameters[pnum].Name = obj
		g := f.push(saved.Name, obj)
		acts = append(acts, expandParams(g, a, ps[1:])...)
	}
	a.Parameters[pnum] = saved

	return
}

func (l *Literal) ExpandQuants(f *expandFrame) *Literal {
	res := &Literal{
		Positive:   l.Positive,
		Name:       l.Name,
		Parameters: make([]Term, len(l.Parameters)),
	}
	for i, t := range l.Parameters {
		if t.Kind == TermConstant {
			res.Parameters[i] = t
			continue
		}
		o, ok := f.lookup(t.Name)
		if !ok {
			res.Parameters[i] = t
			continue
		}
		res.Parameters[i] = Term{
			Kind: TermConstant,
			Name: o,
			Loc:  t.Loc,
		}
	}
	return res
}

func (e ExprTrue) ExpandQuants(*expandFrame) Expr {
	return e
}

func (e ExprFalse) ExpandQuants(*expandFrame) Expr {
	return e
}

func (e *ExprAnd) ExpandQuants(f *expandFrame) Expr {
	l := e.Left.ExpandQuants(f)
	if _, ok := l.(ExprFalse); ok {
		return ExprFalse(0)
	}
	return ExprConj(l, e.Right.ExpandQuants(f))
}

func (e *ExprOr) ExpandQuants(f *expandFrame) Expr {
	l := e.Left.ExpandQuants(f)
	if _, ok := l.(ExprTrue); ok {
		return ExprTrue(1)
	}
	return ExprDisj(l, e.Right.ExpandQuants(f))
}

func (e *ExprNot) ExpandQuants(f *expandFrame) Expr {
	return ExprNeg(e.Expr.ExpandQuants(f))
}

func (e *ExprQuant) ExpandQuants(f *expandFrame, base Expr,
comb func(Expr, Expr) Expr) Expr {
	objs := objsOfType(f, e.Variable.Type)
	seq := base
	for _, obj := range objs {
		g := f.push(e.Variable.Name, obj)
		seq = comb(e.Expr.ExpandQuants(g), seq)
	}
	return seq
}

func (e *ExprForall) ExpandQuants(f *expandFrame) Expr {
	return (*ExprQuant)(e).ExpandQuants(f, ExprTrue(1), ExprConj)
}

func (e *ExprExists) ExpandQuants(f *expandFrame) Expr {
	return (*ExprQuant)(e).ExpandQuants(f, ExprFalse(0), ExprDisj)
}

func (e *ExprLiteral) ExpandQuants(f *expandFrame) Expr {
	return (*ExprLiteral)((*Literal)(e).ExpandQuants(f))
}

func (EffectNone) ExpandQuants(*expandFrame) Effect {
	return EffectNone(0)
}

func (e *EffectAnd) ExpandQuants(f *expandFrame) Effect {
	return EffectConj(e.Left.ExpandQuants(f), e.Right.ExpandQuants(f))
}

func (e *EffectForall) ExpandQuants(f *expandFrame) Effect {
	objs := objsOfType(f, e.Variable.Type)
	seq := Effect(EffectNone(0))
	for _, obj := range objs {
		g := f.push(e.Variable.Name, obj)
		seq = EffectConj(e.Effect.ExpandQuants(g), seq)
	}
	return seq
}

func (e *EffectWhen) ExpandQuants(f *expandFrame) Effect {
	return &EffectWhen{
		Condition:   e.Condition.ExpandQuants(f),
		EffectUnary: EffectUnary{e.Effect.ExpandQuants(f)},
	}
}

func (e *EffectLiteral) ExpandQuants(f *expandFrame) Effect {
	return (*EffectLiteral)((*Literal)(e).ExpandQuants(f))
}

func (e *EffectAssign) ExpandQuants(f *expandFrame) Effect {
	return &EffectAssign{
		Op:   e.Op,
		Lval: e.Lval,
		Rval: e.Rval,
	}
}

type expandFrame struct {
	objsByType map[string][]string
	variable   string
	object     string
	up         *expandFrame
}

func newExpandFrame(objs []TypedName) *expandFrame {
	objsByType := make(map[string][]string)

	for _, obj := range objs {
		for _, t := range obj.Type {
			lst, _ := objsByType[t]
			objsByType[t] = append(lst, obj.Name)
		}
	}

	return &expandFrame{objsByType: objsByType}
}

func (f *expandFrame) push(vr string, obj string) *expandFrame {
	return &expandFrame{
		objsByType: f.objsByType,
		variable:   vr,
		object:     obj,
		up:         f,
	}
}

func (f *expandFrame) lookup(vr string) (string, bool) {
	if f == nil {
		return "", false
	}
	if f.variable == vr {
		return f.object, true
	}
	return f.up.lookup(vr)
}

func objsOfType(f *expandFrame, typ []string) (objs []string) {
	seen := make(map[string]bool)
	for _, t := range typ {
		for _, o := range f.objsByType[t] {
			if _, ok := seen[o]; ok {
				continue
			}
			seen[o] = true
			objs = append(objs, o)
		}
	}
	return
}
<|endoftext|>


<commit_before>package devastator

import (
	"strconv"
	"testing"
)

func TestLen(t *testing.T) {
	a, _ := strconv.Atoi("12344324")
	t.Log(a)
}

// todo: if we are going to expose raw Listener, this should be in integration tests, otherwise Listener should be private
// func TestListener(t *testing.T) {
// 	msg1 := "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
// 	msg2 := "In sit amet lectus felis, at pellentesque turpis."
// 	msg3 := "Nunc urna enim, cursus varius aliquet ac, imperdiet eget tellus."
// 	msg4 := randString(45000)
// 	msg5 := randString(500000)
//
// 	host := "localhost:" + Conf.App.Port
// 	cert, privKey, _ := genCert("localhost", 0, nil, nil, 512, "localhost", "devastator")
// 	l, err := Listen(cert, privKey, host, false)
// 	if err != nil {
// 		t.Fatal(err)
// 	}
//
// 	var listenerWG sync.WaitGroup
// 	listenerWG.Add(1)
// 	go func() {
// 		defer listenerWG.Done()
// 		l.Accept(func(conn *Conn, session *Session, msg []byte) {
// 			certs := conn.ConnectionState().PeerCertificates
// 			if len(certs) > 0 {
// 				t.Logf("Client connected with client certificate subject: %v\n", certs[0].Subject)
// 			}
//
// 			m := string(msg)
// 			if m != msg1 && m != msg2 && m != msg3 && m != msg4 && m != msg5 {
// 				t.Fatal("Sent and incoming message did not match for message:", m)
// 			}
// 		}, func(conn *Conn, session *Session) {
// 		})
// 	}()
//
// 	roots := x509.NewCertPool()
// 	ok := roots.AppendCertsFromPEM(cert)
// 	if !ok {
// 		panic("failed to parse root certificate")
// 	}
//
// 	tlsConf := &tls.Config{RootCAs: roots}
// 	conn, err := tls.Dial("tcp", host, tlsConf)
// 	if err != nil {
// 		t.Fatal(err)
// 	}
//
// 	newconn := NewConn(conn, 0, 0, 0, false)
//
// 	send(t, newconn, "ping")
// 	send(t, newconn, msg1)
// 	send(t, newconn, msg1)
// 	send(t, newconn, msg2)
// 	send(t, newconn, msg3)
// 	send(t, newconn, msg4)
// 	send(t, newconn, msg1)
// 	send(t, newconn, msg5)
// 	send(t, newconn, msg1)
// 	send(t, newconn, "close")
//
// 	l.Close()
// 	listenerWG.Wait()
//
// 	l.reqWG.Wait()
//
// 	newconn.Close()
// 	l.connWG.Wait()
//
// 	// t.Logf("\nconn:\n%+v\n\n", conn)
// 	// t.Logf("\nconn.ConnectionState():\n%+v\n\n", conn.ConnectionState())
// 	// t.Logf("\ntls.Config:\n%+v\n\n", tlsConf)
// }

// func TestClientDisconnect(t *testing.T) {
// 	// todo: we need to verify that events occur in the order that we want them (either via event hooks or log analysis)
// 	// this seems like a listener test than a integration test from a client perspective
// 	s := getServer(t)
// 	c := getClientConnWithClientCert(t)
// 	if err := c.Close(); err != nil {
// 		t.Fatal("Failed to close the client connection:", err)
// 	}
// 	if err := s.Stop(); err != nil {
// 		t.Fatal("Failed to stop the server:", err)
// 	}
// 	wg.Wait()
// }

func send(t *testing.T, conn *Conn, msg string) {
	n, err := conn.Write([]byte(msg))
	if err != nil {
		t.Fatal(err)
	}
	if n < 100 {
		t.Logf("Sent message to listener from client: %v (%v bytes)", msg, n)
	} else {
		t.Logf("Sent message to listener from client: ... (%v bytes)", n)
	}
}
<commit_msg>fix up package listener port collision<commit_after>package devastator

import (
	"crypto/tls"
	"crypto/x509"
	"strconv"
	"sync"
	"testing"
)

func TestLen(t *testing.T) {
	a, _ := strconv.Atoi("12344324")
	t.Log(a)
}

// todo: if we are going to expose raw Listener, this should be in integration tests, otherwise Listener should be private
func TestListener(t *testing.T) {
	msg1 := "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
	msg2 := "In sit amet lectus felis, at pellentesque turpis."
	msg3 := "Nunc urna enim, cursus varius aliquet ac, imperdiet eget tellus."
	msg4 := randString(45000)
	msg5 := randString(500000)

	host := "localhost:3009"
	cert, privKey, _ := genCert("localhost", 0, nil, nil, 512, "localhost", "devastator")
	l, err := Listen(cert, privKey, host, false)
	if err != nil {
		t.Fatal(err)
	}

	var listenerWG sync.WaitGroup
	listenerWG.Add(1)
	go func() {
		defer listenerWG.Done()
		l.Accept(func(conn *Conn, session *Session, msg []byte) {
			certs := conn.ConnectionState().PeerCertificates
			if len(certs) > 0 {
				t.Logf("Client connected with client certificate subject: %v\n", certs[0].Subject)
			}

			m := string(msg)
			if m != msg1 && m != msg2 && m != msg3 && m != msg4 && m != msg5 {
				t.Fatal("Sent and incoming message did not match for message:", m)
			}
		}, func(conn *Conn, session *Session) {
		})
	}()

	roots := x509.NewCertPool()
	ok := roots.AppendCertsFromPEM(cert)
	if !ok {
		panic("failed to parse root certificate")
	}

	tlsConf := &tls.Config{RootCAs: roots}
	conn, err := tls.Dial("tcp", host, tlsConf)
	if err != nil {
		t.Fatal(err)
	}

	newconn := NewConn(conn, 0, 0, 0, false)

	send(t, newconn, "ping")
	send(t, newconn, msg1)
	send(t, newconn, msg1)
	send(t, newconn, msg2)
	send(t, newconn, msg3)
	send(t, newconn, msg4)
	send(t, newconn, msg1)
	send(t, newconn, msg5)
	send(t, newconn, msg1)
	send(t, newconn, "close")

	l.Close()
	listenerWG.Wait()

	l.reqWG.Wait()

	newconn.Close()
	l.connWG.Wait()

	// t.Logf("\nconn:\n%+v\n\n", conn)
	// t.Logf("\nconn.ConnectionState():\n%+v\n\n", conn.ConnectionState())
	// t.Logf("\ntls.Config:\n%+v\n\n", tlsConf)
}

// func TestClientDisconnect(t *testing.T) {
// 	// todo: we need to verify that events occur in the order that we want them (either via event hooks or log analysis)
// 	// this seems like a listener test than a integration test from a client perspective
// 	s := getServer(t)
// 	c := getClientConnWithClientCert(t)
// 	if err := c.Close(); err != nil {
// 		t.Fatal("Failed to close the client connection:", err)
// 	}
// 	if err := s.Stop(); err != nil {
// 		t.Fatal("Failed to stop the server:", err)
// 	}
// 	wg.Wait()
// }

func send(t *testing.T, conn *Conn, msg string) {
	n, err := conn.Write([]byte(msg))
	if err != nil {
		t.Fatal(err)
	}
	if n < 100 {
		t.Logf("Sent message to listener from client: %v (%v bytes)", msg, n)
	} else {
		t.Logf("Sent message to listener from client: ... (%v bytes)", n)
	}
}
<|endoftext|>


<commit_before>package lmdb

import (
	"bytes"
	"reflect"
	"testing"
)

func TestMultiVal(t *testing.T) {
	data := []byte("abcdef")
	m := WrapMulti(data, 2)
	vals := m.Vals()
	if !reflect.DeepEqual(vals, [][]byte{{'a', 'b'}, {'c', 'd'}, {'e', 'f'}}) {
		t.Errorf("unexpected vals: %q", vals)
	}
	size := m.Size()
	if size != 6 {
		t.Errorf("unexpected size: %v (!= %v)", size, 6)
	}
	length := m.Len()
	if length != 3 {
		t.Errorf("unexpected length: %v (!= %v)", length, 3)
	}
	stride := m.Stride()
	if stride != 2 {
		t.Errorf("unexpected stride: %v (!= %v)", stride, 2)
	}
	page := m.Page()
	if !bytes.Equal(page, data) {
		t.Errorf("unexpected page: %v (!= %v)", page, data)
	}
}

func TestMultiVal_panic(t *testing.T) {
	var p bool
	defer func() {
		if e := recover(); e != nil {
			p = true
		}
		if !p {
			t.Errorf("expected a panic")
		}
	}()
	WrapMulti([]byte("123"), 2)
}

func TestValBytes(t *testing.T) {
	ptr, n := valBytes(nil)
	if len(ptr) == 0 {
		t.Errorf("unexpected unadressable slice")
	}
	if n != 0 {
		t.Errorf("unexpected length: %d (expected 0)", n)
	}

	b := []byte("abc")
	ptr, n = valBytes(b)
	if len(ptr) == 0 {
		t.Errorf("unexpected unadressable slice")
	}
	if n != 3 {
		t.Errorf("unexpected length: %d (expected %d)", n, len(b))
	}
}

func TestVal(t *testing.T) {
	orig := []byte("hey hey")
	val := wrapVal(orig)

	p := getBytes(val)
	if !bytes.Equal(p, orig) {
		t.Errorf("getBytes() not the same as original data: %q", p)
	}
	if &p[0] != &orig[0] {
		t.Errorf("getBytes() is not the same slice as original")
	}

	p = getBytesCopy(val)
	if !bytes.Equal(p, orig) {
		t.Errorf("getBytesCopy() not the same as original data: %q", p)
	}
	if &p[0] == &orig[0] {
		t.Errorf("getBytesCopy() overlaps with orignal slice")
	}
}
<commit_msg>lmdb: typo<commit_after>package lmdb

import (
	"bytes"
	"reflect"
	"testing"
)

func TestMultiVal(t *testing.T) {
	data := []byte("abcdef")
	m := WrapMulti(data, 2)
	vals := m.Vals()
	if !reflect.DeepEqual(vals, [][]byte{{'a', 'b'}, {'c', 'd'}, {'e', 'f'}}) {
		t.Errorf("unexpected vals: %q", vals)
	}
	size := m.Size()
	if size != 6 {
		t.Errorf("unexpected size: %v (!= %v)", size, 6)
	}
	length := m.Len()
	if length != 3 {
		t.Errorf("unexpected length: %v (!= %v)", length, 3)
	}
	stride := m.Stride()
	if stride != 2 {
		t.Errorf("unexpected stride: %v (!= %v)", stride, 2)
	}
	page := m.Page()
	if !bytes.Equal(page, data) {
		t.Errorf("unexpected page: %v (!= %v)", page, data)
	}
}

func TestMultiVal_panic(t *testing.T) {
	var p bool
	defer func() {
		if e := recover(); e != nil {
			p = true
		}
		if !p {
			t.Errorf("expected a panic")
		}
	}()
	WrapMulti([]byte("123"), 2)
}

func TestValBytes(t *testing.T) {
	ptr, n := valBytes(nil)
	if len(ptr) == 0 {
		t.Errorf("unexpected unaddressable slice")
	}
	if n != 0 {
		t.Errorf("unexpected length: %d (expected 0)", n)
	}

	b := []byte("abc")
	ptr, n = valBytes(b)
	if len(ptr) == 0 {
		t.Errorf("unexpected unaddressable slice")
	}
	if n != 3 {
		t.Errorf("unexpected length: %d (expected %d)", n, len(b))
	}
}

func TestVal(t *testing.T) {
	orig := []byte("hey hey")
	val := wrapVal(orig)

	p := getBytes(val)
	if !bytes.Equal(p, orig) {
		t.Errorf("getBytes() not the same as original data: %q", p)
	}
	if &p[0] != &orig[0] {
		t.Errorf("getBytes() is not the same slice as original")
	}

	p = getBytesCopy(val)
	if !bytes.Equal(p, orig) {
		t.Errorf("getBytesCopy() not the same as original data: %q", p)
	}
	if &p[0] == &orig[0] {
		t.Errorf("getBytesCopy() overlaps with orignal slice")
	}
}
<|endoftext|>


<commit_before>package lmdb

import (
	"bytes"
	"reflect"
	"testing"
)

func TestMultiVal(t *testing.T) {
	data := []byte("abcdef")
	m := WrapMulti(data, 2)
	vals := m.Vals()
	if !reflect.DeepEqual(vals, [][]byte{{'a', 'b'}, {'c', 'd'}, {'e', 'f'}}) {
		t.Errorf("unexpected vals: %q", vals)
	}
	size := m.Size()
	if size != 6 {
		t.Errorf("unexpected size: %v (!= %v)", size, 6)
	}
	length := m.Len()
	if length != 3 {
		t.Errorf("unexpected length: %v (!= %v)", length, 3)
	}
	stride := m.Stride()
	if stride != 2 {
		t.Errorf("unexpected stride: %v (!= %v)", stride, 2)
	}
	page := m.Page()
	if !bytes.Equal(page, data) {
		t.Errorf("unexpected page: %v (!= %v)", page, data)
	}
}

func TestVal(t *testing.T) {
	orig := "hey hey"
	val := wrapVal([]byte(orig))

	s := val.String()
	if s != orig {
		t.Errorf("String() not the same as original data: %q", s)
	}

	p := val.Bytes()
	if string(p) != orig {
		t.Errorf("Bytes() not the same as original data: %q", p)
	}
}

func TestValCopy(t *testing.T) {
	orig := "hey hey"
	val := wrapVal([]byte(orig))

	s := val.String()
	if s != orig {
		t.Errorf("String() not the same as original data: %q", s)
	}

	p := val.BytesCopy()
	if string(p) != orig {
		t.Errorf("Bytes() not the same as original data: %q", p)
	}
}
<commit_msg>lmdb: add test for WrapMulti error cases and internal val utility funcs<commit_after>package lmdb

import (
	"bytes"
	"reflect"
	"testing"
)

func TestMultiVal(t *testing.T) {
	data := []byte("abcdef")
	m := WrapMulti(data, 2)
	vals := m.Vals()
	if !reflect.DeepEqual(vals, [][]byte{{'a', 'b'}, {'c', 'd'}, {'e', 'f'}}) {
		t.Errorf("unexpected vals: %q", vals)
	}
	size := m.Size()
	if size != 6 {
		t.Errorf("unexpected size: %v (!= %v)", size, 6)
	}
	length := m.Len()
	if length != 3 {
		t.Errorf("unexpected length: %v (!= %v)", length, 3)
	}
	stride := m.Stride()
	if stride != 2 {
		t.Errorf("unexpected stride: %v (!= %v)", stride, 2)
	}
	page := m.Page()
	if !bytes.Equal(page, data) {
		t.Errorf("unexpected page: %v (!= %v)", page, data)
	}
}

func TestMultiVal_panic(t *testing.T) {
	var p bool
	defer func() {
		if e := recover(); e != nil {
			p = true
		}
		if !p {
			t.Errorf("expected a panic")
		}
	}()
	WrapMulti([]byte("123"), 2)
}

func TestValBytes(t *testing.T) {
	ptr, n := valBytes(nil)
	if ptr != nil {
		t.Errorf("unexpected non-nil pointer")
	}
	if n != 0 {
		t.Errorf("unexpected length: %d (expected 0)", n)
	}

	b := []byte("abc")
	ptr, n = valBytes(b)
	if ptr == nil {
		t.Errorf("unexpected nil pointer")
	}
	if n != 3 {
		t.Errorf("unexpected length: %d (expected %d)", n, len(b))
	}
}

func TestVal(t *testing.T) {
	orig := "hey hey"
	val := wrapVal([]byte(orig))

	s := val.String()
	if s != orig {
		t.Errorf("String() not the same as original data: %q", s)
	}

	p := val.Bytes()
	if string(p) != orig {
		t.Errorf("Bytes() not the same as original data: %q", p)
	}
}

func TestValCopy(t *testing.T) {
	orig := "hey hey"
	val := wrapVal([]byte(orig))

	s := val.String()
	if s != orig {
		t.Errorf("String() not the same as original data: %q", s)
	}

	p := val.BytesCopy()
	if string(p) != orig {
		t.Errorf("Bytes() not the same as original data: %q", p)
	}
}
<|endoftext|>


<commit_before>package locking

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/filepathfilter"
	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/lfsapi"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tools/kv"
	"github.com/rubyist/tracerx"
)

var (
	// ErrNoMatchingLocks is an error returned when no matching locks were
	// able to be resolved
	ErrNoMatchingLocks = errors.New("lfs: no matching locks found")
	// ErrLockAmbiguous is an error returned when multiple matching locks
	// were found
	ErrLockAmbiguous = errors.New("lfs: multiple locks found; ambiguous")
)

type LockCacher interface {
	Add(l Lock) error
	RemoveByPath(filePath string) error
	RemoveById(id string) error
	Locks() []Lock
	Clear()
	Save() error
}

// Client is the main interface object for the locking package
type Client struct {
	Remote    string
	RemoteRef *git.Ref
	client    *lockClient
	cache     LockCacher

	lockablePatterns []string
	lockableFilter   *filepathfilter.Filter
	lockableMutex    sync.Mutex

	LocalWorkingDir          string
	LocalGitDir              string
	SetLockableFilesReadOnly bool
}

// NewClient creates a new locking client with the given configuration
// You must call the returned object's `Close` method when you are finished with
// it
func NewClient(remote string, lfsClient *lfsapi.Client) (*Client, error) {
	return &Client{
		Remote: remote,
		client: &lockClient{Client: lfsClient},
		cache:  &nilLockCacher{},
	}, nil
}

func (c *Client) SetupFileCache(path string) error {
	stat, err := os.Stat(path)
	if err != nil {
		return errors.Wrap(err, "init lock cache")
	}

	lockFile := path
	if stat.IsDir() {
		lockFile = filepath.Join(path, "lockcache.db")
	}

	cache, err := NewLockCache(lockFile)
	if err != nil {
		return errors.Wrap(err, "init lock cache")
	}

	c.cache = cache
	return nil
}

// Close this client instance; must be called to dispose of resources
func (c *Client) Close() error {
	return c.cache.Save()
}

// LockFile attempts to lock a file on the current remote
// path must be relative to the root of the repository
// Returns the lock id if successful, or an error
func (c *Client) LockFile(path string) (Lock, error) {
	lockRes, _, err := c.client.Lock(c.Remote, &lockRequest{
		Path: path,
		Ref:  &lockRef{Name: c.RemoteRef.Refspec()},
	})
	if err != nil {
		return Lock{}, errors.Wrap(err, "api")
	}

	if len(lockRes.Message) > 0 {
		if len(lockRes.RequestID) > 0 {
			tracerx.Printf("Server Request ID: %s", lockRes.RequestID)
		}
		return Lock{}, fmt.Errorf("Server unable to create lock: %s", lockRes.Message)
	}

	lock := *lockRes.Lock
	if err := c.cache.Add(lock); err != nil {
		return Lock{}, errors.Wrap(err, "lock cache")
	}

	abs, err := getAbsolutePath(path)
	if err != nil {
		return Lock{}, errors.Wrap(err, "make lockpath absolute")
	}

	// Ensure writeable on return
	if err := tools.SetFileWriteFlag(abs, true); err != nil {
		return Lock{}, err
	}

	return lock, nil
}

// getAbsolutePath takes a repository-relative path and makes it absolute.
//
// For instance, given a repository in /usr/local/src/my-repo and a file called
// dir/foo/bar.txt, getAbsolutePath will return:
//
//   /usr/local/src/my-repo/dir/foo/bar.txt
func getAbsolutePath(p string) (string, error) {
	root, err := git.RootDir()
	if err != nil {
		return "", err
	}

	return filepath.Join(root, p), nil
}

// UnlockFile attempts to unlock a file on the current remote
// path must be relative to the root of the repository
// Force causes the file to be unlocked from other users as well
func (c *Client) UnlockFile(path string, force bool) error {
	id, err := c.lockIdFromPath(path)
	if err != nil {
		return fmt.Errorf("Unable to get lock id: %v", err)
	}

	return c.UnlockFileById(id, force)
}

// UnlockFileById attempts to unlock a lock with a given id on the current remote
// Force causes the file to be unlocked from other users as well
func (c *Client) UnlockFileById(id string, force bool) error {
	unlockRes, _, err := c.client.Unlock(c.RemoteRef, c.Remote, id, force)
	if err != nil {
		return errors.Wrap(err, "api")
	}

	if len(unlockRes.Message) > 0 {
		if len(unlockRes.RequestID) > 0 {
			tracerx.Printf("Server Request ID: %s", unlockRes.RequestID)
		}
		return fmt.Errorf("Server unable to unlock: %s", unlockRes.Message)
	}

	if err := c.cache.RemoveById(id); err != nil {
		return fmt.Errorf("Error caching unlock information: %v", err)
	}

	if unlockRes.Lock != nil {
		abs, err := getAbsolutePath(unlockRes.Lock.Path)
		if err != nil {
			return errors.Wrap(err, "make lockpath absolute")
		}

		// Make non-writeable if required
		if c.SetLockableFilesReadOnly && c.IsFileLockable(unlockRes.Lock.Path) {
			return tools.SetFileWriteFlag(abs, false)
		}
	}

	return nil
}

// Lock is a record of a locked file
type Lock struct {
	// Id is the unique identifier corresponding to this particular Lock. It
	// must be consistent with the local copy, and the server's copy.
	Id string `json:"id"`
	// Path is an absolute path to the file that is locked as a part of this
	// lock.
	Path string `json:"path"`
	// Owner is the identity of the user that created this lock.
	Owner *User `json:"owner,omitempty"`
	// LockedAt is the time at which this lock was acquired.
	LockedAt time.Time `json:"locked_at"`
}

// SearchLocks returns a channel of locks which match the given name/value filter
// If limit > 0 then search stops at that number of locks
// If localOnly = true, don't query the server & report only own local locks
func (c *Client) SearchLocks(filter map[string]string, limit int, localOnly bool) ([]Lock, error) {
	if localOnly {
		return c.searchCachedLocks(filter, limit)
	} else {
		return c.searchRemoteLocks(filter, limit)
	}
}

func (c *Client) VerifiableLocks(ref *git.Ref, limit int) (ourLocks, theirLocks []Lock, err error) {
	if ref == nil {
		ref = c.RemoteRef
	}

	ourLocks = make([]Lock, 0, limit)
	theirLocks = make([]Lock, 0, limit)
	body := &lockVerifiableRequest{
		Ref:   &lockRef{Name: ref.Refspec()},
		Limit: limit,
	}

	c.cache.Clear()

	for {
		list, res, err := c.client.SearchVerifiable(c.Remote, body)
		if res != nil {
			switch res.StatusCode {
			case http.StatusNotFound, http.StatusNotImplemented:
				return ourLocks, theirLocks, errors.NewNotImplementedError(err)
			case http.StatusForbidden:
				return ourLocks, theirLocks, errors.NewAuthError(err)
			}
		}

		if err != nil {
			return ourLocks, theirLocks, err
		}

		if list.Message != "" {
			if len(list.RequestID) > 0 {
				tracerx.Printf("Server Request ID: %s", list.RequestID)
			}
			return ourLocks, theirLocks, fmt.Errorf("Server error searching locks: %s", list.Message)
		}

		for _, l := range list.Ours {
			c.cache.Add(l)
			ourLocks = append(ourLocks, l)
			if limit > 0 && (len(ourLocks)+len(theirLocks)) >= limit {
				return ourLocks, theirLocks, nil
			}
		}

		for _, l := range list.Theirs {
			c.cache.Add(l)
			theirLocks = append(theirLocks, l)
			if limit > 0 && (len(ourLocks)+len(theirLocks)) >= limit {
				return ourLocks, theirLocks, nil
			}
		}

		if list.NextCursor != "" {
			body.Cursor = list.NextCursor
		} else {
			break
		}
	}

	return ourLocks, theirLocks, nil
}

func (c *Client) searchCachedLocks(filter map[string]string, limit int) ([]Lock, error) {
	cachedlocks := c.cache.Locks()
	path, filterByPath := filter["path"]
	id, filterById := filter["id"]
	lockCount := 0
	locks := make([]Lock, 0, len(cachedlocks))
	for _, l := range cachedlocks {
		// Manually filter by Path/Id
		if (filterByPath && path != l.Path) ||
			(filterById && id != l.Id) {
			continue
		}
		locks = append(locks, l)
		lockCount++
		if limit > 0 && lockCount >= limit {
			break
		}
	}
	return locks, nil
}

func (c *Client) searchRemoteLocks(filter map[string]string, limit int) ([]Lock, error) {
	locks := make([]Lock, 0, limit)

	apifilters := make([]lockFilter, 0, len(filter))
	for k, v := range filter {
		apifilters = append(apifilters, lockFilter{Property: k, Value: v})
	}

	query := &lockSearchRequest{
		Filters: apifilters,
		Limit:   limit,
		Refspec: c.RemoteRef.Refspec(),
	}

	for {
		list, _, err := c.client.Search(c.Remote, query)
		if err != nil {
			return locks, errors.Wrap(err, "locking")
		}

		if list.Message != "" {
			if len(list.RequestID) > 0 {
				tracerx.Printf("Server Request ID: %s", list.RequestID)
			}
			return locks, fmt.Errorf("Server error searching for locks: %s", list.Message)
		}

		for _, l := range list.Locks {
			locks = append(locks, l)
			if limit > 0 && len(locks) >= limit {
				// Exit outer loop too
				return locks, nil
			}
		}

		if list.NextCursor != "" {
			query.Cursor = list.NextCursor
		} else {
			break
		}
	}

	return locks, nil
}

// lockIdFromPath makes a call to the LFS API and resolves the ID for the locked
// locked at the given path.
//
// If the API call failed, an error will be returned. If multiple locks matched
// the given path (should not happen during real-world usage), an error will be
// returnd. If no locks matched the given path, an error will be returned.
//
// If the API call is successful, and only one lock matches the given filepath,
// then its ID will be returned, along with a value of "nil" for the error.
func (c *Client) lockIdFromPath(path string) (string, error) {
	list, _, err := c.client.Search(c.Remote, &lockSearchRequest{
		Filters: []lockFilter{
			{Property: "path", Value: path},
		},
	})

	if err != nil {
		return "", err
	}

	switch len(list.Locks) {
	case 0:
		return "", ErrNoMatchingLocks
	case 1:
		return list.Locks[0].Id, nil
	default:
		return "", ErrLockAmbiguous
	}
}

// IsFileLockedByCurrentCommitter returns whether a file is locked by the
// current user, as cached locally
func (c *Client) IsFileLockedByCurrentCommitter(path string) bool {
	filter := map[string]string{"path": path}
	locks, err := c.searchCachedLocks(filter, 1)
	if err != nil {
		tracerx.Printf("Error searching cached locks: %s\nForcing remote search", err)
		locks, _ = c.searchRemoteLocks(filter, 1)
	}
	return len(locks) > 0
}

func init() {
	kv.RegisterTypeForStorage(&Lock{})
}

type nilLockCacher struct{}

func (c *nilLockCacher) Add(l Lock) error {
	return nil
}
func (c *nilLockCacher) RemoveByPath(filePath string) error {
	return nil
}
func (c *nilLockCacher) RemoveById(id string) error {
	return nil
}
func (c *nilLockCacher) Locks() []Lock {
	return nil
}
func (c *nilLockCacher) Clear() {}
func (c *nilLockCacher) Save() error {
	return nil
}
<commit_msg>locking: rename searchCachedLocks to searchLocalLocks<commit_after>package locking

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/filepathfilter"
	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/lfsapi"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tools/kv"
	"github.com/rubyist/tracerx"
)

var (
	// ErrNoMatchingLocks is an error returned when no matching locks were
	// able to be resolved
	ErrNoMatchingLocks = errors.New("lfs: no matching locks found")
	// ErrLockAmbiguous is an error returned when multiple matching locks
	// were found
	ErrLockAmbiguous = errors.New("lfs: multiple locks found; ambiguous")
)

type LockCacher interface {
	Add(l Lock) error
	RemoveByPath(filePath string) error
	RemoveById(id string) error
	Locks() []Lock
	Clear()
	Save() error
}

// Client is the main interface object for the locking package
type Client struct {
	Remote    string
	RemoteRef *git.Ref
	client    *lockClient
	cache     LockCacher

	lockablePatterns []string
	lockableFilter   *filepathfilter.Filter
	lockableMutex    sync.Mutex

	LocalWorkingDir          string
	LocalGitDir              string
	SetLockableFilesReadOnly bool
}

// NewClient creates a new locking client with the given configuration
// You must call the returned object's `Close` method when you are finished with
// it
func NewClient(remote string, lfsClient *lfsapi.Client) (*Client, error) {
	return &Client{
		Remote: remote,
		client: &lockClient{Client: lfsClient},
		cache:  &nilLockCacher{},
	}, nil
}

func (c *Client) SetupFileCache(path string) error {
	stat, err := os.Stat(path)
	if err != nil {
		return errors.Wrap(err, "init lock cache")
	}

	lockFile := path
	if stat.IsDir() {
		lockFile = filepath.Join(path, "lockcache.db")
	}

	cache, err := NewLockCache(lockFile)
	if err != nil {
		return errors.Wrap(err, "init lock cache")
	}

	c.cache = cache
	return nil
}

// Close this client instance; must be called to dispose of resources
func (c *Client) Close() error {
	return c.cache.Save()
}

// LockFile attempts to lock a file on the current remote
// path must be relative to the root of the repository
// Returns the lock id if successful, or an error
func (c *Client) LockFile(path string) (Lock, error) {
	lockRes, _, err := c.client.Lock(c.Remote, &lockRequest{
		Path: path,
		Ref:  &lockRef{Name: c.RemoteRef.Refspec()},
	})
	if err != nil {
		return Lock{}, errors.Wrap(err, "api")
	}

	if len(lockRes.Message) > 0 {
		if len(lockRes.RequestID) > 0 {
			tracerx.Printf("Server Request ID: %s", lockRes.RequestID)
		}
		return Lock{}, fmt.Errorf("Server unable to create lock: %s", lockRes.Message)
	}

	lock := *lockRes.Lock
	if err := c.cache.Add(lock); err != nil {
		return Lock{}, errors.Wrap(err, "lock cache")
	}

	abs, err := getAbsolutePath(path)
	if err != nil {
		return Lock{}, errors.Wrap(err, "make lockpath absolute")
	}

	// Ensure writeable on return
	if err := tools.SetFileWriteFlag(abs, true); err != nil {
		return Lock{}, err
	}

	return lock, nil
}

// getAbsolutePath takes a repository-relative path and makes it absolute.
//
// For instance, given a repository in /usr/local/src/my-repo and a file called
// dir/foo/bar.txt, getAbsolutePath will return:
//
//   /usr/local/src/my-repo/dir/foo/bar.txt
func getAbsolutePath(p string) (string, error) {
	root, err := git.RootDir()
	if err != nil {
		return "", err
	}

	return filepath.Join(root, p), nil
}

// UnlockFile attempts to unlock a file on the current remote
// path must be relative to the root of the repository
// Force causes the file to be unlocked from other users as well
func (c *Client) UnlockFile(path string, force bool) error {
	id, err := c.lockIdFromPath(path)
	if err != nil {
		return fmt.Errorf("Unable to get lock id: %v", err)
	}

	return c.UnlockFileById(id, force)
}

// UnlockFileById attempts to unlock a lock with a given id on the current remote
// Force causes the file to be unlocked from other users as well
func (c *Client) UnlockFileById(id string, force bool) error {
	unlockRes, _, err := c.client.Unlock(c.RemoteRef, c.Remote, id, force)
	if err != nil {
		return errors.Wrap(err, "api")
	}

	if len(unlockRes.Message) > 0 {
		if len(unlockRes.RequestID) > 0 {
			tracerx.Printf("Server Request ID: %s", unlockRes.RequestID)
		}
		return fmt.Errorf("Server unable to unlock: %s", unlockRes.Message)
	}

	if err := c.cache.RemoveById(id); err != nil {
		return fmt.Errorf("Error caching unlock information: %v", err)
	}

	if unlockRes.Lock != nil {
		abs, err := getAbsolutePath(unlockRes.Lock.Path)
		if err != nil {
			return errors.Wrap(err, "make lockpath absolute")
		}

		// Make non-writeable if required
		if c.SetLockableFilesReadOnly && c.IsFileLockable(unlockRes.Lock.Path) {
			return tools.SetFileWriteFlag(abs, false)
		}
	}

	return nil
}

// Lock is a record of a locked file
type Lock struct {
	// Id is the unique identifier corresponding to this particular Lock. It
	// must be consistent with the local copy, and the server's copy.
	Id string `json:"id"`
	// Path is an absolute path to the file that is locked as a part of this
	// lock.
	Path string `json:"path"`
	// Owner is the identity of the user that created this lock.
	Owner *User `json:"owner,omitempty"`
	// LockedAt is the time at which this lock was acquired.
	LockedAt time.Time `json:"locked_at"`
}

// SearchLocks returns a channel of locks which match the given name/value filter
// If limit > 0 then search stops at that number of locks
// If localOnly = true, don't query the server & report only own local locks
func (c *Client) SearchLocks(filter map[string]string, limit int, localOnly bool) ([]Lock, error) {
	if localOnly {
		return c.searchLocalLocks(filter, limit)
	} else {
		return c.searchRemoteLocks(filter, limit)
	}
}

func (c *Client) VerifiableLocks(ref *git.Ref, limit int) (ourLocks, theirLocks []Lock, err error) {
	if ref == nil {
		ref = c.RemoteRef
	}

	ourLocks = make([]Lock, 0, limit)
	theirLocks = make([]Lock, 0, limit)
	body := &lockVerifiableRequest{
		Ref:   &lockRef{Name: ref.Refspec()},
		Limit: limit,
	}

	c.cache.Clear()

	for {
		list, res, err := c.client.SearchVerifiable(c.Remote, body)
		if res != nil {
			switch res.StatusCode {
			case http.StatusNotFound, http.StatusNotImplemented:
				return ourLocks, theirLocks, errors.NewNotImplementedError(err)
			case http.StatusForbidden:
				return ourLocks, theirLocks, errors.NewAuthError(err)
			}
		}

		if err != nil {
			return ourLocks, theirLocks, err
		}

		if list.Message != "" {
			if len(list.RequestID) > 0 {
				tracerx.Printf("Server Request ID: %s", list.RequestID)
			}
			return ourLocks, theirLocks, fmt.Errorf("Server error searching locks: %s", list.Message)
		}

		for _, l := range list.Ours {
			c.cache.Add(l)
			ourLocks = append(ourLocks, l)
			if limit > 0 && (len(ourLocks)+len(theirLocks)) >= limit {
				return ourLocks, theirLocks, nil
			}
		}

		for _, l := range list.Theirs {
			c.cache.Add(l)
			theirLocks = append(theirLocks, l)
			if limit > 0 && (len(ourLocks)+len(theirLocks)) >= limit {
				return ourLocks, theirLocks, nil
			}
		}

		if list.NextCursor != "" {
			body.Cursor = list.NextCursor
		} else {
			break
		}
	}

	return ourLocks, theirLocks, nil
}

func (c *Client) searchLocalLocks(filter map[string]string, limit int) ([]Lock, error) {
	cachedlocks := c.cache.Locks()
	path, filterByPath := filter["path"]
	id, filterById := filter["id"]
	lockCount := 0
	locks := make([]Lock, 0, len(cachedlocks))
	for _, l := range cachedlocks {
		// Manually filter by Path/Id
		if (filterByPath && path != l.Path) ||
			(filterById && id != l.Id) {
			continue
		}
		locks = append(locks, l)
		lockCount++
		if limit > 0 && lockCount >= limit {
			break
		}
	}
	return locks, nil
}

func (c *Client) searchRemoteLocks(filter map[string]string, limit int) ([]Lock, error) {
	locks := make([]Lock, 0, limit)

	apifilters := make([]lockFilter, 0, len(filter))
	for k, v := range filter {
		apifilters = append(apifilters, lockFilter{Property: k, Value: v})
	}

	query := &lockSearchRequest{
		Filters: apifilters,
		Limit:   limit,
		Refspec: c.RemoteRef.Refspec(),
	}

	for {
		list, _, err := c.client.Search(c.Remote, query)
		if err != nil {
			return locks, errors.Wrap(err, "locking")
		}

		if list.Message != "" {
			if len(list.RequestID) > 0 {
				tracerx.Printf("Server Request ID: %s", list.RequestID)
			}
			return locks, fmt.Errorf("Server error searching for locks: %s", list.Message)
		}

		for _, l := range list.Locks {
			locks = append(locks, l)
			if limit > 0 && len(locks) >= limit {
				// Exit outer loop too
				return locks, nil
			}
		}

		if list.NextCursor != "" {
			query.Cursor = list.NextCursor
		} else {
			break
		}
	}

	return locks, nil
}

// lockIdFromPath makes a call to the LFS API and resolves the ID for the locked
// locked at the given path.
//
// If the API call failed, an error will be returned. If multiple locks matched
// the given path (should not happen during real-world usage), an error will be
// returnd. If no locks matched the given path, an error will be returned.
//
// If the API call is successful, and only one lock matches the given filepath,
// then its ID will be returned, along with a value of "nil" for the error.
func (c *Client) lockIdFromPath(path string) (string, error) {
	list, _, err := c.client.Search(c.Remote, &lockSearchRequest{
		Filters: []lockFilter{
			{Property: "path", Value: path},
		},
	})

	if err != nil {
		return "", err
	}

	switch len(list.Locks) {
	case 0:
		return "", ErrNoMatchingLocks
	case 1:
		return list.Locks[0].Id, nil
	default:
		return "", ErrLockAmbiguous
	}
}

// IsFileLockedByCurrentCommitter returns whether a file is locked by the
// current user, as cached locally
func (c *Client) IsFileLockedByCurrentCommitter(path string) bool {
	filter := map[string]string{"path": path}
	locks, err := c.searchLocalLocks(filter, 1)
	if err != nil {
		tracerx.Printf("Error searching cached locks: %s\nForcing remote search", err)
		locks, _ = c.searchRemoteLocks(filter, 1)
	}
	return len(locks) > 0
}

func init() {
	kv.RegisterTypeForStorage(&Lock{})
}

type nilLockCacher struct{}

func (c *nilLockCacher) Add(l Lock) error {
	return nil
}
func (c *nilLockCacher) RemoveByPath(filePath string) error {
	return nil
}
func (c *nilLockCacher) RemoveById(id string) error {
	return nil
}
func (c *nilLockCacher) Locks() []Lock {
	return nil
}
func (c *nilLockCacher) Clear() {}
func (c *nilLockCacher) Save() error {
	return nil
}
<|endoftext|>


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

// Enhanced Logging
//
// This is inspired by the logging functionality in Java.  Essentially, you create a Logger
// object and create output filters for it.  You can send whatever you want to the Logger,
// and it will filter that based on your settings and send it to the outputs.  This way, you
// can put as much debug code in your program as you want, and when you're done you can filter
// out the mundane messages so only the import ones show up.
//
// Utility functions are provided to make life easier. Here is some example code to get started:
//
// log := elog.NewLogger()
// log.AddFilter("stdout", elog.DEBUG, new(elog.ConsoleLogWriter))
// log.AddFilter("log",    elog.FINE,  elog.NewFileLogWriter("example.log", true))
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
//
// The first two lines can be combined with the utility NewConsoleLogger:
//
// log := elog.NewConsoleLogger(elog.DEBUG)
// log.AddFilter("log",    elog.FINE,  elog.NewFileLogWriter("example.log", true))
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
//
// Usage notes:
// - The ConsoleLogWriter does not display the source to standard output, but the FileLogWriter does.
// - The utility functions (Info, Debug, Warn, etc) derive their source from the calling function
//
// Future work: (please let me know if you think I should work on any of these particularly)
// - Log file rotation
// - Logging configuration files ala log4j
// - Have the ability to remove filters?
// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows for another method of logging
// - Add an XML filter type
package log4go

import (
	"os"
	"fmt"
	"time"
	"runtime"
	"container/vector"
)

// Version information
const (
	L4G_VERSION = "log4go-v1.0.0"
	L4G_MAJOR   = 1
	L4G_MINOR   = 0
	L4G_BUILD   = 0
)

/****** Constants ******/

// These are the integer logging levels used by the logger
const (
	FINEST = iota
	FINE
	DEBUG
	TRACE
	INFO
	WARNING
	ERROR
	CRITICAL
)

// Logging level strings
var (
	levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
)

/****** LogRecord ******/

// This is the lifeblood of the package; it contains all of the pertinent information for each message
type LogRecord struct {
	Level   int        // The log level
	Created *time.Time // The time at which the log message was created
	Source  string     // The message source
	Message string     // The log message
}

func newLogRecord(lv int, src string, msg string) *LogRecord {
	lr := new(LogRecord)
	lr.Created = time.LocalTime()
	lr.Level = lv
	lr.Source = src
	lr.Message = msg
	return lr
}

/****** LogWriter ******/

// This is an interface for anything that should be able to write logs
type LogWriter interface {
	// This will be called to log a LogRecord message.
	// If necessary. this function should be *INTERNALLY* synchronzied,
	// and should spawn a separate goroutine if it could hang the program or take a long time.
	// TODO: This may be changed to have an Init() call that returns a
	// channel similar to <-chan *LogRecord for a more go-like internal setup
	LogWrite(rec *LogRecord) (n int, err os.Error)

	// This should return, at any given time, if the LogWriter is still in a good state.
	// A good state is defined as having the ability to dispatch a log message immediately.
	// if a LogWriter is not in a good state, the log message is simply not dispatched.
	Good() bool

	// This should clean up anything lingering about the LogWriter, as it is called before
	// the LogWriter is removed.  If possible, this should guarantee that all LogWrites
	// have been completed.
	Close()
}

/****** Logger ******/

// If LogRecord is the blood of the package, is the heart.
type Logger struct {
	// All filters have an entry in each of the following
	filterLevels     map[string]int
	filterLogWriters map[string]LogWriter
}

// Create a new logger
func NewLogger() *Logger {
	log := new(Logger)
	log.filterLevels = make(map[string]int)
	log.filterLogWriters = make(map[string]LogWriter)
	return log
}

// Closes all log writers in preparation for exiting the program.
// Calling this is not really imperative, unless you want to guarantee that all log messages are written.
func (log *Logger) Close() {
	// Close all open loggers
	for key := range log.filterLogWriters {
		log.filterLogWriters[key].Close()
		log.filterLogWriters[key] = nil, false
		log.filterLevels[key] = 0, false
	}
}

// Add the standard filter.
// This function is NOT INTERNALLY THREAD SAFE.  If you plan on
// calling this function from multiple goroutines, you will want
// to synchronize it yourself somehow.
func (log *Logger) AddFilter(name string, level int, writer LogWriter) {
	if writer == nil || !writer.Good() {
		return
	}
	log.filterLevels[name] = level
	log.filterLogWriters[name] = writer
}

// Create a new logger with the standard stdout
func NewConsoleLogger(level int) *Logger {
	log := NewLogger()
	log.AddFilter("stdout", level, new(ConsoleLogWriter))
	return log
}

/******* Logging *******/

// Send a log message manually
func (log *Logger) Log(level int, source, message string) {
	// Create a vector long enough to not require resizing
	var logto vector.StringVector
	logto.Resize(0, len(log.filterLevels))

	// Determine if any logging will be done
	for filt := range log.filterLevels {
		if level >= log.filterLevels[filt] {
			logto.Push(filt)
		}
	}

	// Only log if a filter requires it
	if len(logto) > 0 {
		// Make the log record
		rec := newLogRecord(level, source, message)

		// Dispatch the logs
		for _,filt := range logto {
			lw := log.filterLogWriters[filt]
			if lw.Good() {
				lw.LogWrite(rec)
			}
		}
	}
}

// Send a formatted log message easily
func (log *Logger) intLogf(level int, format string, args ...interface{}) {
	// Create a vector long enough to not require resizing
	var logto vector.StringVector
	logto.Resize(0, len(log.filterLevels))

	// Determine if any logging will be done
	for filt := range log.filterLevels {
		if level >= log.filterLevels[filt] {
			logto.Push(filt)
		}
	}

	// Only log if a filter requires it
	if len(logto) > 0 {
		// Determine caller func
		pc, _, lineno, ok := runtime.Caller(2)
		src := ""
		if ok {
			src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
		}

		// Make the log record
		rec := newLogRecord(level, src, fmt.Sprintf(format, args))

		// Dispatch the logs
		for _,filt := range logto {
			log.filterLogWriters[filt].LogWrite(rec)
		}
	}
}

// Send a formatted log message easily
func (log *Logger) Logf(level int, format string, args ...interface{}) {
	log.intLogf(level, format, args)
}

// Utility for finest log messages
func (log *Logger) Finest(format string, args ...interface{}) {
	log.intLogf(FINEST, format, args)
}

// Utility for fine log messages
func (log *Logger) Fine(format string, args ...interface{}) {
	log.intLogf(FINE, format, args)
}

// Utility for debug log messages
func (log *Logger) Debug(format string, args ...interface{}) {
	log.intLogf(DEBUG, format, args)
}

// Utility for trace log messages
func (log *Logger) Trace(format string, args ...interface{}) {
	log.intLogf(TRACE, format, args)
}

// Utility for info log messages
func (log *Logger) Info(format string, args ...interface{}) {
	log.intLogf(INFO, format, args)
}

// Utility for warn log messages (returns an os.Error for easy function returns)
func (log *Logger) Warn(format string, args ...interface{}) os.Error {
	log.intLogf(WARNING, format, args)
	return os.NewError(fmt.Sprintf(format, args))
}

// Utility for error log messages (returns an os.Error for easy function returns)
func (log *Logger) Error(format string, args ...interface{}) os.Error {
	log.intLogf(ERROR, format, args)
	return os.NewError(fmt.Sprintf(format, args))
}

// Utility for critical log messages (returns an os.Error for easy function returns)
func (log *Logger) Critical(format string, args ...interface{}) os.Error {
	log.intLogf(CRITICAL, format, args)
	return os.NewError(fmt.Sprintf(format, args))
}
<commit_msg>Version bump<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Enhanced Logging
//
// This is inspired by the logging functionality in Java.  Essentially, you create a Logger
// object and create output filters for it.  You can send whatever you want to the Logger,
// and it will filter that based on your settings and send it to the outputs.  This way, you
// can put as much debug code in your program as you want, and when you're done you can filter
// out the mundane messages so only the import ones show up.
//
// Utility functions are provided to make life easier. Here is some example code to get started:
//
// log := elog.NewLogger()
// log.AddFilter("stdout", elog.DEBUG, new(elog.ConsoleLogWriter))
// log.AddFilter("log",    elog.FINE,  elog.NewFileLogWriter("example.log", true))
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
//
// The first two lines can be combined with the utility NewConsoleLogger:
//
// log := elog.NewConsoleLogger(elog.DEBUG)
// log.AddFilter("log",    elog.FINE,  elog.NewFileLogWriter("example.log", true))
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
//
// Usage notes:
// - The ConsoleLogWriter does not display the source to standard output, but the FileLogWriter does.
// - The utility functions (Info, Debug, Warn, etc) derive their source from the calling function
//
// Future work: (please let me know if you think I should work on any of these particularly)
// - Log file rotation
// - Logging configuration files ala log4j
// - Have the ability to remove filters?
// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows for another method of logging
// - Add an XML filter type
package log4go

import (
	"os"
	"fmt"
	"time"
	"runtime"
	"container/vector"
)

// Version information
const (
	L4G_VERSION = "log4go-v1.0.1"
	L4G_MAJOR   = 1
	L4G_MINOR   = 0
	L4G_BUILD   = 1
)

/****** Constants ******/

// These are the integer logging levels used by the logger
const (
	FINEST = iota
	FINE
	DEBUG
	TRACE
	INFO
	WARNING
	ERROR
	CRITICAL
)

// Logging level strings
var (
	levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
)

/****** LogRecord ******/

// This is the lifeblood of the package; it contains all of the pertinent information for each message
type LogRecord struct {
	Level   int        // The log level
	Created *time.Time // The time at which the log message was created
	Source  string     // The message source
	Message string     // The log message
}

func newLogRecord(lv int, src string, msg string) *LogRecord {
	lr := new(LogRecord)
	lr.Created = time.LocalTime()
	lr.Level = lv
	lr.Source = src
	lr.Message = msg
	return lr
}

/****** LogWriter ******/

// This is an interface for anything that should be able to write logs
type LogWriter interface {
	// This will be called to log a LogRecord message.
	// If necessary. this function should be *INTERNALLY* synchronzied,
	// and should spawn a separate goroutine if it could hang the program or take a long time.
	// TODO: This may be changed to have an Init() call that returns a
	// channel similar to <-chan *LogRecord for a more go-like internal setup
	LogWrite(rec *LogRecord) (n int, err os.Error)

	// This should return, at any given time, if the LogWriter is still in a good state.
	// A good state is defined as having the ability to dispatch a log message immediately.
	// if a LogWriter is not in a good state, the log message is simply not dispatched.
	Good() bool

	// This should clean up anything lingering about the LogWriter, as it is called before
	// the LogWriter is removed.  If possible, this should guarantee that all LogWrites
	// have been completed.
	Close()
}

/****** Logger ******/

// If LogRecord is the blood of the package, is the heart.
type Logger struct {
	// All filters have an entry in each of the following
	filterLevels     map[string]int
	filterLogWriters map[string]LogWriter
}

// Create a new logger
func NewLogger() *Logger {
	log := new(Logger)
	log.filterLevels = make(map[string]int)
	log.filterLogWriters = make(map[string]LogWriter)
	return log
}

// Closes all log writers in preparation for exiting the program.
// Calling this is not really imperative, unless you want to guarantee that all log messages are written.
func (log *Logger) Close() {
	// Close all open loggers
	for key := range log.filterLogWriters {
		log.filterLogWriters[key].Close()
		log.filterLogWriters[key] = nil, false
		log.filterLevels[key] = 0, false
	}
}

// Add the standard filter.
// This function is NOT INTERNALLY THREAD SAFE.  If you plan on
// calling this function from multiple goroutines, you will want
// to synchronize it yourself somehow.
func (log *Logger) AddFilter(name string, level int, writer LogWriter) {
	if writer == nil || !writer.Good() {
		return
	}
	log.filterLevels[name] = level
	log.filterLogWriters[name] = writer
}

// Create a new logger with the standard stdout
func NewConsoleLogger(level int) *Logger {
	log := NewLogger()
	log.AddFilter("stdout", level, new(ConsoleLogWriter))
	return log
}

/******* Logging *******/

// Send a log message manually
func (log *Logger) Log(level int, source, message string) {
	// Create a vector long enough to not require resizing
	var logto vector.StringVector
	logto.Resize(0, len(log.filterLevels))

	// Determine if any logging will be done
	for filt := range log.filterLevels {
		if level >= log.filterLevels[filt] {
			logto.Push(filt)
		}
	}

	// Only log if a filter requires it
	if len(logto) > 0 {
		// Make the log record
		rec := newLogRecord(level, source, message)

		// Dispatch the logs
		for _,filt := range logto {
			lw := log.filterLogWriters[filt]
			if lw.Good() {
				lw.LogWrite(rec)
			}
		}
	}
}

// Send a formatted log message easily
func (log *Logger) intLogf(level int, format string, args ...interface{}) {
	// Create a vector long enough to not require resizing
	var logto vector.StringVector
	logto.Resize(0, len(log.filterLevels))

	// Determine if any logging will be done
	for filt := range log.filterLevels {
		if level >= log.filterLevels[filt] {
			logto.Push(filt)
		}
	}

	// Only log if a filter requires it
	if len(logto) > 0 {
		// Determine caller func
		pc, _, lineno, ok := runtime.Caller(2)
		src := ""
		if ok {
			src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
		}

		// Make the log record
		rec := newLogRecord(level, src, fmt.Sprintf(format, args))

		// Dispatch the logs
		for _,filt := range logto {
			log.filterLogWriters[filt].LogWrite(rec)
		}
	}
}

// Send a formatted log message easily
func (log *Logger) Logf(level int, format string, args ...interface{}) {
	log.intLogf(level, format, args)
}

// Utility for finest log messages
func (log *Logger) Finest(format string, args ...interface{}) {
	log.intLogf(FINEST, format, args)
}

// Utility for fine log messages
func (log *Logger) Fine(format string, args ...interface{}) {
	log.intLogf(FINE, format, args)
}

// Utility for debug log messages
func (log *Logger) Debug(format string, args ...interface{}) {
	log.intLogf(DEBUG, format, args)
}

// Utility for trace log messages
func (log *Logger) Trace(format string, args ...interface{}) {
	log.intLogf(TRACE, format, args)
}

// Utility for info log messages
func (log *Logger) Info(format string, args ...interface{}) {
	log.intLogf(INFO, format, args)
}

// Utility for warn log messages (returns an os.Error for easy function returns)
func (log *Logger) Warn(format string, args ...interface{}) os.Error {
	log.intLogf(WARNING, format, args)
	return os.NewError(fmt.Sprintf(format, args))
}

// Utility for error log messages (returns an os.Error for easy function returns)
func (log *Logger) Error(format string, args ...interface{}) os.Error {
	log.intLogf(ERROR, format, args)
	return os.NewError(fmt.Sprintf(format, args))
}

// Utility for critical log messages (returns an os.Error for easy function returns)
func (log *Logger) Critical(format string, args ...interface{}) os.Error {
	log.intLogf(CRITICAL, format, args)
	return os.NewError(fmt.Sprintf(format, args))
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"flag"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	"io/ioutil"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type logEntry struct {
	Node string
	Time int64
	Msg  string
}

var (
	tsFormat = "2006/01/02 15:04:05"
	addr     string
	dbFile   string
	logChan  chan *logEntry
)

var logSplit = regexp.MustCompile("^([\\w-:]+) (\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2}) (.+)$")
var responseCheck = regexp.MustCompile("response time: (\\d+)us$")

func init() {
	flLogBuffer := flag.Uint("b", 16, "log entries to buffer")
	flDbFile := flag.String("f", "logs.db", "database file")
	port := flag.Uint("p", 5988, "port to listen on")
	flag.Parse()

	addr = fmt.Sprintf(":%d", *port)
	dbFile = *flDbFile
	logChan = make(chan *logEntry, *flLogBuffer)
}

func main() {
	dbSetup()
	go log()
	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		fmt.Fprintln(os.Stderr, "[!] failed to resolve TCP address:", err.Error())
		os.Exit(1)
	}

	listener, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		fmt.Fprintln(os.Stderr, "[!] failed to set up TCP listener:", err.Error())
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("[!] TCP error:", err.Error())
			continue
		}
		go processMessage(conn)
	}
	os.Exit(1)
}

func processMessage(conn net.Conn) {
	defer conn.Close()
	bmsg, err := ioutil.ReadAll(conn)
	if err != nil {
		fmt.Println("[!] error reading message:", err.Error())
		return
	}
	msg := strings.Trim(string(bmsg), "\n \t")
	fmt.Println(string(msg))

	nodeID := logSplit.ReplaceAllString(msg, "$1")
	dateString := logSplit.ReplaceAllString(msg, "$2")
	logMsg := logSplit.ReplaceAllString(msg, "$3")
	tm, err := time.Parse(tsFormat, dateString)
	if err != nil {
		fmt.Fprintf(os.Stderr, "[!] error parsing time %s: %s\n",
			dateString, err.Error())
		return
	}
	le := &logEntry{nodeID, tm.UTC().Unix(), logMsg}
	logChan <- le
}

func log() {
	db, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		fmt.Println("[!] failed to open DB file:", err.Error())
		os.Exit(1)
	}
	defer db.Close()

	for {
		le, ok := <-logChan
		if !ok {
			return
		}
		_, err := db.Exec("insert into entries values (?, ?, ?)",
			le.Node, le.Time, le.Msg)
		if err != nil {
			fmt.Println("[!] database error:", err.Error())
			continue
		}

		if responseCheck.MatchString(le.Msg) {
			logResponseTime(db, le)
		}

	}
}

func logResponseTime(db *sql.DB, le *logEntry) {
	respString := responseCheck.ReplaceAllString(le.Msg, "$1")
	rTime, err := strconv.Atoi(respString)
	if err != nil {
		fmt.Println("[!] error reading response time:", err.Error())
		return
	}
	_, err = db.Exec("insert into response_times values (?, ?, ?)",
		le.Node, le.Time, rTime)
	if err != nil {
		fmt.Println("[!] error writing to database:", err.Error())
	}
}

func dbSetup() {
	entryTable()
	respTable()
}

func entryTable() {
	const createSql = `CREATE TABLE entries (node text, timestamp integer, message string)`
	db, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		fmt.Println("[!] failed to open DB file:", err.Error())
		os.Exit(1)
	}
	defer db.Close()

	rows, err := db.Query(`select sql from sqlite_master
                               where type='table' and name='entries'`)
	if err != nil {
		fmt.Println("[!] error reading database:", err.Error())
		os.Exit(1)
	}

	var tblSql string
	for rows.Next() {
		err = rows.Scan(&tblSql)
		break
	}

	if err != nil {
		fmt.Println("[!] error reading database:", err.Error())
		os.Exit(1)
	} else if tblSql == "" {
		fmt.Println("[+] creating table")
		_, err = db.Exec(createSql)
		if err != nil {
			fmt.Println("[!] error creating table:", err.Error())
			os.Exit(1)
		}
	} else if tblSql != createSql {
		fmt.Println("[+] schema out of sync")
		_, err = db.Exec(`drop table entries`)
		if err != nil {
			fmt.Println("[!] error dropping table:", err.Error())
			os.Exit(1)
		}
		_, err = db.Exec(createSql)
		if err != nil {
			fmt.Println("[!] error creating table:", err.Error())
			os.Exit(1)
		}
	}
}

func respTable() {
	const createSql = `CREATE TABLE response_times (node text, timestamp integer, microsec integer)`
	db, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		fmt.Println("[!] failed to open DB file:", err.Error())
		os.Exit(1)
	}
	defer db.Close()

	rows, err := db.Query(`select sql from sqlite_master
                               where type='table' and name='response_times'`)
	if err != nil {
		fmt.Println("[!] error reading database:", err.Error())
		os.Exit(1)
	}

	var tblSql string
	for rows.Next() {
		err = rows.Scan(&tblSql)
		break
	}

	if err != nil {
		fmt.Println("[!] error reading database:", err.Error())
		os.Exit(1)
	} else if tblSql == "" {
		fmt.Println("[+] creating table")
		_, err = db.Exec(createSql)
		if err != nil {
			fmt.Println("[!] error creating table:", err.Error())
			os.Exit(1)
		}
	} else if tblSql != createSql {
		fmt.Println("[+] schema out of sync")
		_, err = db.Exec(`drop table response_times`)
		if err != nil {
			fmt.Println("[!] error dropping table:", err.Error())
			os.Exit(1)
		}
		_, err = db.Exec(createSql)
		if err != nil {
			fmt.Println("[!] error creating table:", err.Error())
			os.Exit(1)
		}
	}
}
<commit_msg>improve client handling<commit_after>package main

import (
	"bufio"
	"database/sql"
	"flag"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
        "io"
	"net"
	"os"
	"os/signal"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type logEntry struct {
	Node string
	Time int64
	Msg  string
}

var (
	tsFormat = "2006/01/02 15:04:05"
	addr     string
	dbFile   string
	logChan  chan *logEntry
)

var logSplit = regexp.MustCompile("^([\\w-:]+) (\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2}) (.+)$")
var responseCheck = regexp.MustCompile("response time: (\\d+)us$")

func init() {
	flLogBuffer := flag.Uint("b", 16, "log entries to buffer")
	flDbFile := flag.String("f", "logs.db", "database file")
	port := flag.Uint("p", 5988, "port to listen on")
	flag.Parse()

	addr = fmt.Sprintf(":%d", *port)
	dbFile = *flDbFile
	logChan = make(chan *logEntry, *flLogBuffer)
}

func main() {
	dbSetup()
	go log()
	go listen()

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, os.Kill, os.Interrupt, syscall.SIGTERM)
	<-sigc
	close(logChan)
	<-time.After(100 * time.Millisecond)

	os.Exit(1)
}

func listen() {
	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		fmt.Fprintln(os.Stderr, "[!] failed to resolve TCP address:", err.Error())
		os.Exit(1)
	}

	listener, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		fmt.Fprintln(os.Stderr, "[!] failed to set up TCP listener:", err.Error())
	}
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("[!] TCP error:", err.Error())
			continue
		}
		go processMessage(conn)
	}

}

func processMessage(conn net.Conn) {
	defer conn.Close()
	r := bufio.NewReader(conn)

	for {
		msg, err := r.ReadString(0x0a)
		if err != nil {
                        if err != io.EOF {
                                fmt.Println("[!] error reading from client:",
                                        err.Error())
                        }
			return
		}
		msg = strings.Trim(string(msg), "\n \t")
		fmt.Println("-- ", msg)

		nodeID := logSplit.ReplaceAllString(msg, "$1")
		dateString := logSplit.ReplaceAllString(msg, "$2")
		logMsg := logSplit.ReplaceAllString(msg, "$3")
		tm, err := time.Parse(tsFormat, dateString)
		if err != nil {
			fmt.Fprintf(os.Stderr, "[!] error parsing time %s: %s\n",
				dateString, err.Error())
			return
		}
		le := &logEntry{nodeID, tm.UTC().Unix(), logMsg}
		logChan <- le
	}
}

func log() {
	db, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		fmt.Println("[!] failed to open DB file:", err.Error())
		os.Exit(1)
	}
	defer db.Close()

	for {
		le, ok := <-logChan
		if !ok {
                        fmt.Println("[+] shutting down database listener")
			return
		}
		_, err := db.Exec("insert into entries values (?, ?, ?)",
			le.Node, le.Time, le.Msg)
		if err != nil {
			fmt.Println("[!] database error:", err.Error())
			continue
		}

		if responseCheck.MatchString(le.Msg) {
			logResponseTime(db, le)
		}

	}
}

func logResponseTime(db *sql.DB, le *logEntry) {
	respString := responseCheck.ReplaceAllString(le.Msg, "$1")
	rTime, err := strconv.Atoi(respString)
	if err != nil {
		fmt.Println("[!] error reading response time:", err.Error())
		return
	}
	_, err = db.Exec("insert into response_times values (?, ?, ?)",
		le.Node, le.Time, rTime)
	if err != nil {
		fmt.Println("[!] error writing to database:", err.Error())
	}
}

func dbSetup() {
	entryTable()
	respTable()
}

func entryTable() {
	const createSql = `CREATE TABLE entries (node text, timestamp integer, message string)`
	db, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		fmt.Println("[!] failed to open DB file:", err.Error())
		os.Exit(1)
	}
	defer db.Close()

	rows, err := db.Query(`select sql from sqlite_master
                               where type='table' and name='entries'`)
	if err != nil {
		fmt.Println("[!] error reading database:", err.Error())
		os.Exit(1)
	}

	var tblSql string
	for rows.Next() {
		err = rows.Scan(&tblSql)
		break
	}

	if err != nil {
		fmt.Println("[!] error reading database:", err.Error())
		os.Exit(1)
	} else if tblSql == "" {
		fmt.Println("[+] creating table")
		_, err = db.Exec(createSql)
		if err != nil {
			fmt.Println("[!] error creating table:", err.Error())
			os.Exit(1)
		}
	} else if tblSql != createSql {
		fmt.Println("[+] schema out of sync")
		_, err = db.Exec(`drop table entries`)
		if err != nil {
			fmt.Println("[!] error dropping table:", err.Error())
			os.Exit(1)
		}
		_, err = db.Exec(createSql)
		if err != nil {
			fmt.Println("[!] error creating table:", err.Error())
			os.Exit(1)
		}
	}
}

func respTable() {
	const createSql = `CREATE TABLE response_times (node text, timestamp integer, microsec integer)`
	db, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		fmt.Println("[!] failed to open DB file:", err.Error())
		os.Exit(1)
	}
	defer db.Close()

	rows, err := db.Query(`select sql from sqlite_master
                               where type='table' and name='response_times'`)
	if err != nil {
		fmt.Println("[!] error reading database:", err.Error())
		os.Exit(1)
	}

	var tblSql string
	for rows.Next() {
		err = rows.Scan(&tblSql)
		break
	}

	if err != nil {
		fmt.Println("[!] error reading database:", err.Error())
		os.Exit(1)
	} else if tblSql == "" {
		fmt.Println("[+] creating table")
		_, err = db.Exec(createSql)
		if err != nil {
			fmt.Println("[!] error creating table:", err.Error())
			os.Exit(1)
		}
	} else if tblSql != createSql {
		fmt.Println("[+] schema out of sync")
		_, err = db.Exec(`drop table response_times`)
		if err != nil {
			fmt.Println("[!] error dropping table:", err.Error())
			os.Exit(1)
		}
		_, err = db.Exec(createSql)
		if err != nil {
			fmt.Println("[!] error creating table:", err.Error())
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"math/rand"
	"strconv"
	"strings"
	"testing"

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

func TestDotPrefixMatch(t *testing.T) {
	list := cmdList{}

	pass := true
	pass = pass && list.dotPrefixMatch("s.privileged", "security.privileged")
	pass = pass && list.dotPrefixMatch("u.blah", "user.blah")

	if !pass {
		t.Error("failed prefix matching")
	}
}

func TestShouldShow(t *testing.T) {
	list := cmdList{}
	state := &api.Instance{
		Name: "foo",
		ExpandedConfig: map[string]string{
			"security.privileged": "1",
			"user.blah":           "abc",
			"image.os":            "Debian",
			"image.description":   "Debian buster amd64 (20200429_05:24)",
		},
		Status:   "Running",
		Location: "mem-brain",
		Type:     "Container",
		ExpandedDevices: map[string]map[string]string{
			"eth0": {
				"name":         "eth0",
				"ipv4.address": "10.29.85.156",
				"ipv6.address": "fd42:72a:89ac:e457:216:3eff:fe83:8301",
				"type":         "nic",
				"parent":       "lxdbr0",
				"nictype":      "bridged",
			},
		},
		InstancePut: api.InstancePut{
			Architecture: "potato",
			Description:  "Something which does something",
		},
	}

	if !list.shouldShow([]string{"u.blah=abc"}, state) {
		t.Error("u.blah=abc didn't match")
	}

	if !list.shouldShow([]string{"user.blah=abc"}, state) {
		t.Error("user.blah=abc didn't match")
	}

	if !list.shouldShow([]string{"status=RUNNING", "user.blah=abc"}, state) {
		t.Error("user.blah=abc status=RUNNING didn't match")
	}

	if !list.shouldShow([]string{"image.os=Debian", "user.blah=abc"}, state) {
		t.Error("user.blah=abc os=debian didn't match")
	}

	if !list.shouldShow([]string{"location=mem-brain", "user.blah=abc"}, state) {
		t.Error("user.blah=abc location=mem-brain didn't match")
	}

	if !list.shouldShow([]string{"architecture=potato", "user.blah=abc"}, state) {
		t.Error("user.blah=abc architecture=potato didn't match")
	}

	if !list.shouldShow([]string{"name=foo", "user.blah=abc"}, state) {
		t.Error("user.blah=abc name=foo didn't match")
	}

	if list.shouldShow([]string{"image.os=temple-os", "user.blah=abc"}, state) {
		t.Error("user.blah=abc image.os=temple-os did match")
	}

	if list.shouldShow([]string{"status=RUNNING", "type=virtual-machine", "user.blah=abc"}, state) {
		t.Error("user.blah=abc status=RUNNING, type=virtual-machine did match ")
	}

	if list.shouldShow([]string{"status=FROZEN,STOPPED"}, state) {
		t.Error("status=FROZEN,STOPPED did not match ")
	}

	if !list.shouldShow([]string{"status=RUNNING,STOPPED"}, state) {
		t.Error("status=RUNNING,STOPPED  did not match ")
	}

	if !list.shouldShow([]string{"type=container", "user.blah=abc"}, state) {
		t.Error("user.blah=abc type=container didn't match")
	}

	if list.shouldShow([]string{"bar", "u.blah=abc"}, state) {
		t.Errorf("name filter didn't work")
	}

	if list.shouldShow([]string{"bar", "u.blah=other"}, state) {
		t.Errorf("value filter didn't work")
	}

	if !list.shouldShow([]string{"ipv4=10.29.85.0/24"}, state) {
		t.Errorf("net=10.29.85.0/24 filter didn't work")
	}

	if list.shouldShow([]string{"ipv4=10.29.85.0/32"}, state) {
		t.Errorf("net=10.29.85.0/32 filter did work but should not")
	}

	if !list.shouldShow([]string{"ipv4=10.29.85.156"}, state) {
		t.Errorf("net=10.29.85.156 filter did not work")
	}

	if !list.shouldShow([]string{"ipv6=fd42:72a:89ac:e457:216:3eff:fe83:8301"}, state) {
		t.Errorf("net=fd42:72a:89ac:e457:216:3eff:fe83:8301 filter didn't work")
	}

	if list.shouldShow([]string{"ipv6=fd42:072a:89ac:e457:0216:3eff:fe83:ffff/128"}, state) {
		t.Errorf("net=1net=fd42:072a:89ac:e457:0216:3eff:fe83:ffff/128 filter did work but should not")
	}

	if !list.shouldShow([]string{"ipv6=fd42:72a:89ac:e457:216:3eff:fe83:ffff/1"}, state) {
		t.Errorf("net=fd42:72a:89ac:e457:216:3eff:fe83:ffff/1 filter filter didn't work")
	}

	if list.shouldShow([]string{"user.blah=abc", "status=stopped"}, state) {
		t.Error("user.blah=abc status=stopped did match even though container status is 'running'")
	}

}

// Used by TestColumns and TestInvalidColumns
const shorthand = "46abcdDfFlmMnNpPsStuL"
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

func TestColumns(t *testing.T) {
	keys := make([]string, 0, len(shared.KnownInstanceConfigKeys))
	for k := range shared.KnownInstanceConfigKeys {
		keys = append(keys, k)
		//Test compatibility with 'config:' prefix
		keys = append(keys, "config:"+k)
	}
	//Test with 'devices:'
	keys = append(keys, "devices:eth0.parent.rand")
	keys = append(keys, "devices:root.path")

	randShorthand := func(buffer *bytes.Buffer) {
		buffer.WriteByte(shorthand[rand.Intn(len(shorthand))])
	}

	randString := func(buffer *bytes.Buffer) {
		l := rand.Intn(20)
		if l == 0 {
			l = rand.Intn(20) + 20
		}
		for i := 0; i < l; i++ {
			buffer.WriteByte(alphanum[rand.Intn(len(alphanum))])
		}
	}

	randConfigKey := func(buffer *bytes.Buffer) {
		// Unconditionally prepend a comma so that we don't create an invalid
		// column string, redundant commas will be handled immediately prior
		// to parsing the string.
		buffer.WriteRune(',')

		switch rand.Intn(4) {
		case 0:
			buffer.WriteString(keys[rand.Intn(len(keys))])
		case 1:
			buffer.WriteString("user.")
			randString(buffer)
		case 2:
			buffer.WriteString("environment.")
			randString(buffer)
		case 3:
			if rand.Intn(2) == 0 {
				buffer.WriteString(shared.ConfigVolatilePrefix)
				randString(buffer)
				buffer.WriteString(".hwaddr")
			} else {
				buffer.WriteString(shared.ConfigVolatilePrefix)
				randString(buffer)
				buffer.WriteString(".name")
			}
		}

		// Randomize the optional fields in a single shot.  Empty names are legal
		// when specifying the max width, append an extra colon in this case.
		opt := rand.Intn(8)
		if opt&1 != 0 {
			buffer.WriteString(":")
			randString(buffer)
		} else if opt != 0 {
			buffer.WriteString(":")
		}

		switch opt {
		case 2, 3:
			buffer.WriteString(":-1")
		case 4, 5:
			buffer.WriteString(":0")
		case 6, 7:
			buffer.WriteRune(':')
			buffer.WriteString(strconv.FormatUint(uint64(rand.Uint32()), 10))
		}

		// Unconditionally append a comma so that we don't create an invalid
		// column string, redundant commas will be handled immediately prior
		// to parsing the string.
		buffer.WriteRune(',')
	}

	for i := 0; i < 1000; i++ {
		go func() {
			var buffer bytes.Buffer

			l := rand.Intn(10)
			if l == 0 {
				l = rand.Intn(10) + 10
			}

			num := l
			for j := 0; j < l; j++ {
				switch rand.Intn(5) {
				case 0:
					if buffer.Len() > 0 {
						buffer.WriteRune(',')
						num--
					} else {
						randShorthand(&buffer)
					}

				case 1, 2:
					randShorthand(&buffer)
				case 3, 4:
					randConfigKey(&buffer)
				}
			}

			// Generate the column string, removing any leading, trailing or duplicate commas.
			raw := shared.RemoveDuplicatesFromString(strings.Trim(buffer.String(), ","), ",")

			list := cmdList{flagColumns: raw}

			clustered := strings.Contains(raw, "L")
			columns, _, err := list.parseColumns(clustered)
			if err != nil {
				t.Errorf("Failed to parse columns string.  Input: %s, Error: %s", raw, err)
			}
			if len(columns) != num {
				t.Errorf("Did not generate correct number of columns.  Expected: %d, Actual: %d, Input: %s", num, len(columns), raw)
			}
		}()
	}
}

func TestInvalidColumns(t *testing.T) {
	run := func(raw string) {
		list := cmdList{flagColumns: raw}
		_, _, err := list.parseColumns(true)
		if err == nil {
			t.Errorf("Expected error from parseColumns, received nil.  Input: %s", raw)
		}
	}

	for _, v := range alphanum {
		if !strings.ContainsRune(shorthand, v) {
			run(string(v))
		}
	}

	run(",")
	run(",a")
	run("a,")
	run("4,,6")
	run(".")
	run(":")
	run("::")
	run(".key:")
	run("user.key:")
	run("user.key::")
	run(":user.key")
	run(":user.key:0")
	run("user.key::-2")
	run("user.key:name:-2")
	run("volatile")
	run("base_image")
	run("volatile.image")
	run("config:")
	run("config:image")
	run("devices:eth0")
}
<commit_msg>lxc/list_test: Rename state to inst<commit_after>package main

import (
	"bytes"
	"math/rand"
	"strconv"
	"strings"
	"testing"

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

func TestDotPrefixMatch(t *testing.T) {
	list := cmdList{}

	pass := true
	pass = pass && list.dotPrefixMatch("s.privileged", "security.privileged")
	pass = pass && list.dotPrefixMatch("u.blah", "user.blah")

	if !pass {
		t.Error("failed prefix matching")
	}
}

func TestShouldShow(t *testing.T) {
	list := cmdList{}
	inst := &api.Instance{
		Name: "foo",
		ExpandedConfig: map[string]string{
			"security.privileged": "1",
			"user.blah":           "abc",
			"image.os":            "Debian",
			"image.description":   "Debian buster amd64 (20200429_05:24)",
		},
		Status:   "Running",
		Location: "mem-brain",
		Type:     "Container",
		ExpandedDevices: map[string]map[string]string{
			"eth0": {
				"name":         "eth0",
				"ipv4.address": "10.29.85.156",
				"ipv6.address": "fd42:72a:89ac:e457:216:3eff:fe83:8301",
				"type":         "nic",
				"parent":       "lxdbr0",
				"nictype":      "bridged",
			},
		},
		InstancePut: api.InstancePut{
			Architecture: "potato",
			Description:  "Something which does something",
		},
	}

	if !list.shouldShow([]string{"u.blah=abc"}, inst) {
		t.Error("u.blah=abc didn't match")
	}

	if !list.shouldShow([]string{"user.blah=abc"}, inst) {
		t.Error("user.blah=abc didn't match")
	}

	if !list.shouldShow([]string{"status=RUNNING", "user.blah=abc"}, inst) {
		t.Error("user.blah=abc status=RUNNING didn't match")
	}

	if !list.shouldShow([]string{"image.os=Debian", "user.blah=abc"}, inst) {
		t.Error("user.blah=abc os=debian didn't match")
	}

	if !list.shouldShow([]string{"location=mem-brain", "user.blah=abc"}, inst) {
		t.Error("user.blah=abc location=mem-brain didn't match")
	}

	if !list.shouldShow([]string{"architecture=potato", "user.blah=abc"}, inst) {
		t.Error("user.blah=abc architecture=potato didn't match")
	}

	if !list.shouldShow([]string{"name=foo", "user.blah=abc"}, inst) {
		t.Error("user.blah=abc name=foo didn't match")
	}

	if list.shouldShow([]string{"image.os=temple-os", "user.blah=abc"}, inst) {
		t.Error("user.blah=abc image.os=temple-os did match")
	}

	if list.shouldShow([]string{"status=RUNNING", "type=virtual-machine", "user.blah=abc"}, inst) {
		t.Error("user.blah=abc status=RUNNING, type=virtual-machine did match ")
	}

	if list.shouldShow([]string{"status=FROZEN,STOPPED"}, inst) {
		t.Error("status=FROZEN,STOPPED did not match ")
	}

	if !list.shouldShow([]string{"status=RUNNING,STOPPED"}, inst) {
		t.Error("status=RUNNING,STOPPED  did not match ")
	}

	if !list.shouldShow([]string{"type=container", "user.blah=abc"}, inst) {
		t.Error("user.blah=abc type=container didn't match")
	}

	if list.shouldShow([]string{"bar", "u.blah=abc"}, inst) {
		t.Errorf("name filter didn't work")
	}

	if list.shouldShow([]string{"bar", "u.blah=other"}, inst) {
		t.Errorf("value filter didn't work")
	}

	if !list.shouldShow([]string{"ipv4=10.29.85.0/24"}, inst) {
		t.Errorf("net=10.29.85.0/24 filter didn't work")
	}

	if list.shouldShow([]string{"ipv4=10.29.85.0/32"}, inst) {
		t.Errorf("net=10.29.85.0/32 filter did work but should not")
	}

	if !list.shouldShow([]string{"ipv4=10.29.85.156"}, inst) {
		t.Errorf("net=10.29.85.156 filter did not work")
	}

	if !list.shouldShow([]string{"ipv6=fd42:72a:89ac:e457:216:3eff:fe83:8301"}, inst) {
		t.Errorf("net=fd42:72a:89ac:e457:216:3eff:fe83:8301 filter didn't work")
	}

	if list.shouldShow([]string{"ipv6=fd42:072a:89ac:e457:0216:3eff:fe83:ffff/128"}, inst) {
		t.Errorf("net=1net=fd42:072a:89ac:e457:0216:3eff:fe83:ffff/128 filter did work but should not")
	}

	if !list.shouldShow([]string{"ipv6=fd42:72a:89ac:e457:216:3eff:fe83:ffff/1"}, inst) {
		t.Errorf("net=fd42:72a:89ac:e457:216:3eff:fe83:ffff/1 filter filter didn't work")
	}

	if list.shouldShow([]string{"user.blah=abc", "status=stopped"}, inst) {
		t.Error("user.blah=abc status=stopped did match even though container status is 'running'")
	}
}

// Used by TestColumns and TestInvalidColumns
const shorthand = "46abcdDfFlmMnNpPsStuL"
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

func TestColumns(t *testing.T) {
	keys := make([]string, 0, len(shared.KnownInstanceConfigKeys))
	for k := range shared.KnownInstanceConfigKeys {
		keys = append(keys, k)
		//Test compatibility with 'config:' prefix
		keys = append(keys, "config:"+k)
	}
	//Test with 'devices:'
	keys = append(keys, "devices:eth0.parent.rand")
	keys = append(keys, "devices:root.path")

	randShorthand := func(buffer *bytes.Buffer) {
		buffer.WriteByte(shorthand[rand.Intn(len(shorthand))])
	}

	randString := func(buffer *bytes.Buffer) {
		l := rand.Intn(20)
		if l == 0 {
			l = rand.Intn(20) + 20
		}
		for i := 0; i < l; i++ {
			buffer.WriteByte(alphanum[rand.Intn(len(alphanum))])
		}
	}

	randConfigKey := func(buffer *bytes.Buffer) {
		// Unconditionally prepend a comma so that we don't create an invalid
		// column string, redundant commas will be handled immediately prior
		// to parsing the string.
		buffer.WriteRune(',')

		switch rand.Intn(4) {
		case 0:
			buffer.WriteString(keys[rand.Intn(len(keys))])
		case 1:
			buffer.WriteString("user.")
			randString(buffer)
		case 2:
			buffer.WriteString("environment.")
			randString(buffer)
		case 3:
			if rand.Intn(2) == 0 {
				buffer.WriteString(shared.ConfigVolatilePrefix)
				randString(buffer)
				buffer.WriteString(".hwaddr")
			} else {
				buffer.WriteString(shared.ConfigVolatilePrefix)
				randString(buffer)
				buffer.WriteString(".name")
			}
		}

		// Randomize the optional fields in a single shot.  Empty names are legal
		// when specifying the max width, append an extra colon in this case.
		opt := rand.Intn(8)
		if opt&1 != 0 {
			buffer.WriteString(":")
			randString(buffer)
		} else if opt != 0 {
			buffer.WriteString(":")
		}

		switch opt {
		case 2, 3:
			buffer.WriteString(":-1")
		case 4, 5:
			buffer.WriteString(":0")
		case 6, 7:
			buffer.WriteRune(':')
			buffer.WriteString(strconv.FormatUint(uint64(rand.Uint32()), 10))
		}

		// Unconditionally append a comma so that we don't create an invalid
		// column string, redundant commas will be handled immediately prior
		// to parsing the string.
		buffer.WriteRune(',')
	}

	for i := 0; i < 1000; i++ {
		go func() {
			var buffer bytes.Buffer

			l := rand.Intn(10)
			if l == 0 {
				l = rand.Intn(10) + 10
			}

			num := l
			for j := 0; j < l; j++ {
				switch rand.Intn(5) {
				case 0:
					if buffer.Len() > 0 {
						buffer.WriteRune(',')
						num--
					} else {
						randShorthand(&buffer)
					}

				case 1, 2:
					randShorthand(&buffer)
				case 3, 4:
					randConfigKey(&buffer)
				}
			}

			// Generate the column string, removing any leading, trailing or duplicate commas.
			raw := shared.RemoveDuplicatesFromString(strings.Trim(buffer.String(), ","), ",")

			list := cmdList{flagColumns: raw}

			clustered := strings.Contains(raw, "L")
			columns, _, err := list.parseColumns(clustered)
			if err != nil {
				t.Errorf("Failed to parse columns string.  Input: %s, Error: %s", raw, err)
			}
			if len(columns) != num {
				t.Errorf("Did not generate correct number of columns.  Expected: %d, Actual: %d, Input: %s", num, len(columns), raw)
			}
		}()
	}
}

func TestInvalidColumns(t *testing.T) {
	run := func(raw string) {
		list := cmdList{flagColumns: raw}
		_, _, err := list.parseColumns(true)
		if err == nil {
			t.Errorf("Expected error from parseColumns, received nil.  Input: %s", raw)
		}
	}

	for _, v := range alphanum {
		if !strings.ContainsRune(shorthand, v) {
			run(string(v))
		}
	}

	run(",")
	run(",a")
	run("a,")
	run("4,,6")
	run(".")
	run(":")
	run("::")
	run(".key:")
	run("user.key:")
	run("user.key::")
	run(":user.key")
	run(":user.key:0")
	run("user.key::-2")
	run("user.key:name:-2")
	run("volatile")
	run("base_image")
	run("volatile.image")
	run("config:")
	run("config:image")
	run("devices:eth0")
}
<|endoftext|>


<commit_before>// Copyright 2014 The jenkins-client-launcher Authors. All rights reserved.
// Use of this source code is governed by a MIT license that can be found in the LICENSE file.

package environment

import (
	"github.com/jkellerer/jenkins-client-launcher/launcher/modes"
	"github.com/jkellerer/jenkins-client-launcher/launcher/util"
	"io"
	"fmt"
	"os"
	"path/filepath"
)

const (
	ClientJarName         = "slave.jar"
	ClientJarDownloadName = "~slave.jar.download"
	ClientJarURL          = "jnlpJars/slave.jar"
)

// Implements a downloader that ensures that the latest Jenkins client (slave.jar) is
// downloaded before the client mode starts.
type JenkinsClientDownloader struct {
	util.AnyConfigAcceptor
}

func (self *JenkinsClientDownloader) Name() string {
	return "Jenkins Client Downloader"
}

func (self *JenkinsClientDownloader) Prepare(config *util.Config) {
	util.ClientJar, _ = filepath.Abs(ClientJarName)

	modes.RegisterModeListener(func(mode modes.ExecutableMode, nextStatus int32, config *util.Config) {
		if mode.Name() == "client" && nextStatus == modes.ModeStarting && config.HasCIConnection() {
			if err := self.downloadJar(config); err != nil {
				jar, e := os.Open(ClientJarName); defer jar.Close()
				if os.IsNotExist(e) {
					panic(fmt.Sprintf("No jenkins client: %s", err))
				} else {
					util.GOut("DOWNLOAD", "%s", err)
				}
			}
		}
	})
}

func (self *JenkinsClientDownloader) downloadJar(config *util.Config) error {
	util.GOut("DOWNLOAD", "Getting latest Jenkins client %v", (config.CIHostURI+"/"+ClientJarURL))

	// TODO: Implement If-Modified-Since

	var source io.ReadCloser
	if response, err := config.CIGet(ClientJarURL); err == nil && response.StatusCode == 200 {
		source = response.Body
		defer source.Close()
	} else {
		if err == nil {
			return fmt.Errorf("Failed downloading jenkins client. Cause: HTTP-%v %v", response.StatusCode, response.Status)
		} else {
			return fmt.Errorf("Failed downloading jenkins client. Connect failed. Cause: %v", err)
		}
	}

	target, err := os.Create(ClientJarDownloadName); defer target.Close()

	if err != nil {
		return fmt.Errorf("Failed downloading jenkins client. Cannot create local file. Cause: %v", err)
	}

	if _, err = io.Copy(target, source); err == nil {
		target.Close()
		if err = os.Remove(ClientJarName); err == nil || os.IsNotExist(err) {
			err = os.Rename(ClientJarDownloadName, ClientJarName)
		}
		return err
	} else {
		return fmt.Errorf("Failed downloading jenkins client. Transfer failed. Cause: %v", err)
	}
}

// Registering the downloader.
var _ = RegisterPreparer(new(JenkinsClientDownloader))
<commit_msg>ClientDownloader: Added support for Last-Modified handling and If-Modified-Since requests to download the client only when newer on Jenkins (or not existing on the local node).<commit_after>// Copyright 2014 The jenkins-client-launcher Authors. All rights reserved.
// Use of this source code is governed by a MIT license that can be found in the LICENSE file.

package environment

import (
	"github.com/jkellerer/jenkins-client-launcher/launcher/modes"
	"github.com/jkellerer/jenkins-client-launcher/launcher/util"
	"io"
	"fmt"
	"os"
	"path/filepath"
	"net/http"
	"time"
)

const (
	ClientJarName         = "slave.jar"
	ClientJarDownloadName = "~slave.jar.download"
	ClientJarURL          = "jnlpJars/slave.jar"
)

// Implements a downloader that ensures that the latest Jenkins client (slave.jar) is
// downloaded before the client mode starts.
type JenkinsClientDownloader struct {
	util.AnyConfigAcceptor
}

func (self *JenkinsClientDownloader) Name() string {
	return "Jenkins Client Downloader"
}

func (self *JenkinsClientDownloader) Prepare(config *util.Config) {
	util.ClientJar, _ = filepath.Abs(ClientJarName)

	modes.RegisterModeListener(func(mode modes.ExecutableMode, nextStatus int32, config *util.Config) {
		if mode.Name() == "client" && nextStatus == modes.ModeStarting && config.HasCIConnection() {
			if err := self.downloadJar(config); err != nil {
				jar, e := os.Open(ClientJarName); defer jar.Close()
				if os.IsNotExist(e) {
					panic(fmt.Sprintf("No jenkins client: %s", err))
				} else {
					util.GOut("DOWNLOAD", "%s", err)
				}
			}
		}
	})
}

func (self *JenkinsClientDownloader) downloadJar(config *util.Config) error {
	util.GOut("DOWNLOAD", "Getting latest Jenkins client %v", (config.CIHostURI+"/"+ClientJarURL))

	// Create the HTTP request.
	request, err := config.CIRequest("GET", ClientJarURL, nil)
	if err != nil {
		return err
	}

	if file, err := os.Open(ClientJarName); err == nil {
		if fi, err := file.Stat(); err == nil {
			request.Header.Add("If-Modified-Since", fi.ModTime().Format(http.TimeFormat))
		}

		file.Close()
	}

	// Perform the HTTP request.
	var source io.ReadCloser
	sourceTime := time.Now()
	if response, err := config.CIClient().Do(request); err == nil {
		defer response.Body.Close()

		source = response.Body

		if response.StatusCode == 304 {
			util.GOut("DOWNLOAD", "Jenkins client is up-to-date, no need to download.")
			return nil
		} else if response.StatusCode != 200 {
			return fmt.Errorf("Failed downloading jenkins client. Cause: HTTP-%v %v", response.StatusCode, response.Status)
		}

		if value := response.Header.Get("Last-Modified"); value != "" {
			if time, err := http.ParseTime(value); err == nil {
				sourceTime = time
			}
		}
	} else {
		return fmt.Errorf("Failed downloading jenkins client. Connect failed. Cause: %v", err)
	}

	target, err := os.Create(ClientJarDownloadName); defer target.Close()

	if err != nil {
		return fmt.Errorf("Failed downloading jenkins client. Cannot create local file. Cause: %v", err)
	}

	if _, err = io.Copy(target, source); err == nil {
		target.Close()
		if err = os.Remove(ClientJarName); err == nil || os.IsNotExist(err) {
			if err = os.Rename(ClientJarDownloadName, ClientJarName); err == nil {
				os.Chtimes(ClientJarName, sourceTime, sourceTime)
			}
		}
		return err
	} else {
		return fmt.Errorf("Failed downloading jenkins client. Transfer failed. Cause: %v", err)
	}
}

// Registering the downloader.
var _ = RegisterPreparer(new(JenkinsClientDownloader))
<|endoftext|>


<commit_before>package db

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/denisenkom/go-mssqldb"
	"io/ioutil"
)

var _config *SqlDbConfig

func GetSqlServer() *sql.DB {
	sqlServer, err := sql.Open("mssql", _config.SqlConnStr)
	if err != nil {
		fmt.Printf("[db.GetSqlServer] open sql fail:%s", err.Error())
	}
	return sqlServer
}

func SetDBConfig(conf *SqlDbConfig) {
	_config = conf
}
<commit_msg>import<commit_after>package db

import (
	"database/sql"
	"fmt"
	_ "github.com/denisenkom/go-mssqldb"
)

var _config *SqlDbConfig

func GetSqlServer() *sql.DB {
	sqlServer, err := sql.Open("mssql", _config.SqlConnStr)
	if err != nil {
		fmt.Printf("[db.GetSqlServer] open sql fail:%s", err.Error())
	}
	return sqlServer
}

func SetDBConfig(conf *SqlDbConfig) {
	_config = conf
}
<|endoftext|>