<commit_before>package yum

import (
	"fmt"
	"reflect"
	"sort"
	"testing"
)

func rpmString(rpm RPM) string {
	return fmt.Sprintf("%s.%s-%d", rpm.Name(), rpm.Version(), rpm.Release())
}

func TestProvidesComparison(t *testing.T) {
	const name = "TestPackage"

	for _, table := range []struct {
		name     string
		input    RPMSlice
		expected []int
	}{
		{
			name: "Comparison",
			input: RPMSlice{
				NewProvides(name, "1.0.1", 2, 0, "EQ", nil),
				NewProvides(name, "1.0.1", 1, 0, "EQ", nil),
				NewProvides(name, "1.0.0", 1, 0, "EQ", nil),
			},
			expected: []int{2, 1, 0},
		},
		{
			name: "ComparisonNoRelease",
			input: RPMSlice{
				NewProvides(name, "1.0.1", 0, 0, "EQ", nil),
				NewProvides(name, "1.0.1", 1, 0, "EQ", nil),
				NewProvides(name, "1.0.0", 1, 0, "EQ", nil),
			},
			expected: []int{2, 0, 1},
		},
		{
			name: "ComparisonAlpha",
			input: RPMSlice{
				NewProvides(name, "1.0.9.B", 2, 0, "EQ", nil),
				NewProvides(name, "1.0.9.A", 1, 0, "EQ", nil),
				NewProvides(name, "1.0.0", 1, 0, "EQ", nil),
				NewProvides(name, "1.0.10.A", 1, 0, "EQ", nil),
			},
			expected: []int{2, 1, 0, 3},
		},
	} {
		exp := make(RPMSlice, 0, len(table.input))
		for _, idx := range table.expected {
			exp = append(exp, table.input[idx])
		}
		sort.Sort(table.input)

		if !reflect.DeepEqual(exp, table.input) {
			exp_str := "["
			for _, v := range exp {
				exp_str += rpmString(v)
			}
			exp_str += "]"
			in_str := "["
			for _, v := range table.input {
				in_str += rpmString(v)
			}
			in_str += "]"

			t.Fatalf("slices differ (%s):\nexp=%v\ngot=%v\n", table.name, exp_str, in_str)
		}
	}
}

func TestMatchEqual(t *testing.T) {
	const name = "TestPackage"
	const v1 = "1.0.1"
	const v2 = "1.2.0"
	const rel1 = 2
	const rel2 = 3

	// checking equality
	p1 := NewProvides(name, v1, rel1, 0, "EQ", nil)
	req := NewRequires(name, v1, rel1, 0, "EQ", "")
	if !req.ProvideMatches(p1) {
		t.Fatalf("%s should match %s.\n", rpmString(p1), rpmString(req))
	}

	// checking release mismatch
	p2 := NewProvides(name, v1, rel2, 0, "EQ", nil)
	if req.ProvideMatches(p2) {
		t.Fatalf("%s should NOT match %s.\n", rpmString(p2), rpmString(req))
	}

	// checking version mismatch
	p3 := NewProvides(name, v2, rel1, 0, "EQ", nil)
	if req.ProvideMatches(p3) {
		t.Fatalf("%s should NOT match %s.\n", rpmString(p3), rpmString(req))
	}

	// checking name mismatch
	p4 := NewProvides(name+"XYZ", v1, rel1, 0, "EQ", nil)
	if req.ProvideMatches(p4) {
		t.Fatalf("%s should NOT match %s.\n", rpmString(p4), rpmString(req))
	}

}

// EOF
<commit_msg>test: add TestFlags<commit_after>package yum

import (
	"fmt"
	"reflect"
	"sort"
	"testing"
)

func rpmString(rpm RPM) string {
	return fmt.Sprintf("%s.%s-%d", rpm.Name(), rpm.Version(), rpm.Release())
}

func TestProvidesComparison(t *testing.T) {
	const name = "TestPackage"

	for _, table := range []struct {
		name     string
		input    RPMSlice
		expected []int
	}{
		{
			name: "Comparison",
			input: RPMSlice{
				NewProvides(name, "1.0.1", 2, 0, "EQ", nil),
				NewProvides(name, "1.0.1", 1, 0, "EQ", nil),
				NewProvides(name, "1.0.0", 1, 0, "EQ", nil),
			},
			expected: []int{2, 1, 0},
		},
		{
			name: "ComparisonNoRelease",
			input: RPMSlice{
				NewProvides(name, "1.0.1", 0, 0, "EQ", nil),
				NewProvides(name, "1.0.1", 1, 0, "EQ", nil),
				NewProvides(name, "1.0.0", 1, 0, "EQ", nil),
			},
			expected: []int{2, 0, 1},
		},
		{
			name: "ComparisonAlpha",
			input: RPMSlice{
				NewProvides(name, "1.0.9.B", 2, 0, "EQ", nil),
				NewProvides(name, "1.0.9.A", 1, 0, "EQ", nil),
				NewProvides(name, "1.0.0", 1, 0, "EQ", nil),
				NewProvides(name, "1.0.10.A", 1, 0, "EQ", nil),
			},
			expected: []int{2, 1, 0, 3},
		},
	} {
		exp := make(RPMSlice, 0, len(table.input))
		for _, idx := range table.expected {
			exp = append(exp, table.input[idx])
		}
		sort.Sort(table.input)

		if !reflect.DeepEqual(exp, table.input) {
			exp_str := "["
			for _, v := range exp {
				exp_str += rpmString(v)
			}
			exp_str += "]"
			in_str := "["
			for _, v := range table.input {
				in_str += rpmString(v)
			}
			in_str += "]"

			t.Fatalf("slices differ (%s):\nexp=%v\ngot=%v\n", table.name, exp_str, in_str)
		}
	}
}

func TestMatchEqual(t *testing.T) {
	const name = "TestPackage"
	const v1 = "1.0.1"
	const v2 = "1.2.0"
	const rel1 = 2
	const rel2 = 3

	// checking equality
	p1 := NewProvides(name, v1, rel1, 0, "EQ", nil)
	req := NewRequires(name, v1, rel1, 0, "EQ", "")
	if !req.ProvideMatches(p1) {
		t.Fatalf("%s should match %s.\n", rpmString(p1), rpmString(req))
	}

	// checking release mismatch
	p2 := NewProvides(name, v1, rel2, 0, "EQ", nil)
	if req.ProvideMatches(p2) {
		t.Fatalf("%s should NOT match %s.\n", rpmString(p2), rpmString(req))
	}

	// checking version mismatch
	p3 := NewProvides(name, v2, rel1, 0, "EQ", nil)
	if req.ProvideMatches(p3) {
		t.Fatalf("%s should NOT match %s.\n", rpmString(p3), rpmString(req))
	}

	// checking name mismatch
	p4 := NewProvides(name+"XYZ", v1, rel1, 0, "EQ", nil)
	if req.ProvideMatches(p4) {
		t.Fatalf("%s should NOT match %s.\n", rpmString(p4), rpmString(req))
	}

}

func TestFlags(t *testing.T) {
	const name = "TestPackage"
	const v1 = "1.0.1"
	const v2 = "1.2.0"
	const v3 = "1.3.5"
	const rel = 2

	// checking simple comparison
	ps := RPMSlice{
		NewProvides(name, v1, rel, 0, "EQ", nil),
		NewProvides(name, v2, rel, 0, "EQ", nil),
		NewProvides(name, v3, rel, 0, "EQ", nil),
	}

	for _, table := range []struct {
		ctor string
		vals []bool
		strs []string
	}{
		{
			ctor: "GT",
			vals: []bool{false, false, true},
			strs: []string{
				"%s not %s %s",
				"%s not %s %s",
				"%s %s %s",
			},
		},
		{
			ctor: "GE",
			vals: []bool{false, true, true},
			strs: []string{
				"%s not %s %s",
				"%s %s %s",
				"%s %s %s",
			},
		},
		{
			ctor: "LT",
			vals: []bool{true, false, false},
			strs: []string{
				"%s %s %s",
				"%s not %s %s",
				"%s not %s %s",
			},
		},
		{
			ctor: "LE",
			vals: []bool{true, true, false},
			strs: []string{
				"%s %s %s",
				"%s %s %s",
				"%s not %s %s",
			},
		},
	} {
		req := NewRequires(name, v2, rel, 0, table.ctor, "")
		for i := range table.vals {
			o := req.ProvideMatches(ps[i])
			if o != table.vals[i] {
				t.Fatalf(table.strs[i], ps[i], table.ctor, req)
			}
		}
	}

}

// EOF
<|endoftext|>


<commit_before>package amino

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"reflect"
)

//----------------------------------------
// Typ3 and Typ4

type Typ3 uint8
type Typ4 uint8 // Typ3 | 0x08 (pointer bit)

const (
	// Typ3 types
	Typ3_Varint     = Typ3(0)
	Typ3_8Byte      = Typ3(1)
	Typ3_ByteLength = Typ3(2)
	Typ3_Struct     = Typ3(3)
	Typ3_StructTerm = Typ3(4)
	Typ3_4Byte      = Typ3(5)
	Typ3_List       = Typ3(6)
	Typ3_Interface  = Typ3(7)

	// Typ4 bit
	Typ4_Pointer = Typ4(0x08)
)

func (typ Typ3) String() string {
	switch typ {
	case Typ3_Varint:
		return "Varint"
	case Typ3_8Byte:
		return "8Byte"
	case Typ3_ByteLength:
		return "ByteLength"
	case Typ3_Struct:
		return "Struct"
	case Typ3_StructTerm:
		return "StructTerm"
	case Typ3_4Byte:
		return "4Byte"
	case Typ3_List:
		return "List"
	case Typ3_Interface:
		return "Interface"
	default:
		return fmt.Sprintf("<Invalid Typ3 %X>", byte(typ))
	}
}

func (typ Typ4) Typ3() Typ3      { return Typ3(typ & 0x07) }
func (typ Typ4) IsPointer() bool { return (typ & 0x08) > 0 }
func (typ Typ4) String() string {
	if typ&0xF0 != 0 {
		return fmt.Sprintf("<Invalid Typ4 %X>", byte(typ))
	}
	if typ&0x08 != 0 {
		return "*" + Typ3(typ&0x07).String()
	} else {
		return Typ3(typ).String()
	}
}

//----------------------------------------
// *Codec methods

// MarshalBinary encodes the object o according to the Amino spec,
// but prefixed by a uvarint encoding of the object to encode.
// Use MarshalBinaryBare if you don't want byte-length prefixing.
//
// For consistency, MarshalBinary will first dereference pointers
// before encoding.  MarshalBinary will panic if o is a nil-pointer,
// or if o is invalid.
func (cdc *Codec) MarshalBinary(o interface{}) ([]byte, error) {

	// Write the bytes here.
	var buf = new(bytes.Buffer)

	// Write the bz without length-prefixing.
	bz, err := cdc.MarshalBinaryBare(o)
	if err != nil {
		return nil, err
	}

	// Write uvarint(len(bz)).
	err = EncodeUvarint(buf, uint64(len(bz)))
	if err != nil {
		return nil, err
	}

	// Write bz.
	_, err = buf.Write(bz)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// MarshalBinaryWriter writes the bytes as would be returned from
// MarshalBinary to the writer w.
func (cdc *Codec) MarshalBinaryWriter(w io.Writer, o interface{}) error {
	var bz, err = cdc.MarshalBinary(o)
	if err != nil {
		return err
	}
	_, err = w.Write(bz)
	return err
}

// Panics if error.
func (cdc *Codec) MustMarshalBinary(o interface{}) []byte {
	bz, err := cdc.MarshalBinary(o)
	if err != nil {
		panic(err)
	}
	return bz
}

// MarshalBinaryBare encodes the object o according to the Amino spec.
// MarshalBinaryBare doesn't prefix the byte-length of the encoding,
// so the caller must handle framing.
func (cdc *Codec) MarshalBinaryBare(o interface{}) ([]byte, error) {

	// Dereference value if pointer.
	var rv, _, isNilPtr = derefPointers(reflect.ValueOf(o))
	if isNilPtr {
		// NOTE: You can still do so by calling
		// `.MarshalBinary(struct{ *SomeType })` or so on.
		panic("MarshalBinary cannot marshal a nil pointer directly. Try wrapping in a struct?")
	}

	// Encode Amino:binary bytes.
	var bz []byte
	buf := new(bytes.Buffer)
	rt := rv.Type()
	info, err := cdc.getTypeInfo_wlock(rt)
	if err != nil {
		return nil, err
	}
	err = cdc.encodeReflectBinary(buf, info, rv, FieldOptions{})
	if err != nil {
		return nil, err
	}
	bz = buf.Bytes()

	return bz, nil
}

// Panics if error.
func (cdc *Codec) MustMarshalBinaryBare(o interface{}) []byte {
	bz, err := cdc.MarshalBinaryBare(o)
	if err != nil {
		panic(err)
	}
	return bz
}

// Like UnmarshalBinaryBare, but will first decode the byte-length prefix.
// UnmarshalBinary will panic if ptr is a nil-pointer.
// Returns an error if not all of bz is consumed.
func (cdc *Codec) UnmarshalBinary(bz []byte, ptr interface{}) error {

	// Read byte-length prefix.
	u64, n := binary.Uvarint(bz)
	if n < 0 {
		return fmt.Errorf("Error reading msg byte-length prefix: got code %v", n)
	}
	if u64 > uint64(len(bz)-n) {
		return fmt.Errorf("Not enough bytes to read in UnmarshalBinary, want %v more bytes but only have %v",
			u64, len(bz)-n)
	} else if u64 < uint64(len(bz)-n) {
		return fmt.Errorf("Bytes left over in UnmarshalBinary, should read %v more bytes but have %v",
			u64, len(bz)-n)
	}
	bz = bz[n:]

	// Decode.
	return cdc.UnmarshalBinaryBare(bz, ptr)
}

// Like UnmarshalBinaryBare, but will first read the byte-length prefix.
// UnmarshalBinaryReader will panic if ptr is a nil-pointer.
// If maxSize is 0, there is no limit (not recommended).
func (cdc *Codec) UnmarshalBinaryReader(r io.Reader, ptr interface{}, maxSize int64) error {
	if maxSize < 0 {
		panic("maxSize cannot be negative.")
	}
	var br = bufio.NewReader(r)

	// Read byte-length prefix.
	var l int64
	u64, err := binary.ReadUvarint(br)
	if err != nil {
		return err
	}
	if maxSize > 0 && u64 > uint64(maxSize) {
		return fmt.Errorf("Read overflow, maxSize is %v but next message is %v bytes", maxSize, u64)
	}
	l = int64(u64)

	// Read that many bytes.
	var bz = make([]byte, l, l)
	_, err = io.ReadFull(br, bz)
	if err != nil {
		return err
	}

	// Decode.
	return cdc.UnmarshalBinaryBare(bz, ptr)
}

// UnmarshalBinaryBare will panic if ptr is a nil-pointer.
func (cdc *Codec) UnmarshalBinaryBare(bz []byte, ptr interface{}) error {
	rv, rt := reflect.ValueOf(ptr), reflect.TypeOf(ptr)
	if rv.Kind() != reflect.Ptr {
		panic("Unmarshal expects a pointer")
	}
	rv, rt = rv.Elem(), rt.Elem()
	info, err := cdc.getTypeInfo_wlock(rt)
	if err != nil {
		return err
	}
	n, err := cdc.decodeReflectBinary(bz, info, rv, FieldOptions{})
	if err != nil {
		return err
	}
	if n != len(bz) {
		return fmt.Errorf("Unmarshal didn't read all bytes. Expected to read %v, only read %v", len(bz), n)
	}
	return nil
}

func (cdc *Codec) MarshalJSON(o interface{}) ([]byte, error) {
	rv := reflect.ValueOf(o)
	if rv.Kind() == reflect.Invalid {
		return []byte("null"), nil
	}
	rt := rv.Type()

	// Note that we can't yet skip directly
	// to checking if a type implements
	// json.Marshaler because in some cases
	// var s GenericInterface = t1(v1)
	// var t GenericInterface = t2(v1)
	// but we need to be able to encode
	// both s and t disambiguated, so:
	//    {"type":<disfix>, "value":<data>}
	// for the above case.

	w := new(bytes.Buffer)
	info, err := cdc.getTypeInfo_wlock(rt)
	if err != nil {
		return nil, err
	}
	if err := cdc.encodeReflectJSON(w, info, rv, FieldOptions{}); err != nil {
		return nil, err
	}
	return w.Bytes(), nil
}

func (cdc *Codec) UnmarshalJSON(bz []byte, ptr interface{}) error {
	rv := reflect.ValueOf(ptr)
	if rv.Kind() != reflect.Ptr {
		return errors.New("UnmarshalJSON expects a pointer")
	}

	// If the type implements json.Unmarshaler, just
	// automatically respect that and skip to it.
	// if rv.Type().Implements(jsonUnmarshalerType) {
	// 	return rv.Interface().(json.Unmarshaler).UnmarshalJSON(bz)
	// }

	// 1. Dereference until we find the first addressable type.
	rv = rv.Elem()
	rt := rv.Type()
	info, err := cdc.getTypeInfo_wlock(rt)
	if err != nil {
		return err
	}
	return cdc.decodeReflectJSON(bz, info, rv, FieldOptions{})
}

//----------------------------------------
// Misc.

var (
	jsonMarshalerType   = reflect.TypeOf(new(json.Marshaler)).Elem()
	jsonUnmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem()
	errorType           = reflect.TypeOf(new(error)).Elem()
)
<commit_msg>MarshalBinaryWriter returns the number of bytes written<commit_after>package amino

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"reflect"
)

//----------------------------------------
// Typ3 and Typ4

type Typ3 uint8
type Typ4 uint8 // Typ3 | 0x08 (pointer bit)

const (
	// Typ3 types
	Typ3_Varint     = Typ3(0)
	Typ3_8Byte      = Typ3(1)
	Typ3_ByteLength = Typ3(2)
	Typ3_Struct     = Typ3(3)
	Typ3_StructTerm = Typ3(4)
	Typ3_4Byte      = Typ3(5)
	Typ3_List       = Typ3(6)
	Typ3_Interface  = Typ3(7)

	// Typ4 bit
	Typ4_Pointer = Typ4(0x08)
)

func (typ Typ3) String() string {
	switch typ {
	case Typ3_Varint:
		return "Varint"
	case Typ3_8Byte:
		return "8Byte"
	case Typ3_ByteLength:
		return "ByteLength"
	case Typ3_Struct:
		return "Struct"
	case Typ3_StructTerm:
		return "StructTerm"
	case Typ3_4Byte:
		return "4Byte"
	case Typ3_List:
		return "List"
	case Typ3_Interface:
		return "Interface"
	default:
		return fmt.Sprintf("<Invalid Typ3 %X>", byte(typ))
	}
}

func (typ Typ4) Typ3() Typ3      { return Typ3(typ & 0x07) }
func (typ Typ4) IsPointer() bool { return (typ & 0x08) > 0 }
func (typ Typ4) String() string {
	if typ&0xF0 != 0 {
		return fmt.Sprintf("<Invalid Typ4 %X>", byte(typ))
	}
	if typ&0x08 != 0 {
		return "*" + Typ3(typ&0x07).String()
	} else {
		return Typ3(typ).String()
	}
}

//----------------------------------------
// *Codec methods

// MarshalBinary encodes the object o according to the Amino spec,
// but prefixed by a uvarint encoding of the object to encode.
// Use MarshalBinaryBare if you don't want byte-length prefixing.
//
// For consistency, MarshalBinary will first dereference pointers
// before encoding.  MarshalBinary will panic if o is a nil-pointer,
// or if o is invalid.
func (cdc *Codec) MarshalBinary(o interface{}) ([]byte, error) {

	// Write the bytes here.
	var buf = new(bytes.Buffer)

	// Write the bz without length-prefixing.
	bz, err := cdc.MarshalBinaryBare(o)
	if err != nil {
		return nil, err
	}

	// Write uvarint(len(bz)).
	err = EncodeUvarint(buf, uint64(len(bz)))
	if err != nil {
		return nil, err
	}

	// Write bz.
	_, err = buf.Write(bz)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// MarshalBinaryWriter writes the bytes as would be returned from
// MarshalBinary to the writer w.
func (cdc *Codec) MarshalBinaryWriter(w io.Writer, o interface{}) (n int64, err error) {
	var bz, err = cdc.MarshalBinary(o)
	if err != nil {
		return err
	}
	n, err = w.Write(bz) // TODO: handle overflow in 32-bit systems.
	return err
}

// Panics if error.
func (cdc *Codec) MustMarshalBinary(o interface{}) []byte {
	bz, err := cdc.MarshalBinary(o)
	if err != nil {
		panic(err)
	}
	return bz
}

// MarshalBinaryBare encodes the object o according to the Amino spec.
// MarshalBinaryBare doesn't prefix the byte-length of the encoding,
// so the caller must handle framing.
func (cdc *Codec) MarshalBinaryBare(o interface{}) ([]byte, error) {

	// Dereference value if pointer.
	var rv, _, isNilPtr = derefPointers(reflect.ValueOf(o))
	if isNilPtr {
		// NOTE: You can still do so by calling
		// `.MarshalBinary(struct{ *SomeType })` or so on.
		panic("MarshalBinary cannot marshal a nil pointer directly. Try wrapping in a struct?")
	}

	// Encode Amino:binary bytes.
	var bz []byte
	buf := new(bytes.Buffer)
	rt := rv.Type()
	info, err := cdc.getTypeInfo_wlock(rt)
	if err != nil {
		return nil, err
	}
	err = cdc.encodeReflectBinary(buf, info, rv, FieldOptions{})
	if err != nil {
		return nil, err
	}
	bz = buf.Bytes()

	return bz, nil
}

// Panics if error.
func (cdc *Codec) MustMarshalBinaryBare(o interface{}) []byte {
	bz, err := cdc.MarshalBinaryBare(o)
	if err != nil {
		panic(err)
	}
	return bz
}

// Like UnmarshalBinaryBare, but will first decode the byte-length prefix.
// UnmarshalBinary will panic if ptr is a nil-pointer.
// Returns an error if not all of bz is consumed.
func (cdc *Codec) UnmarshalBinary(bz []byte, ptr interface{}) error {

	// Read byte-length prefix.
	u64, n := binary.Uvarint(bz)
	if n < 0 {
		return fmt.Errorf("Error reading msg byte-length prefix: got code %v", n)
	}
	if u64 > uint64(len(bz)-n) {
		return fmt.Errorf("Not enough bytes to read in UnmarshalBinary, want %v more bytes but only have %v",
			u64, len(bz)-n)
	} else if u64 < uint64(len(bz)-n) {
		return fmt.Errorf("Bytes left over in UnmarshalBinary, should read %v more bytes but have %v",
			u64, len(bz)-n)
	}
	bz = bz[n:]

	// Decode.
	return cdc.UnmarshalBinaryBare(bz, ptr)
}

// Like UnmarshalBinaryBare, but will first read the byte-length prefix.
// UnmarshalBinaryReader will panic if ptr is a nil-pointer.
// If maxSize is 0, there is no limit (not recommended).
func (cdc *Codec) UnmarshalBinaryReader(r io.Reader, ptr interface{}, maxSize int64) error {
	if maxSize < 0 {
		panic("maxSize cannot be negative.")
	}
	var br = bufio.NewReader(r)

	// Read byte-length prefix.
	var l int64
	u64, err := binary.ReadUvarint(br)
	if err != nil {
		return err
	}
	if maxSize > 0 && u64 > uint64(maxSize) {
		return fmt.Errorf("Read overflow, maxSize is %v but next message is %v bytes", maxSize, u64)
	}
	l = int64(u64)

	// Read that many bytes.
	var bz = make([]byte, l, l)
	_, err = io.ReadFull(br, bz)
	if err != nil {
		return err
	}

	// Decode.
	return cdc.UnmarshalBinaryBare(bz, ptr)
}

// UnmarshalBinaryBare will panic if ptr is a nil-pointer.
func (cdc *Codec) UnmarshalBinaryBare(bz []byte, ptr interface{}) error {
	rv, rt := reflect.ValueOf(ptr), reflect.TypeOf(ptr)
	if rv.Kind() != reflect.Ptr {
		panic("Unmarshal expects a pointer")
	}
	rv, rt = rv.Elem(), rt.Elem()
	info, err := cdc.getTypeInfo_wlock(rt)
	if err != nil {
		return err
	}
	n, err := cdc.decodeReflectBinary(bz, info, rv, FieldOptions{})
	if err != nil {
		return err
	}
	if n != len(bz) {
		return fmt.Errorf("Unmarshal didn't read all bytes. Expected to read %v, only read %v", len(bz), n)
	}
	return nil
}

func (cdc *Codec) MarshalJSON(o interface{}) ([]byte, error) {
	rv := reflect.ValueOf(o)
	if rv.Kind() == reflect.Invalid {
		return []byte("null"), nil
	}
	rt := rv.Type()

	// Note that we can't yet skip directly
	// to checking if a type implements
	// json.Marshaler because in some cases
	// var s GenericInterface = t1(v1)
	// var t GenericInterface = t2(v1)
	// but we need to be able to encode
	// both s and t disambiguated, so:
	//    {"type":<disfix>, "value":<data>}
	// for the above case.

	w := new(bytes.Buffer)
	info, err := cdc.getTypeInfo_wlock(rt)
	if err != nil {
		return nil, err
	}
	if err := cdc.encodeReflectJSON(w, info, rv, FieldOptions{}); err != nil {
		return nil, err
	}
	return w.Bytes(), nil
}

func (cdc *Codec) UnmarshalJSON(bz []byte, ptr interface{}) error {
	rv := reflect.ValueOf(ptr)
	if rv.Kind() != reflect.Ptr {
		return errors.New("UnmarshalJSON expects a pointer")
	}

	// If the type implements json.Unmarshaler, just
	// automatically respect that and skip to it.
	// if rv.Type().Implements(jsonUnmarshalerType) {
	// 	return rv.Interface().(json.Unmarshaler).UnmarshalJSON(bz)
	// }

	// 1. Dereference until we find the first addressable type.
	rv = rv.Elem()
	rt := rv.Type()
	info, err := cdc.getTypeInfo_wlock(rt)
	if err != nil {
		return err
	}
	return cdc.decodeReflectJSON(bz, info, rv, FieldOptions{})
}

//----------------------------------------
// Misc.

var (
	jsonMarshalerType   = reflect.TypeOf(new(json.Marshaler)).Elem()
	jsonUnmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem()
	errorType           = reflect.TypeOf(new(error)).Elem()
)
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"time"

	"github.com/juju/errors"
	"github.com/juju/names"
	jujutxn "github.com/juju/txn"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
	"gopkg.in/mgo.v2/txn"

	"github.com/juju/juju/resource"
)

const (
	// CleanupKindResourceBlob identifies the cleanup kind
	// for resource blobs.
	CleanupKindResourceBlob = "resourceBlob"
)

// ResourcePersistenceBase exposes the core persistence functionality
// needed for resources.
type ResourcePersistenceBase interface {
	// One populates doc with the document corresponding to the given
	// ID. Missing documents result in errors.NotFound.
	One(collName, id string, doc interface{}) error

	// All populates docs with the list of the documents corresponding
	// to the provided query.
	All(collName string, query, docs interface{}) error

	// Run runs the transaction generated by the provided factory
	// function. It may be retried several times.
	Run(transactions jujutxn.TransactionSource) error

	// ServiceExistsOps returns the operations that verify that the
	// identified service exists.
	ServiceExistsOps(serviceID string) []txn.Op

	// IncCharmModifiedVersionOps returns the operations necessary to increment
	// the CharmModifiedVersion field for the given service.
	IncCharmModifiedVersionOps(serviceID string) []txn.Op

	// NewCleanupOp creates a mgo transaction operation that queues up
	// some cleanup action in state.
	NewCleanupOp(kind, prefix string) txn.Op
}

// ResourcePersistence provides the persistence functionality for the
// Juju environment as a whole.
type ResourcePersistence struct {
	base ResourcePersistenceBase
}

// NewResourcePersistence wraps the base in a new ResourcePersistence.
func NewResourcePersistence(base ResourcePersistenceBase) *ResourcePersistence {
	return &ResourcePersistence{
		base: base,
	}
}

// ListResources returns the info for each non-pending resource of the
// identified service.
func (p ResourcePersistence) ListResources(serviceID string) (resource.ServiceResources, error) {
	logger.Tracef("listing all resources for service %q", serviceID)

	docs, err := p.resources(serviceID)
	if err != nil {
		return resource.ServiceResources{}, errors.Trace(err)
	}

	store := map[string]charmresource.Resource{}
	units := map[names.UnitTag][]resource.Resource{}
	downloadProgress := make(map[names.UnitTag]map[string]int64)

	var results resource.ServiceResources
	for _, doc := range docs {
		if doc.PendingID != "" {
			continue
		}

		res, err := doc2basicResource(doc)
		if err != nil {
			return resource.ServiceResources{}, errors.Trace(err)
		}
		if !doc.LastPolled.IsZero() {
			store[res.Name] = res.Resource
			continue
		}
		if doc.UnitID == "" {
			results.Resources = append(results.Resources, res)
			continue
		}
		tag := names.NewUnitTag(doc.UnitID)
		units[tag] = append(units[tag], res)
		if doc.DownloadProgress != nil {
			if downloadProgress[tag] == nil {
				downloadProgress[tag] = make(map[string]int64)
			}
			downloadProgress[tag][doc.Name] = *doc.DownloadProgress
		}
	}
	for _, res := range results.Resources {
		storeRes := store[res.Name]
		results.CharmStoreResources = append(results.CharmStoreResources, storeRes)
	}
	for tag, res := range units {
		results.UnitResources = append(results.UnitResources, resource.UnitResources{
			Tag:              tag,
			Resources:        res,
			DownloadProgress: downloadProgress[tag],
		})
	}
	return results, nil
}

// ListPendingResources returns the extended, model-related info for
// each pending resource of the identifies service.
func (p ResourcePersistence) ListPendingResources(serviceID string) ([]resource.Resource, error) {
	docs, err := p.resources(serviceID)
	if err != nil {
		return nil, errors.Trace(err)
	}

	var resources []resource.Resource
	for _, doc := range docs {
		if doc.PendingID == "" {
			continue
		}
		// doc.UnitID will always be empty here.

		res, err := doc2basicResource(doc)
		if err != nil {
			return nil, errors.Trace(err)
		}
		resources = append(resources, res)
	}
	return resources, nil
}

// GetResource returns the extended, model-related info for the non-pending
// resource.
func (p ResourcePersistence) GetResource(id string) (res resource.Resource, storagePath string, _ error) {
	doc, err := p.getOne(id)
	if err != nil {
		return res, "", errors.Trace(err)
	}

	stored, err := doc2resource(doc)
	if err != nil {
		return res, "", errors.Trace(err)
	}

	return stored.Resource, stored.storagePath, nil
}

// StageResource adds the resource in a separate staging area
// if the resource isn't already staged. If it is then
// errors.AlreadyExists is returned. A wrapper around the staged
// resource is returned which supports both finalizing and removing
// the staged resource.
func (p ResourcePersistence) StageResource(res resource.Resource, storagePath string) (*StagedResource, error) {
	if storagePath == "" {
		return nil, errors.Errorf("missing storage path")
	}

	if err := res.Validate(); err != nil {
		return nil, errors.Annotate(err, "bad resource")
	}

	stored := storedResource{
		Resource:    res,
		storagePath: storagePath,
	}
	staged := &StagedResource{
		base:   p.base,
		id:     res.ID,
		stored: stored,
	}
	if err := staged.stage(); err != nil {
		return nil, errors.Trace(err)
	}
	return staged, nil
}

// SetResource sets the info for the resource.
func (p ResourcePersistence) SetResource(res resource.Resource) error {
	stored, err := p.getStored(res)
	if errors.IsNotFound(err) {
		stored = storedResource{Resource: res}
	} else if err != nil {
		return errors.Trace(err)
	}
	// TODO(ericsnow) Ensure that stored.Resource matches res? If we do
	// so then the following line is unnecessary.
	stored.Resource = res

	if err := res.Validate(); err != nil {
		return errors.Annotate(err, "bad resource")
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		// This is an "upsert".
		var ops []txn.Op
		switch attempt {
		case 0:
			ops = newInsertResourceOps(stored)
		case 1:
			ops = newUpdateResourceOps(stored)
		default:
			// Either insert or update will work so we should not get here.
			return nil, errors.New("setting the resource failed")
		}
		if stored.PendingID == "" {
			// Only non-pending resources must have an existing service.
			ops = append(ops, p.base.ServiceExistsOps(res.ServiceID)...)
		}
		return ops, nil
	}
	if err := p.base.Run(buildTxn); err != nil {
		return errors.Trace(err)
	}
	return nil
}

// SetCharmStoreResource stores the resource info that was retrieved
// from the charm store.
func (p ResourcePersistence) SetCharmStoreResource(id, serviceID string, res charmresource.Resource, lastPolled time.Time) error {
	if err := res.Validate(); err != nil {
		return errors.Annotate(err, "bad resource")
	}

	csRes := charmStoreResource{
		Resource:   res,
		id:         id,
		serviceID:  serviceID,
		lastPolled: lastPolled,
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		// This is an "upsert".
		var ops []txn.Op
		switch attempt {
		case 0:
			ops = newInsertCharmStoreResourceOps(csRes)
		case 1:
			ops = newUpdateCharmStoreResourceOps(csRes)
		default:
			// Either insert or update will work so we should not get here.
			return nil, errors.New("setting the resource failed")
		}
		// No pending resources so we always do this here.
		ops = append(ops, p.base.ServiceExistsOps(serviceID)...)
		return ops, nil
	}
	if err := p.base.Run(buildTxn); err != nil {
		return errors.Trace(err)
	}
	return nil
}

// SetUnitResource stores the resource info for a particular unit. The
// resource must already be set for the service.
func (p ResourcePersistence) SetUnitResource(unitID string, res resource.Resource) error {
	if res.PendingID != "" {
		return errors.Errorf("pending resources not allowed")
	}

	stored, err := p.getStored(res)
	if err != nil {
		return errors.Trace(err)
	}
	// TODO(ericsnow) Ensure that stored.Resource matches res? If we do
	// so then the following line is unnecessary.
	stored.Resource = res

	if err := res.Validate(); err != nil {
		return errors.Annotate(err, "bad resource")
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		// This is an "upsert".
		var ops []txn.Op
		switch attempt {
		case 0:
			ops = newInsertUnitResourceOps(unitID, stored)
		case 1:
			ops = newUpdateUnitResourceOps(unitID, stored)
		default:
			// Either insert or update will work so we should not get here.
			return nil, errors.New("setting the resource failed")
		}
		// No pending resources so we always do this here.
		ops = append(ops, p.base.ServiceExistsOps(res.ServiceID)...)
		return ops, nil
	}
	if err := p.base.Run(buildTxn); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (p ResourcePersistence) getStored(res resource.Resource) (storedResource, error) {
	doc, err := p.getOne(res.ID)
	if errors.IsNotFound(err) {
		err = errors.NotFoundf("resource %q", res.Name)
	}
	if err != nil {
		return storedResource{}, errors.Trace(err)
	}

	stored, err := doc2resource(doc)
	if err != nil {
		return stored, errors.Trace(err)
	}

	return stored, nil
}

// NewResolvePendingResourceOps generates mongo transaction operations
// to set the identified resource as active.
//
// Leaking mongo details (transaction ops) is a necessary evil since we
// do not have any machinery to facilitate transactions between
// different components.
func (p ResourcePersistence) NewResolvePendingResourceOps(resID, pendingID string) ([]txn.Op, error) {
	if pendingID == "" {
		return nil, errors.New("missing pending ID")
	}

	oldDoc, err := p.getOnePending(resID, pendingID)
	if errors.IsNotFound(err) {
		return nil, errors.NotFoundf("pending resource %q (%s)", resID, pendingID)
	}
	if err != nil {
		return nil, errors.Trace(err)
	}
	pending, err := doc2resource(oldDoc)
	if err != nil {
		return nil, errors.Trace(err)
	}

	exists := true
	if _, err := p.getOne(resID); errors.IsNotFound(err) {
		exists = false
	} else if err != nil {
		return nil, errors.Trace(err)
	}

	ops := newResolvePendingResourceOps(pending, exists)
	return ops, nil
}

// NewRemoveUnitResourcesOps returns mgo transaction operations
// that remove resource information specific to the unit from state.
func (p ResourcePersistence) NewRemoveUnitResourcesOps(unitID string) ([]txn.Op, error) {
	docs, err := p.unitResources(unitID)
	if err != nil {
		return nil, errors.Trace(err)
	}

	ops := newRemoveResourcesOps(docs)
	// We do not remove the resource from the blob store here. That is
	// a service-level matter.
	return ops, nil
}

// NewRemoveResourcesOps returns mgo transaction operations that
// remove all the service's resources from state.
func (p ResourcePersistence) NewRemoveResourcesOps(serviceID string) ([]txn.Op, error) {
	docs, err := p.resources(serviceID)
	if err != nil {
		return nil, errors.Trace(err)
	}

	ops := newRemoveResourcesOps(docs)
	for _, doc := range docs {
		ops = append(ops, p.base.NewCleanupOp(CleanupKindResourceBlob, doc.StoragePath))
	}
	return ops, nil
}
<commit_msg>Do not list pending unit resources.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"time"

	"github.com/juju/errors"
	"github.com/juju/names"
	jujutxn "github.com/juju/txn"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
	"gopkg.in/mgo.v2/txn"

	"github.com/juju/juju/resource"
)

const (
	// CleanupKindResourceBlob identifies the cleanup kind
	// for resource blobs.
	CleanupKindResourceBlob = "resourceBlob"
)

// ResourcePersistenceBase exposes the core persistence functionality
// needed for resources.
type ResourcePersistenceBase interface {
	// One populates doc with the document corresponding to the given
	// ID. Missing documents result in errors.NotFound.
	One(collName, id string, doc interface{}) error

	// All populates docs with the list of the documents corresponding
	// to the provided query.
	All(collName string, query, docs interface{}) error

	// Run runs the transaction generated by the provided factory
	// function. It may be retried several times.
	Run(transactions jujutxn.TransactionSource) error

	// ServiceExistsOps returns the operations that verify that the
	// identified service exists.
	ServiceExistsOps(serviceID string) []txn.Op

	// IncCharmModifiedVersionOps returns the operations necessary to increment
	// the CharmModifiedVersion field for the given service.
	IncCharmModifiedVersionOps(serviceID string) []txn.Op

	// NewCleanupOp creates a mgo transaction operation that queues up
	// some cleanup action in state.
	NewCleanupOp(kind, prefix string) txn.Op
}

// ResourcePersistence provides the persistence functionality for the
// Juju environment as a whole.
type ResourcePersistence struct {
	base ResourcePersistenceBase
}

// NewResourcePersistence wraps the base in a new ResourcePersistence.
func NewResourcePersistence(base ResourcePersistenceBase) *ResourcePersistence {
	return &ResourcePersistence{
		base: base,
	}
}

// ListResources returns the info for each non-pending resource of the
// identified service.
func (p ResourcePersistence) ListResources(serviceID string) (resource.ServiceResources, error) {
	logger.Tracef("listing all resources for service %q", serviceID)

	docs, err := p.resources(serviceID)
	if err != nil {
		return resource.ServiceResources{}, errors.Trace(err)
	}

	store := map[string]charmresource.Resource{}
	units := map[names.UnitTag][]resource.Resource{}
	downloadProgress := make(map[names.UnitTag]map[string]int64)

	var results resource.ServiceResources
	for _, doc := range docs {
		if doc.PendingID != "" {
			continue
		}

		res, err := doc2basicResource(doc)
		if err != nil {
			return resource.ServiceResources{}, errors.Trace(err)
		}
		if !doc.LastPolled.IsZero() {
			store[res.Name] = res.Resource
			continue
		}
		if doc.UnitID == "" {
			results.Resources = append(results.Resources, res)
			continue
		}
		tag := names.NewUnitTag(doc.UnitID)
		if doc.PendingID == "" {
			units[tag] = append(units[tag], res)
		}
		if doc.DownloadProgress != nil {
			if downloadProgress[tag] == nil {
				downloadProgress[tag] = make(map[string]int64)
			}
			downloadProgress[tag][doc.Name] = *doc.DownloadProgress
		}
	}
	for _, res := range results.Resources {
		storeRes := store[res.Name]
		results.CharmStoreResources = append(results.CharmStoreResources, storeRes)
	}
	for tag, res := range units {
		results.UnitResources = append(results.UnitResources, resource.UnitResources{
			Tag:              tag,
			Resources:        res,
			DownloadProgress: downloadProgress[tag],
		})
	}
	return results, nil
}

// ListPendingResources returns the extended, model-related info for
// each pending resource of the identifies service.
func (p ResourcePersistence) ListPendingResources(serviceID string) ([]resource.Resource, error) {
	docs, err := p.resources(serviceID)
	if err != nil {
		return nil, errors.Trace(err)
	}

	var resources []resource.Resource
	for _, doc := range docs {
		if doc.PendingID == "" {
			continue
		}
		// doc.UnitID will always be empty here.

		res, err := doc2basicResource(doc)
		if err != nil {
			return nil, errors.Trace(err)
		}
		resources = append(resources, res)
	}
	return resources, nil
}

// GetResource returns the extended, model-related info for the non-pending
// resource.
func (p ResourcePersistence) GetResource(id string) (res resource.Resource, storagePath string, _ error) {
	doc, err := p.getOne(id)
	if err != nil {
		return res, "", errors.Trace(err)
	}

	stored, err := doc2resource(doc)
	if err != nil {
		return res, "", errors.Trace(err)
	}

	return stored.Resource, stored.storagePath, nil
}

// StageResource adds the resource in a separate staging area
// if the resource isn't already staged. If it is then
// errors.AlreadyExists is returned. A wrapper around the staged
// resource is returned which supports both finalizing and removing
// the staged resource.
func (p ResourcePersistence) StageResource(res resource.Resource, storagePath string) (*StagedResource, error) {
	if storagePath == "" {
		return nil, errors.Errorf("missing storage path")
	}

	if err := res.Validate(); err != nil {
		return nil, errors.Annotate(err, "bad resource")
	}

	stored := storedResource{
		Resource:    res,
		storagePath: storagePath,
	}
	staged := &StagedResource{
		base:   p.base,
		id:     res.ID,
		stored: stored,
	}
	if err := staged.stage(); err != nil {
		return nil, errors.Trace(err)
	}
	return staged, nil
}

// SetResource sets the info for the resource.
func (p ResourcePersistence) SetResource(res resource.Resource) error {
	stored, err := p.getStored(res)
	if errors.IsNotFound(err) {
		stored = storedResource{Resource: res}
	} else if err != nil {
		return errors.Trace(err)
	}
	// TODO(ericsnow) Ensure that stored.Resource matches res? If we do
	// so then the following line is unnecessary.
	stored.Resource = res

	if err := res.Validate(); err != nil {
		return errors.Annotate(err, "bad resource")
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		// This is an "upsert".
		var ops []txn.Op
		switch attempt {
		case 0:
			ops = newInsertResourceOps(stored)
		case 1:
			ops = newUpdateResourceOps(stored)
		default:
			// Either insert or update will work so we should not get here.
			return nil, errors.New("setting the resource failed")
		}
		if stored.PendingID == "" {
			// Only non-pending resources must have an existing service.
			ops = append(ops, p.base.ServiceExistsOps(res.ServiceID)...)
		}
		return ops, nil
	}
	if err := p.base.Run(buildTxn); err != nil {
		return errors.Trace(err)
	}
	return nil
}

// SetCharmStoreResource stores the resource info that was retrieved
// from the charm store.
func (p ResourcePersistence) SetCharmStoreResource(id, serviceID string, res charmresource.Resource, lastPolled time.Time) error {
	if err := res.Validate(); err != nil {
		return errors.Annotate(err, "bad resource")
	}

	csRes := charmStoreResource{
		Resource:   res,
		id:         id,
		serviceID:  serviceID,
		lastPolled: lastPolled,
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		// This is an "upsert".
		var ops []txn.Op
		switch attempt {
		case 0:
			ops = newInsertCharmStoreResourceOps(csRes)
		case 1:
			ops = newUpdateCharmStoreResourceOps(csRes)
		default:
			// Either insert or update will work so we should not get here.
			return nil, errors.New("setting the resource failed")
		}
		// No pending resources so we always do this here.
		ops = append(ops, p.base.ServiceExistsOps(serviceID)...)
		return ops, nil
	}
	if err := p.base.Run(buildTxn); err != nil {
		return errors.Trace(err)
	}
	return nil
}

// SetUnitResource stores the resource info for a particular unit. The
// resource must already be set for the service.
func (p ResourcePersistence) SetUnitResource(unitID string, res resource.Resource) error {
	if res.PendingID != "" {
		return errors.Errorf("pending resources not allowed")
	}

	stored, err := p.getStored(res)
	if err != nil {
		return errors.Trace(err)
	}
	// TODO(ericsnow) Ensure that stored.Resource matches res? If we do
	// so then the following line is unnecessary.
	stored.Resource = res

	if err := res.Validate(); err != nil {
		return errors.Annotate(err, "bad resource")
	}

	buildTxn := func(attempt int) ([]txn.Op, error) {
		// This is an "upsert".
		var ops []txn.Op
		switch attempt {
		case 0:
			ops = newInsertUnitResourceOps(unitID, stored)
		case 1:
			ops = newUpdateUnitResourceOps(unitID, stored)
		default:
			// Either insert or update will work so we should not get here.
			return nil, errors.New("setting the resource failed")
		}
		// No pending resources so we always do this here.
		ops = append(ops, p.base.ServiceExistsOps(res.ServiceID)...)
		return ops, nil
	}
	if err := p.base.Run(buildTxn); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (p ResourcePersistence) getStored(res resource.Resource) (storedResource, error) {
	doc, err := p.getOne(res.ID)
	if errors.IsNotFound(err) {
		err = errors.NotFoundf("resource %q", res.Name)
	}
	if err != nil {
		return storedResource{}, errors.Trace(err)
	}

	stored, err := doc2resource(doc)
	if err != nil {
		return stored, errors.Trace(err)
	}

	return stored, nil
}

// NewResolvePendingResourceOps generates mongo transaction operations
// to set the identified resource as active.
//
// Leaking mongo details (transaction ops) is a necessary evil since we
// do not have any machinery to facilitate transactions between
// different components.
func (p ResourcePersistence) NewResolvePendingResourceOps(resID, pendingID string) ([]txn.Op, error) {
	if pendingID == "" {
		return nil, errors.New("missing pending ID")
	}

	oldDoc, err := p.getOnePending(resID, pendingID)
	if errors.IsNotFound(err) {
		return nil, errors.NotFoundf("pending resource %q (%s)", resID, pendingID)
	}
	if err != nil {
		return nil, errors.Trace(err)
	}
	pending, err := doc2resource(oldDoc)
	if err != nil {
		return nil, errors.Trace(err)
	}

	exists := true
	if _, err := p.getOne(resID); errors.IsNotFound(err) {
		exists = false
	} else if err != nil {
		return nil, errors.Trace(err)
	}

	ops := newResolvePendingResourceOps(pending, exists)
	return ops, nil
}

// NewRemoveUnitResourcesOps returns mgo transaction operations
// that remove resource information specific to the unit from state.
func (p ResourcePersistence) NewRemoveUnitResourcesOps(unitID string) ([]txn.Op, error) {
	docs, err := p.unitResources(unitID)
	if err != nil {
		return nil, errors.Trace(err)
	}

	ops := newRemoveResourcesOps(docs)
	// We do not remove the resource from the blob store here. That is
	// a service-level matter.
	return ops, nil
}

// NewRemoveResourcesOps returns mgo transaction operations that
// remove all the service's resources from state.
func (p ResourcePersistence) NewRemoveResourcesOps(serviceID string) ([]txn.Op, error) {
	docs, err := p.resources(serviceID)
	if err != nil {
		return nil, errors.Trace(err)
	}

	ops := newRemoveResourcesOps(docs)
	for _, doc := range docs {
		ops = append(ops, p.base.NewCleanupOp(CleanupKindResourceBlob, doc.StoragePath))
	}
	return ops, nil
}
<|endoftext|>


<commit_before>package hammer

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/arodland/go-descriptivestats/descriptivestats"
	"github.com/bmizerany/perks/quantile"
)

type Hammer struct {
	RunFor           float64
	Threads          int
	Backlog          int
	QPS              float64
	LogErrors        bool
	GenerateFunction RequestGenerator
	Exit             chan int
	Requests         chan Request
	throttled        chan Request
	results          chan Result
	stats            chan StatsSummary
	requestWorkers   sync.WaitGroup
	finishedResults  sync.WaitGroup
}

func (hammer *Hammer) warn(msg string) {
	log.Println(msg)
}

func (hammer *Hammer) warnf(fmt string, args ...interface{}) {
	log.Printf(fmt, args...)
}

type BodyBehavior int

const (
	IgnoreBody BodyBehavior = iota
	DiscardBody
	CopyBody
)

type Request struct {
	HTTPRequest  *http.Request
	Name         string
	BodyBehavior BodyBehavior
	Callback     RequestCallback
}

type RequestCallback func(Request, *http.Response, Result)

type RequestGenerator func(*Hammer)

type RandomURLGeneratorOptions struct {
	URLs         []string
	Headers      map[string][]string
	Name         string
	BodyBehavior BodyBehavior
	Callback     RequestCallback
}

func RandomURLGenerator(opts RandomURLGeneratorOptions) RequestGenerator {
	readiedRequests := make([]Request, len(opts.URLs))
	for i, url := range opts.URLs {
		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			panic(err)
		}
		req.Header = opts.Headers
		readiedRequests[i] = Request{
			BodyBehavior: opts.BodyBehavior,
			HTTPRequest:  req,
			Name:         opts.Name,
			Callback:     opts.Callback,
		}
	}
	num := len(readiedRequests)

	return func(hammer *Hammer) {
		defer func() { close(hammer.Requests) }()

		for {
			select {
			case <-hammer.Exit:
				return
			default:
				var idx int
				if num == 1 {
					idx = 0
				} else {
					idx = rand.Intn(len(readiedRequests))
				}
				hammer.Requests <- readiedRequests[idx]
			}
		}
	}
}

func (hammer *Hammer) SendRequest(req Request) {
	hammer.Requests <- req
}

func (hammer *Hammer) SendRequestImmediately(req Request) {
	hammer.throttled <- req
}

func (hammer *Hammer) throttle() {
	ticker := time.NewTicker(time.Duration(float64(time.Second) / hammer.QPS))
	defer ticker.Stop()
	defer close(hammer.throttled)

	for {
		select {
		case <-ticker.C:
			req, ok := <-hammer.Requests
			if !ok {
				return
			}
			hammer.throttled <- req
		}
	}
}

type Result struct {
	Name       string
	Status     int
	Start      time.Time
	GotHeaders time.Time
	GotBody    time.Time
	BodySize   int64
}

func copyResponseBody(res *http.Response) (gotBody time.Time, size int64) {
	var buf bytes.Buffer
	size, _ = io.Copy(&buf, res.Body)
	gotBody = time.Now()
	res.Body = ioutil.NopCloser(bytes.NewReader(buf.Bytes()))
	return
}

func (hammer *Hammer) sendRequests() {
	defer hammer.requestWorkers.Done()

	client := &http.Client{}

	for req := range hammer.throttled {
		var result Result
		result.Name = req.Name
		result.Start = time.Now()
		res, err := client.Do(req.HTTPRequest)
		result.GotHeaders = time.Now()
		if err != nil {
			result.Status = 499
			hammer.warn(err.Error())
		} else {
			result.Status = res.StatusCode
			if result.Status >= 400 {
				if req.BodyBehavior == CopyBody {
					result.GotBody, result.BodySize = copyResponseBody(res)
				}
				if hammer.LogErrors {
					// TODO: refactor this into a method
					logOut, err := ioutil.TempFile(".", "error.log.")
					if err == nil {
						res.Write(logOut)
						result.GotBody = time.Now()
					} else {
						hammer.warnf("%s writing error log\n", err.Error())
					}
				} else if req.BodyBehavior == DiscardBody {
					result.BodySize, _ = io.Copy(ioutil.Discard, res.Body)
					result.GotBody = time.Now()
				}
				hammer.warnf("Got status %s for %s\n", res.Status, req.HTTPRequest.URL.String())
			} else if req.BodyBehavior == CopyBody {
				result.GotBody, result.BodySize = copyResponseBody(res)
			} else if req.BodyBehavior == DiscardBody {
				result.BodySize, _ = io.Copy(ioutil.Discard, res.Body)
				result.GotBody = time.Now()
			} else {
				res.Body.Close()
			}
		}
		if req.Callback != nil {
			go req.Callback(req, res, result)
		}
		hammer.results <- result
	}
}

type Stats struct {
	Name           string
	Quantiles      []float64
	Begin          time.Time
	End            time.Time
	Statuses       map[int]int
	HeaderStats    descriptivestats.Stats
	HeaderQuantile quantile.Stream
	BodyStats      descriptivestats.Stats
	BodyQuantile   quantile.Stream
	Bytes          float64
}

type SingleStatSummary struct {
	descriptivestats.Stats
	Quantiles map[float64]float64
}

type StatsSummary struct {
	Name     string
	Begin    time.Time
	End      time.Time
	Statuses map[int]int
	Headers  SingleStatSummary
	Body     SingleStatSummary
	Bytes    float64
}

func newStats(name string, quantiles ...float64) *Stats {
	stats := Stats{
		Name:           name,
		Quantiles:      quantiles,
		Statuses:       make(map[int]int),
		HeaderStats:    descriptivestats.Stats{},
		HeaderQuantile: *(quantile.NewTargeted(quantiles...)),
		BodyStats:      descriptivestats.Stats{},
		BodyQuantile:   *(quantile.NewTargeted(quantiles...)),
	}
	stats.HeaderQuantile.SetEpsilon(0.001)
	stats.BodyQuantile.SetEpsilon(0.001)
	return &stats
}

func (stats *Stats) Summarize() (summary StatsSummary) {
	summary.Name = stats.Name
	summary.Begin = stats.Begin
	summary.End = stats.End
	summary.Statuses = stats.Statuses
	summary.Bytes = stats.Bytes
	summary.Headers.Stats = stats.HeaderStats
	summary.Headers.Quantiles = make(map[float64]float64, len(stats.Quantiles))
	for _, quantile := range stats.Quantiles {
		summary.Headers.Quantiles[quantile] = stats.HeaderQuantile.Query(quantile)
	}
	summary.Body.Stats = stats.BodyStats
	if stats.BodyStats.Count > 0 {
		summary.Body.Quantiles = make(map[float64]float64, len(stats.Quantiles))
		for _, quantile := range stats.Quantiles {
			summary.Body.Quantiles[quantile] = stats.BodyQuantile.Query(quantile)
		}
	}
	return
}

func (hammer *Hammer) collectResults() {
	defer hammer.finishedResults.Done()

	statsMap := map[string]*Stats{}

	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	defer func() {
		for _, stats := range statsMap {
			hammer.stats <- stats.Summarize()
		}
		close(hammer.stats)
	}()

	for {
		select {
		case res, ok := <-hammer.results:
			if !ok {
				return
			}

			stats, statsExisted := statsMap[res.Name]
			if !statsExisted {
				stats = newStats(res.Name, 0.05, 0.5, 0.95)
				statsMap[res.Name] = stats
			}

			stats.Statuses[res.Status]++
			stats.Bytes += float64(res.BodySize)

			start := res.Start
			end := res.GotHeaders
			dur := end.Sub(start).Seconds()
			stats.HeaderStats.Add(dur)
			stats.HeaderQuantile.Insert(dur)
			if res.GotBody != (time.Time{}) {
				end = res.GotBody
				dur := end.Sub(start).Seconds()
				stats.BodyStats.Add(dur)
				stats.BodyQuantile.Insert(dur)
			}
			if !statsExisted {
				stats.Begin = start
				stats.End = end
			} else {
				if start.Before(stats.Begin) {
					stats.Begin = start
				}
				if start.After(stats.End) {
					stats.End = start
				}
			}
		case <-ticker.C:
			for _, stats := range statsMap {
				hammer.stats <- stats.Summarize()
			}
		}
	}
}

func (stats *StatsSummary) PrintReport(w io.Writer) {
	runTime := stats.End.Sub(stats.Begin).Seconds()
	count := stats.Headers.Count
	fmt.Fprintf(
		w,
		`Run time: %.3f
Total hits: %.0f
Hits/sec: %.3f
`,
		runTime,
		count,
		count/runTime,
	)
	if stats.Body.Count > 0 {
		fmt.Fprintf(
			w,
			"Total bytes: %0.f\nBytes/sec: %.0f\n",
			stats.Bytes,
			stats.Bytes/runTime,
		)
	}

	fmt.Fprintf(w, "\nStatus totals:\n")
	statusCodes := []int{}
	for code, _ := range stats.Statuses {
		statusCodes = append(statusCodes, code)
	}
	sort.Ints(statusCodes)
	for _, code := range statusCodes {
		fmt.Fprintf(w, "%d\t%d\t%.3f\n", code, stats.Statuses[code], 100*float64(stats.Statuses[code])/count)
	}
	if count > 0 {
		fmt.Fprintf(
			w,
			"\nFirst byte mean +/- std. dev.: %.2f +/- %.2f ms\n",
			1000*stats.Headers.Mean(),
			1000*stats.Headers.StdDev(),
		)
		fmt.Fprintf(
			w,
			"First byte quantiles: (%.2f, %.2f, %.2f) ms\n",
			1000*stats.Headers.Quantiles[0.05],
			1000*stats.Headers.Quantiles[0.5],
			1000*stats.Headers.Quantiles[0.95],
		)
		if stats.Body.Count > 0 {
			fmt.Fprintf(
				w,
				"\nFull response mean +/- std. dev.: %.2f +/- %.2f ms\n",
				1000*stats.Body.Mean(),
				1000*stats.Body.StdDev(),
			)
			fmt.Fprintf(
				w,
				"Full response quantiles: (%.2f, %.2f, %.2f) ms\n",
				1000*stats.Body.Quantiles[0.05],
				1000*stats.Body.Quantiles[0.5],
				1000*stats.Body.Quantiles[0.95],
			)
		}
	}
}

func (hammer *Hammer) ReportPrinter(format string) func(StatsSummary) {
	return func(stats StatsSummary) {
		w, err := os.Create(fmt.Sprintf(format, stats.Name))
		if err != nil {
			hammer.warn(err.Error())
			return
		}
		fmt.Fprintf(w, "HAMMER REPORT FOR %s\n\n", stats.Name)
		stats.PrintReport(w)
		w.Close()
	}
}

func (hammer *Hammer) StatsPrinter(filename string) func(StatsSummary) {
	return func(stats StatsSummary) {
		statsFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			hammer.warn(err.Error())
			return
		}
		runTime := stats.End.Sub(stats.Begin).Seconds()
		count := stats.Headers.Count
		fmt.Fprintf(
			statsFile,
			"%s\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f",
			stats.Name,
			hammer.Threads,
			hammer.QPS,
			runTime,
			count,
			count/runTime,
			1000*stats.Headers.Mean(),
			1000*stats.Headers.StdDev(),
			1000*stats.Headers.Quantiles[0.05],
			1000*stats.Headers.Quantiles[0.5],
			1000*stats.Headers.Quantiles[0.95],
		)
		if stats.Body.Count > 0 {
			fmt.Fprintf(
				statsFile,
				"%f\t%f\t%f\t%f\t%f\t%.0f\t%.0f\n",
				1000*stats.Body.Mean(),
				1000*stats.Body.StdDev(),
				1000*stats.Body.Quantiles[0.05],
				1000*stats.Body.Quantiles[0.5],
				1000*stats.Body.Quantiles[0.95],
				stats.Bytes,
				stats.Bytes/runTime,
			)
		} else {
			fmt.Fprintf(statsFile, "\n")
		}
		statsFile.Close()
	}
}

func (hammer *Hammer) Run(statschan chan StatsSummary) {
	hammer.Exit = make(chan int)
	hammer.Requests = make(chan Request)
	hammer.throttled = make(chan Request, hammer.Backlog)
	hammer.results = make(chan Result, hammer.Threads*2)
	hammer.stats = statschan

	for i := 0; i < hammer.Threads; i++ {
		hammer.requestWorkers.Add(1)
		go hammer.sendRequests()
	}
	hammer.finishedResults.Add(1)
	go hammer.collectResults()
	go hammer.throttle()
	go hammer.GenerateFunction(hammer)
	go func() {
		hammer.requestWorkers.Wait()
		close(hammer.results)
	}()

	// Give it time to run...
	time.Sleep(time.Duration(hammer.RunFor * float64(time.Second)))
	// And then signal GenerateRequests to stop.
	close(hammer.Exit)
	hammer.finishedResults.Wait()
}
<commit_msg>Allow a Backlog of zero to mean the number of threads<commit_after>package hammer

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/arodland/go-descriptivestats/descriptivestats"
	"github.com/bmizerany/perks/quantile"
)

type Hammer struct {
	RunFor           float64
	Threads          int
	Backlog          int
	QPS              float64
	LogErrors        bool
	GenerateFunction RequestGenerator
	Exit             chan int
	Requests         chan Request
	throttled        chan Request
	results          chan Result
	stats            chan StatsSummary
	requestWorkers   sync.WaitGroup
	finishedResults  sync.WaitGroup
}

func (hammer *Hammer) warn(msg string) {
	log.Println(msg)
}

func (hammer *Hammer) warnf(fmt string, args ...interface{}) {
	log.Printf(fmt, args...)
}

type BodyBehavior int

const (
	IgnoreBody BodyBehavior = iota
	DiscardBody
	CopyBody
)

type Request struct {
	HTTPRequest  *http.Request
	Name         string
	BodyBehavior BodyBehavior
	Callback     RequestCallback
}

type RequestCallback func(Request, *http.Response, Result)

type RequestGenerator func(*Hammer)

type RandomURLGeneratorOptions struct {
	URLs         []string
	Headers      map[string][]string
	Name         string
	BodyBehavior BodyBehavior
	Callback     RequestCallback
}

func RandomURLGenerator(opts RandomURLGeneratorOptions) RequestGenerator {
	readiedRequests := make([]Request, len(opts.URLs))
	for i, url := range opts.URLs {
		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			panic(err)
		}
		req.Header = opts.Headers
		readiedRequests[i] = Request{
			BodyBehavior: opts.BodyBehavior,
			HTTPRequest:  req,
			Name:         opts.Name,
			Callback:     opts.Callback,
		}
	}
	num := len(readiedRequests)

	return func(hammer *Hammer) {
		defer func() { close(hammer.Requests) }()

		for {
			select {
			case <-hammer.Exit:
				return
			default:
				var idx int
				if num == 1 {
					idx = 0
				} else {
					idx = rand.Intn(len(readiedRequests))
				}
				hammer.Requests <- readiedRequests[idx]
			}
		}
	}
}

func (hammer *Hammer) SendRequest(req Request) {
	hammer.Requests <- req
}

func (hammer *Hammer) SendRequestImmediately(req Request) {
	hammer.throttled <- req
}

func (hammer *Hammer) throttle() {
	ticker := time.NewTicker(time.Duration(float64(time.Second) / hammer.QPS))
	defer ticker.Stop()
	defer close(hammer.throttled)

	for {
		select {
		case <-ticker.C:
			req, ok := <-hammer.Requests
			if !ok {
				return
			}
			hammer.throttled <- req
		}
	}
}

type Result struct {
	Name       string
	Status     int
	Start      time.Time
	GotHeaders time.Time
	GotBody    time.Time
	BodySize   int64
}

func copyResponseBody(res *http.Response) (gotBody time.Time, size int64) {
	var buf bytes.Buffer
	size, _ = io.Copy(&buf, res.Body)
	gotBody = time.Now()
	res.Body = ioutil.NopCloser(bytes.NewReader(buf.Bytes()))
	return
}

func (hammer *Hammer) sendRequests() {
	defer hammer.requestWorkers.Done()

	client := &http.Client{}

	for req := range hammer.throttled {
		var result Result
		result.Name = req.Name
		result.Start = time.Now()
		res, err := client.Do(req.HTTPRequest)
		result.GotHeaders = time.Now()
		if err != nil {
			result.Status = 499
			hammer.warn(err.Error())
		} else {
			result.Status = res.StatusCode
			if result.Status >= 400 {
				if req.BodyBehavior == CopyBody {
					result.GotBody, result.BodySize = copyResponseBody(res)
				}
				if hammer.LogErrors {
					// TODO: refactor this into a method
					logOut, err := ioutil.TempFile(".", "error.log.")
					if err == nil {
						res.Write(logOut)
						result.GotBody = time.Now()
					} else {
						hammer.warnf("%s writing error log\n", err.Error())
					}
				} else if req.BodyBehavior == DiscardBody {
					result.BodySize, _ = io.Copy(ioutil.Discard, res.Body)
					result.GotBody = time.Now()
				}
				hammer.warnf("Got status %s for %s\n", res.Status, req.HTTPRequest.URL.String())
			} else if req.BodyBehavior == CopyBody {
				result.GotBody, result.BodySize = copyResponseBody(res)
			} else if req.BodyBehavior == DiscardBody {
				result.BodySize, _ = io.Copy(ioutil.Discard, res.Body)
				result.GotBody = time.Now()
			} else {
				res.Body.Close()
			}
		}
		if req.Callback != nil {
			go req.Callback(req, res, result)
		}
		hammer.results <- result
	}
}

type Stats struct {
	Name           string
	Quantiles      []float64
	Begin          time.Time
	End            time.Time
	Statuses       map[int]int
	HeaderStats    descriptivestats.Stats
	HeaderQuantile quantile.Stream
	BodyStats      descriptivestats.Stats
	BodyQuantile   quantile.Stream
	Bytes          float64
}

type SingleStatSummary struct {
	descriptivestats.Stats
	Quantiles map[float64]float64
}

type StatsSummary struct {
	Name     string
	Begin    time.Time
	End      time.Time
	Statuses map[int]int
	Headers  SingleStatSummary
	Body     SingleStatSummary
	Bytes    float64
}

func newStats(name string, quantiles ...float64) *Stats {
	stats := Stats{
		Name:           name,
		Quantiles:      quantiles,
		Statuses:       make(map[int]int),
		HeaderStats:    descriptivestats.Stats{},
		HeaderQuantile: *(quantile.NewTargeted(quantiles...)),
		BodyStats:      descriptivestats.Stats{},
		BodyQuantile:   *(quantile.NewTargeted(quantiles...)),
	}
	stats.HeaderQuantile.SetEpsilon(0.001)
	stats.BodyQuantile.SetEpsilon(0.001)
	return &stats
}

func (stats *Stats) Summarize() (summary StatsSummary) {
	summary.Name = stats.Name
	summary.Begin = stats.Begin
	summary.End = stats.End
	summary.Statuses = stats.Statuses
	summary.Bytes = stats.Bytes
	summary.Headers.Stats = stats.HeaderStats
	summary.Headers.Quantiles = make(map[float64]float64, len(stats.Quantiles))
	for _, quantile := range stats.Quantiles {
		summary.Headers.Quantiles[quantile] = stats.HeaderQuantile.Query(quantile)
	}
	summary.Body.Stats = stats.BodyStats
	if stats.BodyStats.Count > 0 {
		summary.Body.Quantiles = make(map[float64]float64, len(stats.Quantiles))
		for _, quantile := range stats.Quantiles {
			summary.Body.Quantiles[quantile] = stats.BodyQuantile.Query(quantile)
		}
	}
	return
}

func (hammer *Hammer) collectResults() {
	defer hammer.finishedResults.Done()

	statsMap := map[string]*Stats{}

	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	defer func() {
		for _, stats := range statsMap {
			hammer.stats <- stats.Summarize()
		}
		close(hammer.stats)
	}()

	for {
		select {
		case res, ok := <-hammer.results:
			if !ok {
				return
			}

			stats, statsExisted := statsMap[res.Name]
			if !statsExisted {
				stats = newStats(res.Name, 0.05, 0.5, 0.95)
				statsMap[res.Name] = stats
			}

			stats.Statuses[res.Status]++
			stats.Bytes += float64(res.BodySize)

			start := res.Start
			end := res.GotHeaders
			dur := end.Sub(start).Seconds()
			stats.HeaderStats.Add(dur)
			stats.HeaderQuantile.Insert(dur)
			if res.GotBody != (time.Time{}) {
				end = res.GotBody
				dur := end.Sub(start).Seconds()
				stats.BodyStats.Add(dur)
				stats.BodyQuantile.Insert(dur)
			}
			if !statsExisted {
				stats.Begin = start
				stats.End = end
			} else {
				if start.Before(stats.Begin) {
					stats.Begin = start
				}
				if start.After(stats.End) {
					stats.End = start
				}
			}
		case <-ticker.C:
			for _, stats := range statsMap {
				hammer.stats <- stats.Summarize()
			}
		}
	}
}

func (stats *StatsSummary) PrintReport(w io.Writer) {
	runTime := stats.End.Sub(stats.Begin).Seconds()
	count := stats.Headers.Count
	fmt.Fprintf(
		w,
		`Run time: %.3f
Total hits: %.0f
Hits/sec: %.3f
`,
		runTime,
		count,
		count/runTime,
	)
	if stats.Body.Count > 0 {
		fmt.Fprintf(
			w,
			"Total bytes: %0.f\nBytes/sec: %.0f\n",
			stats.Bytes,
			stats.Bytes/runTime,
		)
	}

	fmt.Fprintf(w, "\nStatus totals:\n")
	statusCodes := []int{}
	for code, _ := range stats.Statuses {
		statusCodes = append(statusCodes, code)
	}
	sort.Ints(statusCodes)
	for _, code := range statusCodes {
		fmt.Fprintf(w, "%d\t%d\t%.3f\n", code, stats.Statuses[code], 100*float64(stats.Statuses[code])/count)
	}
	if count > 0 {
		fmt.Fprintf(
			w,
			"\nFirst byte mean +/- std. dev.: %.2f +/- %.2f ms\n",
			1000*stats.Headers.Mean(),
			1000*stats.Headers.StdDev(),
		)
		fmt.Fprintf(
			w,
			"First byte quantiles: (%.2f, %.2f, %.2f) ms\n",
			1000*stats.Headers.Quantiles[0.05],
			1000*stats.Headers.Quantiles[0.5],
			1000*stats.Headers.Quantiles[0.95],
		)
		if stats.Body.Count > 0 {
			fmt.Fprintf(
				w,
				"\nFull response mean +/- std. dev.: %.2f +/- %.2f ms\n",
				1000*stats.Body.Mean(),
				1000*stats.Body.StdDev(),
			)
			fmt.Fprintf(
				w,
				"Full response quantiles: (%.2f, %.2f, %.2f) ms\n",
				1000*stats.Body.Quantiles[0.05],
				1000*stats.Body.Quantiles[0.5],
				1000*stats.Body.Quantiles[0.95],
			)
		}
	}
}

func (hammer *Hammer) ReportPrinter(format string) func(StatsSummary) {
	return func(stats StatsSummary) {
		w, err := os.Create(fmt.Sprintf(format, stats.Name))
		if err != nil {
			hammer.warn(err.Error())
			return
		}
		fmt.Fprintf(w, "HAMMER REPORT FOR %s\n\n", stats.Name)
		stats.PrintReport(w)
		w.Close()
	}
}

func (hammer *Hammer) StatsPrinter(filename string) func(StatsSummary) {
	return func(stats StatsSummary) {
		statsFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			hammer.warn(err.Error())
			return
		}
		runTime := stats.End.Sub(stats.Begin).Seconds()
		count := stats.Headers.Count
		fmt.Fprintf(
			statsFile,
			"%s\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f",
			stats.Name,
			hammer.Threads,
			hammer.QPS,
			runTime,
			count,
			count/runTime,
			1000*stats.Headers.Mean(),
			1000*stats.Headers.StdDev(),
			1000*stats.Headers.Quantiles[0.05],
			1000*stats.Headers.Quantiles[0.5],
			1000*stats.Headers.Quantiles[0.95],
		)
		if stats.Body.Count > 0 {
			fmt.Fprintf(
				statsFile,
				"%f\t%f\t%f\t%f\t%f\t%.0f\t%.0f\n",
				1000*stats.Body.Mean(),
				1000*stats.Body.StdDev(),
				1000*stats.Body.Quantiles[0.05],
				1000*stats.Body.Quantiles[0.5],
				1000*stats.Body.Quantiles[0.95],
				stats.Bytes,
				stats.Bytes/runTime,
			)
		} else {
			fmt.Fprintf(statsFile, "\n")
		}
		statsFile.Close()
	}
}

func (hammer *Hammer) Run(statschan chan StatsSummary) {
	if hammer.Backlog == 0 {
		hammer.Backlog = hammer.Threads
	}

	hammer.Exit = make(chan int)
	hammer.Requests = make(chan Request)
	hammer.throttled = make(chan Request, hammer.Backlog)
	hammer.results = make(chan Result, hammer.Threads*2)
	hammer.stats = statschan

	for i := 0; i < hammer.Threads; i++ {
		hammer.requestWorkers.Add(1)
		go hammer.sendRequests()
	}
	hammer.finishedResults.Add(1)
	go hammer.collectResults()
	go hammer.throttle()
	go hammer.GenerateFunction(hammer)
	go func() {
		hammer.requestWorkers.Wait()
		close(hammer.results)
	}()

	// Give it time to run...
	time.Sleep(time.Duration(hammer.RunFor * float64(time.Second)))
	// And then signal GenerateRequests to stop.
	close(hammer.Exit)
	hammer.finishedResults.Wait()
}
<|endoftext|>


<commit_before>package p124

import "fmt"

/**
Given a binary tree, find the maximum path sum.

For this problem, a path is defined as any sequence of nodes from some starting node to
 any node in the tree along the parent-child connections.
 The path must contain at least one node and does not need to go through the root.

For example:
Given the below binary tree,

       1
      / \
     2   3
Return 6.
 */

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func maxPathSum(root *TreeNode) int {
	maxPath := 0
	if root != nil {
		maxPath = root.Val
	}
	max := func(a, b int) int {
		if a > b {
			return a
		}
		return b
	}

	var maxTraversal func(node *TreeNode) int
	maxTraversal = func(node *TreeNode) int {
		if node == nil {
			return 0
		}
		left := maxTraversal(node.Left)
		right := maxTraversal(node.Right)

		singleHand := node.Val + max(left, right)
		fmt.Println(node.Val, left, right, singleHand, maxPath)
		if left+right+node.Val > maxPath {
			maxPath = left + right + node.Val
		}
		if singleHand > maxPath {
			maxPath = singleHand
		}
		if node.Val > maxPath {
			maxPath = node.Val
		}
		return max(singleHand, node.Val)
	}
	maxTraversal(root)
	return maxPath
}
<commit_msg>109 hits,  p124<commit_after>package p124

/**
Given a binary tree, find the maximum path sum.

For this problem, a path is defined as any sequence of nodes from some starting node to
 any node in the tree along the parent-child connections.
 The path must contain at least one node and does not need to go through the root.

For example:
Given the below binary tree,

       1
      / \
     2   3
Return 6.
 */

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func maxPathSum(root *TreeNode) int {
	maxPath := 0
	if root != nil {
		maxPath = root.Val
	}
	max := func(a, b int) int {
		if a > b {
			return a
		}
		return b
	}

	var maxTraversal func(node *TreeNode) int
	maxTraversal = func(node *TreeNode) int {
		if node == nil {
			return 0
		}
		left := maxTraversal(node.Left)
		right := maxTraversal(node.Right)

		singleHand := node.Val + max(left, right)
		if left+right+node.Val > maxPath {
			maxPath = left + right + node.Val
		}
		if singleHand > maxPath {
			maxPath = singleHand
		}
		if node.Val > maxPath {
			maxPath = node.Val
		}
		return max(singleHand, node.Val)
	}
	maxTraversal(root)
	return maxPath
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

//
// Loads a configuration form a given path
//
func loadConfig(confPath string) (*Config, error) {
	file, err := ioutil.ReadFile(confPath)
	if err != nil {
		return nil, err
	}

	rawConfig := new(struct {
		*Config
		Protocols map[ProtocolType]json.RawMessage `json:"protocols"`
	})
	err = json.Unmarshal(file, rawConfig)
	if err != nil {
		return nil, err
	}
	config := rawConfig.Config
	config.Protocols = make(map[ProtocolType]interface{})

	// Parse config protocols
	for k, v := range rawConfig.Protocols {
		switch k {
		case ProtocolTypeREST:
			protoConf := RestProtocol{}
			err := json.Unmarshal(v, &protoConf)
			if err != nil {
				return nil, errors.New("Invalid config of REST protocol")
			}
			config.Protocols[ProtocolTypeREST] = protoConf

		case ProtocolTypeMQTT:
			protoConf := MqttProtocol{}
			err := json.Unmarshal(v, &protoConf)
			if err != nil {
				return nil, errors.New("Invalid config of MQTT protocol")
			}
			config.Protocols[ProtocolTypeMQTT] = protoConf
		}
	}

	dir := filepath.Dir(confPath)
	devicesDir := filepath.Join(dir, "devices")
	if _, err = os.Stat(devicesDir); os.IsNotExist(err) {
		return nil, err
	}

	err = filepath.Walk(devicesDir, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() || !strings.HasSuffix(path, ".json") {
			return nil
		}
		if err != nil {
			return err
		}

		f, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		device := new(Device)
		err = json.Unmarshal(f, device)
		if err != nil {
			return err
		}
		config.Devices = append(config.Devices, *device)

		return nil
	})

	if err != nil {
		return nil, err
	}

	if err = config.Validate(); err != nil {
		return nil, err
	}
	return config, nil
}

//
// Main configuration container
//
type Config struct {
	Id         string                       `json:"id"`
	Name       string                       `json:"name"`
	PublicAddr string                       `json:"publicAddr"`
	StaticDir  string                       `json:"staticDir`
	Catalog    []Catalog                    `json:"catalog"`
	Http       HttpConfig                   `json:"http"`
	Protocols  map[ProtocolType]interface{} `json:"protocols"`
	Devices    []Device                     `json:"devices"`
}

// Validates the loaded configuration
func (self *Config) Validate() error {
	//if _, ok := self.Catalog[CatalogTypeLocal]; !ok {
	//	return fmt.Errorf("Catalog should contain local section")
	//}
	//TODO: add more validation rules here

	// Check if REST protocol is configured
	_, ok := self.Protocols[ProtocolTypeREST]
	if !ok {
		return errors.New("Invalid config: REST protocol has to be configured")
	}

	return nil
}

// Finds resource record by given resource id
func (self *Config) FindResource(resourceId string) (*Resource, bool) {
	for _, d := range self.Devices {
		for _, r := range d.Resources {
			if resourceId == d.ResourceId(r.Name) {
				return &r, true
			}
		}
	}
	return nil, false
}

//
// Catalog entry and types
//
type Catalog struct {
	Discover bool   `json:"discover"`
	Endpoint string `json:"endpoint"`
}

//
// Http config (for protocols using it)
//
type HttpConfig struct {
	BindAddr string `json:"bindAddr"`
	BindPort int    `json:"bindPort"`
}

//
// Protocol entry and types
//
type RestProtocol struct {
	Location string `json:"location"`
}

type MqttProtocol struct {
	Host   string `json:"host"`
	Port   int    `json:"port"`
	Prefix string `json:"prefix"`
}

type ProtocolType string

const (
	ProtocolTypeUnknown ProtocolType = ""
	ProtocolTypeREST    ProtocolType = "REST"
	ProtocolTypeMQTT    ProtocolType = "MQTT"
)

//
// Device information container (has one or many resources)
//
type Device struct {
	Name        string
	Description string
	Meta        map[string]interface{}
	Ttl         int
	Resources   []Resource
}

func (device *Device) ResourceId(name string) string {
	return fmt.Sprintf("%s/%s", device.Name, name)
}

//
// Resource information container (belongs to device)
//
type Resource struct {
	Name           string
	Meta           map[string]interface{}
	Representation map[string]interface{}
	Protocols      []SupportedProtocol
	Agent          Agent
}

//
// Protocol supported by resource and its supported content-types/methods
//
type SupportedProtocol struct {
	Type         ProtocolType
	Methods      []string
	ContentTypes []string `json:"content-types"`
}

//
// Description of how to run an agent that communicates with hardware
//
type Agent struct {
	Type     ExecType
	Interval time.Duration
	Dir      string
	Exec     string
}

type ExecType string

const (
	// Executes, outputs data, exits
	ExecTypeTask ExecType = "task"
	// Executes periodically (see Interval)
	ExecTypeTimer ExecType = "timer"
	// Constantly running and emitting output
	ExecTypeService ExecType = "service"
)
<commit_msg>Added validation of HTTP bindAddr/bindPort in DGW config. Fixes #23<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

//
// Loads a configuration form a given path
//
func loadConfig(confPath string) (*Config, error) {
	file, err := ioutil.ReadFile(confPath)
	if err != nil {
		return nil, err
	}

	rawConfig := new(struct {
		*Config
		Protocols map[ProtocolType]json.RawMessage `json:"protocols"`
	})
	err = json.Unmarshal(file, rawConfig)
	if err != nil {
		return nil, err
	}
	config := rawConfig.Config
	config.Protocols = make(map[ProtocolType]interface{})

	// Parse config protocols
	for k, v := range rawConfig.Protocols {
		switch k {
		case ProtocolTypeREST:
			protoConf := RestProtocol{}
			err := json.Unmarshal(v, &protoConf)
			if err != nil {
				return nil, errors.New("Invalid config of REST protocol")
			}
			config.Protocols[ProtocolTypeREST] = protoConf

		case ProtocolTypeMQTT:
			protoConf := MqttProtocol{}
			err := json.Unmarshal(v, &protoConf)
			if err != nil {
				return nil, errors.New("Invalid config of MQTT protocol")
			}
			config.Protocols[ProtocolTypeMQTT] = protoConf
		}
	}

	dir := filepath.Dir(confPath)
	devicesDir := filepath.Join(dir, "devices")
	if _, err = os.Stat(devicesDir); os.IsNotExist(err) {
		return nil, err
	}

	err = filepath.Walk(devicesDir, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() || !strings.HasSuffix(path, ".json") {
			return nil
		}
		if err != nil {
			return err
		}

		f, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		device := new(Device)
		err = json.Unmarshal(f, device)
		if err != nil {
			return err
		}
		config.Devices = append(config.Devices, *device)

		return nil
	})

	if err != nil {
		return nil, err
	}

	if err = config.Validate(); err != nil {
		return nil, err
	}
	return config, nil
}

//
// Main configuration container
//
type Config struct {
	Id         string                       `json:"id"`
	Name       string                       `json:"name"`
	PublicAddr string                       `json:"publicAddr"`
	StaticDir  string                       `json:"staticDir`
	Catalog    []Catalog                    `json:"catalog"`
	Http       HttpConfig                   `json:"http"`
	Protocols  map[ProtocolType]interface{} `json:"protocols"`
	Devices    []Device                     `json:"devices"`
}

// Validates the loaded configuration
func (self *Config) Validate() error {
	// Check if HTTP is configured
	if self.Http.BindAddr == "" || self.Http.BindPort == 0 {
		return errors.New("Invalid config: HTTP has to be properly configured")
	}

	// Check if REST protocol is configured
	_, ok := self.Protocols[ProtocolTypeREST]
	if !ok {
		return errors.New("Invalid config: REST protocol has to be configured")
	}

	return nil
}

// Finds resource record by given resource id
func (self *Config) FindResource(resourceId string) (*Resource, bool) {
	for _, d := range self.Devices {
		for _, r := range d.Resources {
			if resourceId == d.ResourceId(r.Name) {
				return &r, true
			}
		}
	}
	return nil, false
}

//
// Catalog entry and types
//
type Catalog struct {
	Discover bool   `json:"discover"`
	Endpoint string `json:"endpoint"`
}

//
// Http config (for protocols using it)
//
type HttpConfig struct {
	BindAddr string `json:"bindAddr"`
	BindPort int    `json:"bindPort"`
}

//
// Protocol entry and types
//
type RestProtocol struct {
	Location string `json:"location"`
}

type MqttProtocol struct {
	Host   string `json:"host"`
	Port   int    `json:"port"`
	Prefix string `json:"prefix"`
}

type ProtocolType string

const (
	ProtocolTypeUnknown ProtocolType = ""
	ProtocolTypeREST    ProtocolType = "REST"
	ProtocolTypeMQTT    ProtocolType = "MQTT"
)

//
// Device information container (has one or many resources)
//
type Device struct {
	Name        string
	Description string
	Meta        map[string]interface{}
	Ttl         int
	Resources   []Resource
}

func (device *Device) ResourceId(name string) string {
	return fmt.Sprintf("%s/%s", device.Name, name)
}

//
// Resource information container (belongs to device)
//
type Resource struct {
	Name           string
	Meta           map[string]interface{}
	Representation map[string]interface{}
	Protocols      []SupportedProtocol
	Agent          Agent
}

//
// Protocol supported by resource and its supported content-types/methods
//
type SupportedProtocol struct {
	Type         ProtocolType
	Methods      []string
	ContentTypes []string `json:"content-types"`
}

//
// Description of how to run an agent that communicates with hardware
//
type Agent struct {
	Type     ExecType
	Interval time.Duration
	Dir      string
	Exec     string
}

type ExecType string

const (
	// Executes, outputs data, exits
	ExecTypeTask ExecType = "task"
	// Executes periodically (see Interval)
	ExecTypeTimer ExecType = "timer"
	// Constantly running and emitting output
	ExecTypeService ExecType = "service"
)
<|endoftext|>


<commit_before>package view

import (
	"github.com/ungerik/go-start/debug"
)

const (
	RichTextAreaDefaultCols = 80
	RichTextAreaDefaultRows = 10
)

///////////////////////////////////////////////////////////////////////////////
// TextArea

type RichTextArea struct {
	ViewBaseWithId
	Text                 string
	Name                 string
	Cols                 int
	Rows                 int
	Readonly             bool
	Disabled             bool
	TabIndex             int
	Class                string
	Placeholder          string
	ToolbarStylesheetURL string
}

// We use https://github.com/xing/wysihtml5/ as open source rich text editor
//
func (self *RichTextArea) Render(ctx *Context) (err error) {

	if !Config.RichText.UseGlobalCSS {
		if self.ToolbarStylesheetURL != "" {
			debug.Print("Loading StylesheetURL")
			ctx.Response.RequireStyleURL(self.ToolbarStylesheetURL, 0)
		} else {
			debug.Print("Loading Config")
			ctx.Response.RequireStyleURL(Config.RichText.ToolbarCSS, 0)
		}
	}
	// ctx.Response.RequireStyleURL("http://yui.yahooapis.com/2.9.0/build/reset/reset-min.css", 0)

	// wysihtml5 parser rules
	ctx.Response.RequireScriptURL("/js/libs/wysihtml5-advanced.js", 0)
	// wysihtml5 Library
	ctx.Response.RequireScriptURL("/js/libs/wysihtml5-0.3.0.min.js", 1)

	ctx.Response.RequireScript(`var editor = new wysihtml5.Editor("`+self.id+`", { // id of textarea element
  toolbar:      "wysihtml5-editor-toolbar", // id of toolbar element
  parserRules:  wysihtml5ParserRules, // defined in parser rules set
  composerClassName:    "wysihtml5-editor",
  stylesheets: ["`+Config.RichText.EditorCSS+`"]
});`, 2)

	// ctx.Response.XML.Content(`<div id="wysihtml5-toolbar" style="display: none;">` + self.ToolbarHtml + `</div>`)

	ctx.Response.XML.Content(`<div id="wysihtml5-editor-toolbar" style="display: none;">` + Config.RichText.DefaultToolbar + `</div>`)
	// ctx.Response.XML.OpenTag("form")
	ctx.Response.XML.OpenTag("textarea")
	ctx.Response.XML.AttribIfNotDefault("id", self.id)
	ctx.Response.XML.AttribIfNotDefault("class", ""+self.Class)

	cols := self.Cols
	if cols == 0 {
		cols = RichTextAreaDefaultCols
	}
	rows := self.Rows
	if rows == 0 {
		rows = RichTextAreaDefaultRows
	}

	ctx.Response.XML.Attrib("name", self.Name)
	ctx.Response.XML.Attrib("rows", rows)
	ctx.Response.XML.Attrib("cols", cols)
	ctx.Response.XML.AttribIfNotDefault("tabindex", self.TabIndex)
	if self.Readonly {
		ctx.Response.XML.Attrib("readonly", "readonly")
	}
	if self.Disabled {
		ctx.Response.XML.Attrib("disabled", "disabled")
	}
	ctx.Response.XML.AttribIfNotDefault("placeholder", self.Placeholder)

	ctx.Response.XML.Attrib("autofocus", "autofocus")

	ctx.Response.XML.EscapeContent(self.Text)

	ctx.Response.XML.ForceCloseTag()
	// ctx.Response.XML.ForceCloseTag()
	return nil
}

func SetDefaultToolbar() string {
	return `
  bold
  italic

  red
  green
  blue

  insert link

      Link:
      <input data-wysihtml5-dialog-field="href" value="http://" class="text">

    OK Cancel

`
}

//func (self *RichTextArea) SetText(text string) {
//	self.Text = text
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetName(name string) {
//	self.Name = name
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetCols(cols int) {
//	self.Cols = cols
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetRows(rows int) {
//	self.Rows = rows
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetReadonly(readonly bool) {
//	self.Readonly = readonly
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetTabIndex(tabindex int) {
//	self.TabIndex = tabindex
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetClass(class string) {
//	self.Class = class
//	ViewChanged(self)
//}
<commit_msg>added class to rich textarea<commit_after>package view

import (
	"github.com/ungerik/go-start/debug"
)

const (
	RichTextAreaDefaultCols = 80
	RichTextAreaDefaultRows = 10
)

///////////////////////////////////////////////////////////////////////////////
// TextArea

type RichTextArea struct {
	ViewBaseWithId
	Text                 string
	Name                 string
	Cols                 int
	Rows                 int
	Readonly             bool
	Disabled             bool
	TabIndex             int
	Class                string
	Placeholder          string
	ToolbarStylesheetURL string
}

// We use https://github.com/xing/wysihtml5/ as open source rich text editor
//
func (self *RichTextArea) Render(ctx *Context) (err error) {

	if !Config.RichText.UseGlobalCSS {
		if self.ToolbarStylesheetURL != "" {
			debug.Print("Loading StylesheetURL")
			ctx.Response.RequireStyleURL(self.ToolbarStylesheetURL, 0)
		} else {
			debug.Print("Loading Config")
			ctx.Response.RequireStyleURL(Config.RichText.ToolbarCSS, 0)
		}
	}
	// ctx.Response.RequireStyleURL("http://yui.yahooapis.com/2.9.0/build/reset/reset-min.css", 0)

	// wysihtml5 parser rules
	ctx.Response.RequireScriptURL("/js/libs/wysihtml5-advanced.js", 0)
	// wysihtml5 Library
	ctx.Response.RequireScriptURL("/js/libs/wysihtml5-0.3.0.min.js", 1)

	ctx.Response.RequireScript(`var editor = new wysihtml5.Editor("`+self.id+`", { // id of textarea element
  toolbar:      "wysihtml5-editor-toolbar", // id of toolbar element
  parserRules:  wysihtml5ParserRules, // defined in parser rules set
  composerClassName:    "wysihtml5-editor",
  stylesheets: ["`+Config.RichText.EditorCSS+`"]
});`, 2)

	// ctx.Response.XML.Content(`<div id="wysihtml5-toolbar" style="display: none;">` + self.ToolbarHtml + `</div>`)

	ctx.Response.XML.Content(`<div id="wysihtml5-editor-toolbar" style="display: none;">` + Config.RichText.DefaultToolbar + `</div>`)
	// ctx.Response.XML.OpenTag("form")
	ctx.Response.XML.OpenTag("textarea")
	ctx.Response.XML.AttribIfNotDefault("id", self.id)

	ctx.Response.XML.Attrib("class", "wysihtml5 "+self.Class)

	cols := self.Cols
	if cols == 0 {
		cols = RichTextAreaDefaultCols
	}
	rows := self.Rows
	if rows == 0 {
		rows = RichTextAreaDefaultRows
	}

	ctx.Response.XML.Attrib("name", self.Name)
	ctx.Response.XML.Attrib("rows", rows)
	ctx.Response.XML.Attrib("cols", cols)
	ctx.Response.XML.AttribIfNotDefault("tabindex", self.TabIndex)
	if self.Readonly {
		ctx.Response.XML.Attrib("readonly", "readonly")
	}
	if self.Disabled {
		ctx.Response.XML.Attrib("disabled", "disabled")
	}
	ctx.Response.XML.AttribIfNotDefault("placeholder", self.Placeholder)

	ctx.Response.XML.Attrib("autofocus", "autofocus")

	ctx.Response.XML.EscapeContent(self.Text)

	ctx.Response.XML.ForceCloseTag()
	// ctx.Response.XML.ForceCloseTag()
	return nil
}

func SetDefaultToolbar() string {
	return `
  bold
  italic

  red
  green
  blue

  insert link

      Link:
      <input data-wysihtml5-dialog-field="href" value="http://" class="text">

    OK Cancel

`
}

//func (self *RichTextArea) SetText(text string) {
//	self.Text = text
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetName(name string) {
//	self.Name = name
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetCols(cols int) {
//	self.Cols = cols
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetRows(rows int) {
//	self.Rows = rows
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetReadonly(readonly bool) {
//	self.Readonly = readonly
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetTabIndex(tabindex int) {
//	self.TabIndex = tabindex
//	ViewChanged(self)
//}
//
//func (self *RichTextArea) SetClass(class string) {
//	self.Class = class
//	ViewChanged(self)
//}
<|endoftext|>