<commit_before>package config

import (
	"fmt"
	"sort"
)

// Device represents a LXD container device
type Device map[string]string

// Clone returns a copy of the Device.
func (device Device) Clone() Device {
	copy := map[string]string{}

	for k, v := range device {
		copy[k] = v
	}

	return copy
}

// Validate accepts a map of field/validation functions to run against the device's config.
func (device Device) Validate(rules map[string]func(value string) error) error {
	checkedFields := map[string]struct{}{}

	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(device[k])
		if err != nil {
			return fmt.Errorf("Invalid value for device option %s: %v", k, err)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range device {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// Skip type fields are these are validated by the presence of an implementation.
		if k == "type" {
			continue
		}

		if k == "nictype" && (device["type"] == "nic" || device["type"] == "infiniband") {
			continue
		}

		return fmt.Errorf("Invalid device option: %s", k)
	}

	return nil
}

// Devices represents a set of LXD container devices
type Devices map[string]Device

// NewDevices creates a new Devices set from a native map[string]map[string]string set.
func NewDevices(nativeSet map[string]map[string]string) Devices {
	newDevices := Devices{}

	for devName, devConfig := range nativeSet {
		newDev := Device{}
		for k, v := range devConfig {
			newDev[k] = v
		}
		newDevices[devName] = newDev
	}

	return newDevices
}

// Contains checks if a given device exists in the set and if it's
// identical to that provided
func (list Devices) Contains(k string, d Device) bool {
	// If it didn't exist, it's different
	if list[k] == nil {
		return false
	}

	old := list[k]

	return deviceEquals(old, d)
}

// Update returns the difference between two sets
func (list Devices) Update(newlist Devices, updateFields func(Device, Device) []string) (map[string]Device, map[string]Device, map[string]Device, []string) {
	rmlist := map[string]Device{}
	addlist := map[string]Device{}
	updatelist := map[string]Device{}

	for key, d := range list {
		if !newlist.Contains(key, d) {
			rmlist[key] = d
		}
	}

	for key, d := range newlist {
		if !list.Contains(key, d) {
			addlist[key] = d
		}
	}

	updateDiff := []string{}
	for key, d := range addlist {
		srcOldDevice := rmlist[key]
		oldDevice := srcOldDevice.Clone()

		srcNewDevice := newlist[key]
		newDevice := srcNewDevice.Clone()

		updateDiff = deviceEqualsDiffKeys(oldDevice, newDevice)
		for _, k := range updateFields(oldDevice, newDevice) {
			delete(oldDevice, k)
			delete(newDevice, k)
		}

		if deviceEquals(oldDevice, newDevice) {
			delete(rmlist, key)
			delete(addlist, key)
			updatelist[key] = d
		}
	}

	return rmlist, addlist, updatelist, updateDiff
}

// DeviceNames returns the name of all devices in the set, sorted properly
func (list Devices) DeviceNames() []string {
	sortable := sortableDevices{}
	for k, d := range list {
		sortable = append(sortable, namedDevice{k, d})
	}

	sort.Sort(sortable)
	return sortable.Names()
}

// CloneNative returns a copy of the Devices set as a native map[string]map[string]string type.
func (list Devices) CloneNative() map[string]map[string]string {
	copy := map[string]map[string]string{}

	for deviceName, device := range list {
		copy[deviceName] = device.Clone()
	}

	return copy
}
<commit_msg>device/config/devices: Removes DeviceNames() and replaces with Sorted() and Reversed()<commit_after>package config

import (
	"fmt"
	"sort"
)

// Device represents a LXD container device
type Device map[string]string

// Clone returns a copy of the Device.
func (device Device) Clone() Device {
	copy := map[string]string{}

	for k, v := range device {
		copy[k] = v
	}

	return copy
}

// Validate accepts a map of field/validation functions to run against the device's config.
func (device Device) Validate(rules map[string]func(value string) error) error {
	checkedFields := map[string]struct{}{}

	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(device[k])
		if err != nil {
			return fmt.Errorf("Invalid value for device option %s: %v", k, err)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range device {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// Skip type fields are these are validated by the presence of an implementation.
		if k == "type" {
			continue
		}

		if k == "nictype" && (device["type"] == "nic" || device["type"] == "infiniband") {
			continue
		}

		return fmt.Errorf("Invalid device option: %s", k)
	}

	return nil
}

// Devices represents a set of LXD container devices
type Devices map[string]Device

// NewDevices creates a new Devices set from a native map[string]map[string]string set.
func NewDevices(nativeSet map[string]map[string]string) Devices {
	newDevices := Devices{}

	for devName, devConfig := range nativeSet {
		newDev := Device{}
		for k, v := range devConfig {
			newDev[k] = v
		}
		newDevices[devName] = newDev
	}

	return newDevices
}

// Contains checks if a given device exists in the set and if it's
// identical to that provided
func (list Devices) Contains(k string, d Device) bool {
	// If it didn't exist, it's different
	if list[k] == nil {
		return false
	}

	old := list[k]

	return deviceEquals(old, d)
}

// Update returns the difference between two sets
func (list Devices) Update(newlist Devices, updateFields func(Device, Device) []string) (map[string]Device, map[string]Device, map[string]Device, []string) {
	rmlist := map[string]Device{}
	addlist := map[string]Device{}
	updatelist := map[string]Device{}

	for key, d := range list {
		if !newlist.Contains(key, d) {
			rmlist[key] = d
		}
	}

	for key, d := range newlist {
		if !list.Contains(key, d) {
			addlist[key] = d
		}
	}

	updateDiff := []string{}
	for key, d := range addlist {
		srcOldDevice := rmlist[key]
		oldDevice := srcOldDevice.Clone()

		srcNewDevice := newlist[key]
		newDevice := srcNewDevice.Clone()

		updateDiff = deviceEqualsDiffKeys(oldDevice, newDevice)
		for _, k := range updateFields(oldDevice, newDevice) {
			delete(oldDevice, k)
			delete(newDevice, k)
		}

		if deviceEquals(oldDevice, newDevice) {
			delete(rmlist, key)
			delete(addlist, key)
			updatelist[key] = d
		}
	}

	return rmlist, addlist, updatelist, updateDiff
}

// Clone returns a copy of the Devices set.
func (list Devices) Clone() Devices {
	copy := Devices{}

	for deviceName, device := range list {
		copy[deviceName] = device.Clone()
	}

	return copy
}

// CloneNative returns a copy of the Devices set as a native map[string]map[string]string type.
func (list Devices) CloneNative() map[string]map[string]string {
	copy := map[string]map[string]string{}

	for deviceName, device := range list {
		copy[deviceName] = device.Clone()
	}

	return copy
}

// Sorted returns the name of all devices in the set, sorted properly.
func (list Devices) Sorted() DevicesSortable {
	sortable := DevicesSortable{}
	for k, d := range list {
		sortable = append(sortable, DeviceNamed{k, d})
	}

	sort.Sort(sortable)
	return sortable
}

// Reversed returns the name of all devices in the set, sorted reversed.
func (list Devices) Reversed() DevicesSortable {
	sortable := DevicesSortable{}
	for k, d := range list {
		sortable = append(sortable, DeviceNamed{k, d})
	}

	sort.Sort(sort.Reverse(sortable))
	return sortable
}
<|endoftext|>


<commit_before>package editor

import (
	"bytes"
	"context"
	"testing"
)

func TestCommandline(t *testing.T) {
	command := append([]byte("quit"), CharCtrlM)
	ed := commandline{
		streamSet: streamSet{in: NewReaderContext(context.TODO(), bytes.NewReader(command))},
		editor:    newEditor(),
		basic:     &basic{},
	}
	var (
		end continuity
		err error
	)
	for range command {
		end, _, err = ed.Run()
		if err != nil {
			t.Errorf("commandline: %v", err)
		}
	}
	if end != exit {
		t.Errorf("commandline (%q): want %v, but got %v", command, exit, end)
	}
}

func TestSubstituteNoArgs(t *testing.T) {
	command := append([]byte("substitute"), CharCtrlM)
	ed := commandline{
		streamSet: streamSet{in: NewReaderContext(context.TODO(), bytes.NewReader(command))},
		editor:    newEditor(),
		basic:     &basic{},
	}
	ed.buf = []rune("a")
	for range command {
		_, _, err := ed.Run()
		if err != nil {
			t.Errorf("commandline: %v", err)
		}
	}
	got := string(ed.buf)
	want := "a"
	if got != want {
		t.Errorf("commandline (%q): want %v, but got %v", command, got, want)
	}
}

func TestSubstitute(t *testing.T) {
	command := append([]byte("substitute a b"), CharCtrlM)
	ed := commandline{
		streamSet: streamSet{in: NewReaderContext(context.TODO(), bytes.NewReader(command))},
		editor:    newEditor(),
		basic:     &basic{},
	}
	ed.buf = []rune("a")
	for range command {
		_, _, err := ed.Run()
		if err != nil {
			t.Errorf("commandline: %v", err)
		}
	}
	got := string(ed.buf)
	want := "b"
	if got != want {
		t.Errorf("commandline (%q): want %v, but got %v", command, got, want)
	}
}

func TestEmpty(t *testing.T) {
	command := append([]byte(""), CharCtrlM)
	ed := commandline{
		streamSet: streamSet{in: NewReaderContext(context.TODO(), bytes.NewReader(command))},
		editor:    newEditor(),
		basic:     &basic{},
	}
	for range command {
		_, _, err := ed.Run()
		if err != nil {
			t.Errorf("commandline: %v", err)
		}
	}
}
<commit_msg>Use table for test<commit_after>package editor

import (
	"bytes"
	"context"
	"testing"
)

func TestCommandline(t *testing.T) {
	command := append([]byte("quit"), CharCtrlM)
	ed := commandline{
		streamSet: streamSet{in: NewReaderContext(context.TODO(), bytes.NewReader(command))},
		editor:    newEditor(),
		basic:     &basic{},
	}
	var (
		end continuity
		err error
	)
	for range command {
		end, _, err = ed.Run()
		if err != nil {
			t.Errorf("commandline: %v", err)
		}
	}
	if end != exit {
		t.Errorf("commandline (%q): want %v, but got %v", command, exit, end)
	}
}

func TestSubstituteNoArgs(t *testing.T) {
	command := append([]byte("substitute"), CharCtrlM)
	ed := commandline{
		streamSet: streamSet{in: NewReaderContext(context.TODO(), bytes.NewReader(command))},
		editor:    newEditor(),
		basic:     &basic{},
	}
	ed.buf = []rune("a")
	for range command {
		_, _, err := ed.Run()
		if err != nil {
			t.Errorf("commandline: %v", err)
		}
	}
	got := string(ed.buf)
	want := "a"
	if got != want {
		t.Errorf("commandline (%q): want %v, but got %v", command, got, want)
	}
}

func TestSubstitute(t *testing.T) {
	command := append([]byte("substitute a b"), CharCtrlM)
	ed := commandline{
		streamSet: streamSet{in: NewReaderContext(context.TODO(), bytes.NewReader(command))},
		editor:    newEditor(),
		basic:     &basic{},
	}
	ed.buf = []rune("a")
	for range command {
		_, _, err := ed.Run()
		if err != nil {
			t.Errorf("commandline: %v", err)
		}
	}
	got := string(ed.buf)
	want := "b"
	if got != want {
		t.Errorf("commandline (%q): want %v, but got %v", command, got, want)
	}
}

func TestEmpty(t *testing.T) {
	tests := []struct {
		command []byte
	}{
		{command: append([]byte(""), CharCtrlM)},
	}
	for _, test := range tests {
		ed := commandline{
			streamSet: streamSet{in: NewReaderContext(context.TODO(), bytes.NewReader(test.command))},
			editor:    newEditor(),
			basic:     &basic{},
		}
		for range test.command {
			_, _, err := ed.Run()
			if err != nil {
				t.Errorf("commandline: %v", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package builder

import (
	"fmt"
	"strings"
	"testing"

	"github.com/gernest/ngorm/dialects/ql"
	"github.com/gernest/ngorm/fixture"
	"github.com/gernest/ngorm/search"
)

func TestGroup(t *testing.T) {
	e := fixture.TestEngine()
	s := GroupSQL(e)
	if s != "" {
		t.Errorf("expected an empty string got %s", s)
	}
	by := "location"
	search.Group(e, by)
	s = GroupSQL(e)
	expect := " GROUP BY " + by
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

}

func TestLimitAndOffsetSQL(t *testing.T) {
	e := fixture.TestEngine()
	e.Dialect = ql.Memory()
	limit := 2
	offset := 4
	search.Limit(e, limit)
	search.Offset(e, offset)
	expect := fmt.Sprintf(" LIMIT %d OFFSET %d", limit, offset)
	s := LimitAndOffsetSQL(e)
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

}

func TestPrepareQuerySQL(t *testing.T) {
	e := fixture.TestEngine()
	e.Dialect = ql.Memory()
	search.Limit(e, 1)
	search.Where(e, "name=?", "gernest")
	var user fixture.User
	s, err := PrepareQuerySQL(e, &user)
	if err != nil {
		//t.Error(err)
	}
	fmt.Println(s)
}

func TestWhere(t *testing.T) {
	e := fixture.TestEngine()
	e.Dialect = ql.Memory()

	// Where using Plain SQL
	search.Where(e, "name=?", "gernest")
	var user fixture.User
	s, err := Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect := "(name=$1)"
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// IN
	search.Where(e, "name in (?)", []string{"jinzhu", "jinzhu 2"})
	s, err = Where(e, &user, e.Search.WhereConditions[1])
	if err != nil {
		t.Fatal(err)
	}
	expect = "(name in ($2,$3))"
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// LIKE
	search.Where(e, "name LIKE ?", "%jin%")
	s, err = Where(e, &user, e.Search.WhereConditions[2])
	if err != nil {
		t.Fatal(err)
	}
	expect = "(name LIKE $4)"
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// AND
	search.Where(e, "name = ? AND age >= ?", "jinzhu", "22")
	s, err = Where(e, &user, e.Search.WhereConditions[3])
	if err != nil {
		t.Fatal(err)
	}
	expect = "(name = $5 AND age >= $6)"
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Where with  Map
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, map[string]interface{}{"name": "jinzhu", "age": 20})
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."name"`
	if !strings.Contains(s, expect) {
		t.Errorf("expected %s to containe %s", s, expect)
	}

	// Map when value is nil
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, map[string]interface{}{"name": "jinzhu", "age": nil})
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expected := `("users"."age" IS NULL)`
	if !strings.Contains(s, expected) {
		t.Errorf("expected %s to contain %s", s, expected)
	}

	// Primary Key
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, 10)
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."id" = $1)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	/// Slice of primary Keys
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, []int64{20, 21, 22})
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."id" IN ($1,$2,$3))`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Struct
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, &fixture.User{Name: "jinzhu", Age: 20})
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."age" = $1) AND ("users"."name" = $2)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}
}

func TestNot(t *testing.T) {
	e := fixture.TestEngine()
	e.Dialect = ql.Memory()

	search.Not(e, "name", "gernest")
	var user fixture.User
	s, err := Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect := `("users"."name" <> $1)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Not in
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, "name", []string{"jinzhu", "jinzhu 2"})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."name" NOT IN ($1,$2))`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Not in slice of primary keys
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, []int64{1, 2, 3})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}

	expect = `("users"."id" NOT IN ($1,$2,$3))`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Not in with empty slice
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, []int64{})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}

	expect = ``
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Struct
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, &fixture.Email{Email: "jinzhu"})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}

	expect = `("users"."email" <> $1)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Map when value is nil
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, map[string]interface{}{"name": "jinzhu", "age": nil})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expected := `("users"."age" IS NOT NULL)`
	if !strings.Contains(s, expected) {
		t.Errorf("expected %s to contain %s", s, expected)
	}

	// Primary Key
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, 10)
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."id" <> 10)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

}
<commit_msg>[builder] Clean up<commit_after>package builder

import (
	"fmt"
	"strings"
	"testing"

	"github.com/gernest/ngorm/dialects/ql"
	"github.com/gernest/ngorm/fixture"
	"github.com/gernest/ngorm/search"
)

func TestGroup(t *testing.T) {
	e := fixture.TestEngine()
	s := GroupSQL(e)
	if s != "" {
		t.Errorf("expected an empty string got %s", s)
	}
	by := "location"
	search.Group(e, by)
	s = GroupSQL(e)
	expect := " GROUP BY " + by
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

}

func TestLimitAndOffsetSQL(t *testing.T) {
	e := fixture.TestEngine()
	e.Dialect = ql.Memory()
	limit := 2
	offset := 4
	search.Limit(e, limit)
	search.Offset(e, offset)
	expect := fmt.Sprintf(" LIMIT %d OFFSET %d", limit, offset)
	s := LimitAndOffsetSQL(e)
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

}

func TestPrepareQuerySQL(t *testing.T) {
	e := fixture.TestEngine()
	e.Dialect = ql.Memory()
	search.Limit(e, 1)
	search.Where(e, "name=?", "gernest")
	var user fixture.User
	s, err := PrepareQuerySQL(e, &user)
	if err != nil {
		//t.Error(err)
	}
	fmt.Println(s)
}

func TestWhere(t *testing.T) {
	e := fixture.TestEngine()
	e.Dialect = ql.Memory()

	// Where using Plain SQL
	search.Where(e, "name=?", "gernest")
	var user fixture.User
	s, err := Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect := "(name=$1)"
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// IN
	search.Where(e, "name in (?)", []string{"gernest", "gernest 2"})
	s, err = Where(e, &user, e.Search.WhereConditions[1])
	if err != nil {
		t.Fatal(err)
	}
	expect = "(name in ($2,$3))"
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// LIKE
	search.Where(e, "name LIKE ?", "%jin%")
	s, err = Where(e, &user, e.Search.WhereConditions[2])
	if err != nil {
		t.Fatal(err)
	}
	expect = "(name LIKE $4)"
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// AND
	search.Where(e, "name = ? AND age >= ?", "gernest", "22")
	s, err = Where(e, &user, e.Search.WhereConditions[3])
	if err != nil {
		t.Fatal(err)
	}
	expect = "(name = $5 AND age >= $6)"
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Where with  Map
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, map[string]interface{}{"name": "gernest", "age": 20})
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."name"`
	if !strings.Contains(s, expect) {
		t.Errorf("expected %s to containe %s", s, expect)
	}

	// Map when value is nil
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, map[string]interface{}{"name": "gernest", "age": nil})
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expected := `("users"."age" IS NULL)`
	if !strings.Contains(s, expected) {
		t.Errorf("expected %s to contain %s", s, expected)
	}

	// Primary Key
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, 10)
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."id" = $1)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	/// Slice of primary Keys
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, []int64{20, 21, 22})
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."id" IN ($1,$2,$3))`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Struct
	e.Search.WhereConditions = nil
	e.Scope.SQLVars = nil
	search.Where(e, &fixture.User{Name: "gernest", Age: 20})
	s, err = Where(e, &user, e.Search.WhereConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."age" = $1) AND ("users"."name" = $2)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}
}

func TestNot(t *testing.T) {
	e := fixture.TestEngine()
	e.Dialect = ql.Memory()

	search.Not(e, "name", "gernest")
	var user fixture.User
	s, err := Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect := `("users"."name" <> $1)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Not in
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, "name", []string{"gernest", "gernest 2"})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."name" NOT IN ($1,$2))`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Not in slice of primary keys
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, []int64{1, 2, 3})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}

	expect = `("users"."id" NOT IN ($1,$2,$3))`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Not in with empty slice
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, []int64{})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}

	expect = ``
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Struct
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, &fixture.Email{Email: "gernest"})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}

	expect = `("users"."email" <> $1)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

	// Map when value is nil
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, map[string]interface{}{"name": "gernest", "age": nil})
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expected := `("users"."age" IS NOT NULL)`
	if !strings.Contains(s, expected) {
		t.Errorf("expected %s to contain %s", s, expected)
	}

	// Primary Key
	e.Search.NotConditions = nil
	e.Scope.SQLVars = nil
	search.Not(e, 10)
	s, err = Not(e, &user, e.Search.NotConditions[0])
	if err != nil {
		t.Fatal(err)
	}
	expect = `("users"."id" <> 10)`
	if s != expect {
		t.Errorf("expected %s got %s", expect, s)
	}

}
<|endoftext|>


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

package buildkite

import (
	"fmt"
	"time"
)

// BuildsService handles communication with the build related
// methods of the buildkite API.
//
// buildkite API docs: https://buildkite.com/docs/api/builds
type BuildsService struct {
	client *Client
}

// Author of a commit (used in CreateBuild)
type Author struct {
	Name  string `json:"name,omitempty"`
	Email string `json:"email,omitempty"`
}

// CreateBuild - Create a build.
type CreateBuild struct {
	Commit  string `json:"commit"`
	Branch  string `json:"branch"`
	Message string `json:"message"`

	// Optional fields
	Author                      Author            `json:"author,omitempty"`
	Env                         map[string]string `json:"env,omitempty"`
	MetaData                    map[string]string `json:"meta_data,omitempty"`
	IgnorePipelineBranchFilters bool              `json:"ignore_pipeline_branch_filters,omitempty"`
	PullRequestBaseBranch       string            `json:"pull_request_base_branch,omitempty"`
	PullRequestID               int64             `json:"pull_request_id,omitempty"`
	PullRequestRepository       string            `json:"pull_request_repository,omitempty"`
}

// Creator represents who created a build
type Creator struct {
	AvatarURL string     `json:"avatar_url"`
	CreatedAt *Timestamp `json:"created_at"`
	Email     string     `json:"email"`
	ID        string     `json:"id"`
	Name      string     `json:"name"`
}

// PullRequest represents a Github PR
type PullRequest struct {
	ID         *string `json:"id,omitempty"`
	Base       *string `json:"base,omitempty"`
	Repository *string `json:"repository,omitempty"`
}

// Build represents a build which has run in buildkite
type Build struct {
	ID          *string                `json:"id,omitempty"`
	URL         *string                `json:"url,omitempty"`
	WebURL      *string                `json:"web_url,omitempty"`
	Number      *int                   `json:"number,omitempty"`
	State       *string                `json:"state,omitempty"`
	Blocked     *bool                  `json:"blocked,omitempty"`
	Message     *string                `json:"message,omitempty"`
	Commit      *string                `json:"commit,omitempty"`
	Branch      *string                `json:"branch,omitempty"`
	Env         map[string]interface{} `json:"env,omitempty"`
	CreatedAt   *Timestamp             `json:"created_at,omitempty"`
	ScheduledAt *Timestamp             `json:"scheduled_at,omitempty"`
	StartedAt   *Timestamp             `json:"started_at,omitempty"`
	FinishedAt  *Timestamp             `json:"finished_at,omitempty"`
	MetaData    interface{}            `json:"meta_data,omitempty"`
	Creator     *Creator               `json:"creator,omitempty"`

	// jobs run during the build
	Jobs []*Job `json:"jobs,omitempty"`

	// the pipeline this build is associated with
	Pipeline *Pipeline `json:"pipeline,omitempty"`

	// the pull request this build is associated with
	PullRequest *PullRequest `json:"pull_request,omitempty"`
}

// BuildsListOptions specifies the optional parameters to the
// BuildsService.List method.
type BuildsListOptions struct {

	// Filters the results by the user who created the build
	Creator string `url:"creator,omitempty"`

	// Filters the results by builds created on or after the given time
	CreatedFrom time.Time `url:"created_from,omitempty"`

	// Filters the results by builds created before the given time
	CreatedTo time.Time `url:"created_to,omitempty"`

	// Filters the results by builds finished on or after the given time
	FinishedFrom time.Time `url:"finished_from,omitempty"`

	// State of builds to list.  Possible values are: running, scheduled, passed,
	// failed, canceled, skipped and not_run. Default is "".
	State []string `url:"state,brackets,omitempty"`

	// Branch filter by the name of the branch. Default is "".
	Branch string `url:"branch,omitempty"`

	ListOptions
}

// Cancel triggers a canel for the tagrget build
//
// buildkite API docs: https://buildkite.com/docs/apis/rest-api/builds#cancel-a-build
func (bs *BuildsService) Cancel(org, pipeline, build string) (*Build, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s/cancel", org, pipeline, build)
	req, err := bs.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	result := Build{}
	_, err = bs.client.Do(req, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}

// Create - Create a pipeline
//
// buildkite API docs: https://buildkite.com/docs/api/builds#create-a-build
func (bs *BuildsService) Create(org string, pipeline string, b *CreateBuild) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	req, err := bs.client.NewRequest("POST", u, b)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := bs.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// Get fetches a build.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#get-a-build
func (bs *BuildsService) Get(org string, pipeline string, id string) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s", org, pipeline, id)

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := bs.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// List the builds for the current user.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-all-builds
func (bs *BuildsService) List(opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/builds")

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByOrg lists the builds within the specified orginisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-an-organization
func (bs *BuildsService) ListByOrg(org string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/builds", org)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByPipeline lists the builds for a pipeline within the specified originisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-a-pipeline
func (bs *BuildsService) ListByPipeline(org string, pipeline string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// Rebuild triggers a rebuild for the target build
//
// buildkite API docs: https://buildkite.com/docs/apis/rest-api/builds#rebuild-a-build
func (bs *BuildsService) Rebuild(org, pipeline, build string) (*Build, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s/rebuild", org, pipeline, build)
	req, err := bs.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	result := Build{}
	_, err = bs.client.Do(req, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}
<commit_msg>Add filtering option for listing builds on commit<commit_after>// Copyright 2014 Mark Wolfe. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildkite

import (
	"fmt"
	"time"
)

// BuildsService handles communication with the build related
// methods of the buildkite API.
//
// buildkite API docs: https://buildkite.com/docs/api/builds
type BuildsService struct {
	client *Client
}

// Author of a commit (used in CreateBuild)
type Author struct {
	Name  string `json:"name,omitempty"`
	Email string `json:"email,omitempty"`
}

// CreateBuild - Create a build.
type CreateBuild struct {
	Commit  string `json:"commit"`
	Branch  string `json:"branch"`
	Message string `json:"message"`

	// Optional fields
	Author                      Author            `json:"author,omitempty"`
	Env                         map[string]string `json:"env,omitempty"`
	MetaData                    map[string]string `json:"meta_data,omitempty"`
	IgnorePipelineBranchFilters bool              `json:"ignore_pipeline_branch_filters,omitempty"`
	PullRequestBaseBranch       string            `json:"pull_request_base_branch,omitempty"`
	PullRequestID               int64             `json:"pull_request_id,omitempty"`
	PullRequestRepository       string            `json:"pull_request_repository,omitempty"`
}

// Creator represents who created a build
type Creator struct {
	AvatarURL string     `json:"avatar_url"`
	CreatedAt *Timestamp `json:"created_at"`
	Email     string     `json:"email"`
	ID        string     `json:"id"`
	Name      string     `json:"name"`
}

// PullRequest represents a Github PR
type PullRequest struct {
	ID         *string `json:"id,omitempty"`
	Base       *string `json:"base,omitempty"`
	Repository *string `json:"repository,omitempty"`
}

// Build represents a build which has run in buildkite
type Build struct {
	ID          *string                `json:"id,omitempty"`
	URL         *string                `json:"url,omitempty"`
	WebURL      *string                `json:"web_url,omitempty"`
	Number      *int                   `json:"number,omitempty"`
	State       *string                `json:"state,omitempty"`
	Blocked     *bool                  `json:"blocked,omitempty"`
	Message     *string                `json:"message,omitempty"`
	Commit      *string                `json:"commit,omitempty"`
	Branch      *string                `json:"branch,omitempty"`
	Env         map[string]interface{} `json:"env,omitempty"`
	CreatedAt   *Timestamp             `json:"created_at,omitempty"`
	ScheduledAt *Timestamp             `json:"scheduled_at,omitempty"`
	StartedAt   *Timestamp             `json:"started_at,omitempty"`
	FinishedAt  *Timestamp             `json:"finished_at,omitempty"`
	MetaData    interface{}            `json:"meta_data,omitempty"`
	Creator     *Creator               `json:"creator,omitempty"`

	// jobs run during the build
	Jobs []*Job `json:"jobs,omitempty"`

	// the pipeline this build is associated with
	Pipeline *Pipeline `json:"pipeline,omitempty"`

	// the pull request this build is associated with
	PullRequest *PullRequest `json:"pull_request,omitempty"`
}

// BuildsListOptions specifies the optional parameters to the
// BuildsService.List method.
type BuildsListOptions struct {

	// Filters the results by the user who created the build
	Creator string `url:"creator,omitempty"`

	// Filters the results by builds created on or after the given time
	CreatedFrom time.Time `url:"created_from,omitempty"`

	// Filters the results by builds created before the given time
	CreatedTo time.Time `url:"created_to,omitempty"`

	// Filters the results by builds finished on or after the given time
	FinishedFrom time.Time `url:"finished_from,omitempty"`

	// State of builds to list.  Possible values are: running, scheduled, passed,
	// failed, canceled, skipped and not_run. Default is "".
	State []string `url:"state,brackets,omitempty"`

	// Branch filter by the name of the branch. Default is "".
	Branch string `url:"branch,omitempty"`

	// Filters the results by builds for the specific commit SHA (full, not shortened). Default is ""/
	Commit string `url:"commit,omitempty`

	ListOptions
}

// Cancel triggers a canel for the tagrget build
//
// buildkite API docs: https://buildkite.com/docs/apis/rest-api/builds#cancel-a-build
func (bs *BuildsService) Cancel(org, pipeline, build string) (*Build, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s/cancel", org, pipeline, build)
	req, err := bs.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	result := Build{}
	_, err = bs.client.Do(req, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}

// Create - Create a pipeline
//
// buildkite API docs: https://buildkite.com/docs/api/builds#create-a-build
func (bs *BuildsService) Create(org string, pipeline string, b *CreateBuild) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	req, err := bs.client.NewRequest("POST", u, b)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := bs.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// Get fetches a build.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#get-a-build
func (bs *BuildsService) Get(org string, pipeline string, id string) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s", org, pipeline, id)

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := bs.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// List the builds for the current user.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-all-builds
func (bs *BuildsService) List(opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/builds")

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByOrg lists the builds within the specified orginisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-an-organization
func (bs *BuildsService) ListByOrg(org string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/builds", org)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByPipeline lists the builds for a pipeline within the specified originisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-a-pipeline
func (bs *BuildsService) ListByPipeline(org string, pipeline string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// Rebuild triggers a rebuild for the target build
//
// buildkite API docs: https://buildkite.com/docs/apis/rest-api/builds#rebuild-a-build
func (bs *BuildsService) Rebuild(org, pipeline, build string) (*Build, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s/rebuild", org, pipeline, build)
	req, err := bs.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	result := Build{}
	_, err = bs.client.Do(req, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}
<|endoftext|>


<commit_before>// +build !windows

package buildkite

// Logic for this file is largely based on:
// https://github.com/jarib/childprocess/blob/783f7a00a1678b5d929062564ef5ae76822dfd62/lib/childprocess/unix/process.rb

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/kr/pty"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"syscall"
	"time"
)

type Process struct {
	Output     string
	Pid        int
	Running    bool
	RunInPty   bool
	ExitStatus string
	command    *exec.Cmd
	callback   func(*Process)
}

// Implement the Stringer thingy
func (p Process) String() string {
	return fmt.Sprintf("Process{Pid: %d, Running: %t, ExitStatus: %s}", p.Pid, p.Running, p.ExitStatus)
}

func InitProcess(scriptPath string, env []string, runInPty bool, callback func(*Process)) *Process {
	// Create a new instance of our process struct
	var process Process
	process.RunInPty = runInPty

	// Find the script to run
	absolutePath, _ := filepath.Abs(scriptPath)
	scriptDirectory := filepath.Dir(absolutePath)

	process.command = exec.Command(absolutePath)
	process.command.Dir = scriptDirectory

	// Children of the forked process will inherit its process group
	// This is to make sure that all grandchildren dies when this Process instance is killed
	process.command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	// Copy the current processes ENV and merge in the new ones. We do this
	// so the sub process gets PATH and stuff. We merge our path in over
	// the top of the current one so the ENV from Buildkite and the agent
	// take precedence over the agent
	currentEnv := os.Environ()
	process.command.Env = append(currentEnv, env...)

	// Set the callback
	process.callback = callback

	return &process
}

func (p *Process) Start() error {
	var buffer bytes.Buffer
	var buffer2 bytes.Buffer
	var waitGroup sync.WaitGroup

	multiWriter := io.MultiWriter(&buffer, &buffer2)

	Logger.Infof("Starting to run script: %s", p.command.Path)

	// Toggle between running in a pty
	if p.RunInPty {
		pty, err := pty.Start(p.command)
		if err != nil {
			p.ExitStatus = "1"
			return err
		}

		p.Pid = p.command.Process.Pid
		p.Running = true

		waitGroup.Add(3)

		go func() {
			Logger.Debug("Starting to copy PTY to the buffer")

			// Copy the pty to our buffer. This will block until it EOF's
			// or something breaks.
			_, err = io.Copy(multiWriter, pty)
			if e, ok := err.(*os.PathError); ok && e.Err == syscall.EIO {
				// We can safely ignore this error, because
				// it's just the PTY telling us that it closed
				// successfully.
				// See: https://github.com/buildkite/agent/pull/34#issuecomment-46080419
			} else if err != nil {
				Logger.Errorf("io.Copy failed with error: %T: %v", err, err)
			} else {
				Logger.Debug("io.Copy finsihed")
			}

			waitGroup.Done()
		}()
	} else {
		p.command.Stdout = multiWriter
		p.command.Stderr = multiWriter

		err := p.command.Start()
		if err != nil {
			p.ExitStatus = "1"
			return err
		}

		p.Pid = p.command.Process.Pid
		p.Running = true

		// We only have to wait for 2 things if we're not running in a PTY.
		waitGroup.Add(2)
	}

	Logger.Infof("Process is running with PID: %d", p.Pid)

	go func() {
		for p.Running {
			Logger.Debug("Copying buffer to the process output")

			// Convert the stdout buffer to a string
			p.Output = buffer.String()

			// Call the callback and pass in our process object
			p.callback(p)

			// Sleep for 1 second
			time.Sleep(1000 * time.Millisecond)
		}

		Logger.Debug("Finished routine that copies the buffer to the process output")

		waitGroup.Done()
	}()

	go func() {
		for p.Running {
			line, err := buffer2.ReadString('\n')
			if err != nil {
				continue
			}

			fmt.Printf("line: %s", line)
		}

		waitGroup.Done()
	}()

	// Wait until the process has finished. The returned error is nil if the command runs,
	// has no problems copying stdin, stdout, and stderr, and exits with a zero exit status.
	waitResult := p.command.Wait()

	// The process is no longer running at this point
	p.Running = false

	// Find the exit status of the script
	p.ExitStatus = getExitStatus(waitResult)

	Logger.Infof("Process with PID: %d finished with Exit Status: %s", p.Pid, p.ExitStatus)

	// Sometimes (in docker containers) io.Copy never seems to finish. This is a mega
	// hack around it. If it doesn't finish after 1 second, just continue.
	Logger.Debug("Waiting for io.Copy and incremental output to finish")
	err := timeoutWait(&waitGroup)
	if err != nil {
		Logger.Errorf("Timed out waiting for wait group: (%T: %v)", err, err)
	}

	// Copy the final output back to the process
	p.Output = buffer.String()

	// No error occured so we can return nil
	return nil
}

func (p *Process) Kill() error {
	// Send a sigterm
	err := p.signal(syscall.SIGTERM)
	if err != nil {
		return err
	}

	// Make a chanel that we'll use as a timeout
	c := make(chan int, 1)
	checking := true

	// Start a routine that checks to see if the process
	// is still alive.
	go func() {
		for checking {
			Logger.Debugf("Checking to see if PID: %d is still alive", p.Pid)

			foundProcess, err := os.FindProcess(p.Pid)

			// Can't find the process at all
			if err != nil {
				Logger.Debugf("Could not find process with PID: %d", p.Pid)

				break
			}

			// We have some information about the procss
			if foundProcess != nil {
				processState, err := foundProcess.Wait()

				if err != nil || processState.Exited() {
					Logger.Debugf("Process with PID: %d has exited.", p.Pid)

					break
				}
			}

			// Retry in a moment
			sleepTime := time.Duration(1 * time.Second)
			time.Sleep(sleepTime)
		}

		c <- 1
	}()

	// Timeout this process after 3 seconds
	select {
	case _ = <-c:
		// Was successfully terminated
	case <-time.After(10 * time.Second):
		// Stop checking in the routine above
		checking = false

		// Forcefully kill the thing
		err = p.signal(syscall.SIGKILL)

		if err != nil {
			return err
		}
	}

	return nil
}

func (p *Process) signal(sig os.Signal) error {
	Logger.Debugf("Sending signal: %s to PID: %d", sig.String(), p.Pid)

	err := p.command.Process.Signal(syscall.SIGTERM)
	if err != nil {
		Logger.Errorf("Failed to send signal: %s to PID: %d (%T: %v)", sig.String(), p.Pid, err, err)
		return err
	}

	return nil
}

// https://github.com/hnakamur/commango/blob/fe42b1cf82bf536ce7e24dceaef6656002e03743/os/executil/executil.go#L29
// TODO: Can this be better?
func getExitStatus(waitResult error) string {
	exitStatus := -1

	if waitResult != nil {
		if err, ok := waitResult.(*exec.ExitError); ok {
			if s, ok := err.Sys().(syscall.WaitStatus); ok {
				exitStatus = s.ExitStatus()
			} else {
				Logger.Error("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
			}
		}
	} else {
		exitStatus = 0
	}

	return fmt.Sprintf("%d", exitStatus)
}

func timeoutWait(waitGroup *sync.WaitGroup) error {
	// Make a chanel that we'll use as a timeout
	c := make(chan int, 1)

	// Start waiting for the routines to finish
	go func() {
		waitGroup.Wait()
		c <- 1
	}()

	select {
	case _ = <-c:
		return nil
	case <-time.After(5 * time.Second):
		return errors.New("Timeout")
	}

	return nil
}
<commit_msg>Correct use of reading lines as well from the output.<commit_after>// +build !windows

package buildkite

// Logic for this file is largely based on:
// https://github.com/jarib/childprocess/blob/783f7a00a1678b5d929062564ef5ae76822dfd62/lib/childprocess/unix/process.rb

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/kr/pty"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"syscall"
	"time"
)

type Process struct {
	Output     string
	Pid        int
	Running    bool
	RunInPty   bool
	ExitStatus string
	command    *exec.Cmd
	callback   func(*Process)
}

// Implement the Stringer thingy
func (p Process) String() string {
	return fmt.Sprintf("Process{Pid: %d, Running: %t, ExitStatus: %s}", p.Pid, p.Running, p.ExitStatus)
}

func InitProcess(scriptPath string, env []string, runInPty bool, callback func(*Process)) *Process {
	// Create a new instance of our process struct
	var process Process
	process.RunInPty = runInPty

	// Find the script to run
	absolutePath, _ := filepath.Abs(scriptPath)
	scriptDirectory := filepath.Dir(absolutePath)

	process.command = exec.Command(absolutePath)
	process.command.Dir = scriptDirectory

	// Children of the forked process will inherit its process group
	// This is to make sure that all grandchildren dies when this Process instance is killed
	process.command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	// Copy the current processes ENV and merge in the new ones. We do this
	// so the sub process gets PATH and stuff. We merge our path in over
	// the top of the current one so the ENV from Buildkite and the agent
	// take precedence over the agent
	currentEnv := os.Environ()
	process.command.Env = append(currentEnv, env...)

	// Set the callback
	process.callback = callback

	return &process
}

func (p *Process) Start() error {
	var buffer bytes.Buffer
	var waitGroup sync.WaitGroup

	lineReadr, lineWritr := io.Pipe()

	multiWriter := io.MultiWriter(&buffer, lineWritr)

	Logger.Infof("Starting to run script: %s", p.command.Path)

	// Toggle between running in a pty
	if p.RunInPty {
		Logger.Debugf("Process is running in a PTY")

		pty, err := pty.Start(p.command)
		if err != nil {
			p.ExitStatus = "1"
			return err
		}

		p.Pid = p.command.Process.Pid
		p.Running = true

		waitGroup.Add(3)

		go func() {
			Logger.Debug("Starting to copy PTY to the buffer")

			// Copy the pty to our buffer. This will block until it EOF's
			// or something breaks.
			_, err = io.Copy(multiWriter, pty)
			if e, ok := err.(*os.PathError); ok && e.Err == syscall.EIO {
				// We can safely ignore this error, because
				// it's just the PTY telling us that it closed
				// successfully.
				// See: https://github.com/buildkite/agent/pull/34#issuecomment-46080419
			} else if err != nil {
				Logger.Errorf("io.Copy failed with error: %T: %v", err, err)
			} else {
				Logger.Debug("io.Copy finsihed")
			}

			waitGroup.Done()
		}()
	} else {
		Logger.Debugf("Process is running without a PTY")

		p.command.Stdout = multiWriter
		p.command.Stderr = multiWriter

		err := p.command.Start()
		if err != nil {
			p.ExitStatus = "1"
			return err
		}

		p.Pid = p.command.Process.Pid
		p.Running = true

		// We only have to wait for 2 things if we're not running in a PTY.
		waitGroup.Add(2)
	}

	Logger.Infof("Process is running with PID: %d", p.Pid)

	go func() {
		for p.Running {
			Logger.Debug("Copying buffer to the process output")

			// Convert the stdout buffer to a string
			p.Output = buffer.String()

			// Call the callback and pass in our process object
			p.callback(p)

			// Sleep for 1 second
			time.Sleep(1000 * time.Millisecond)
		}

		Logger.Debug("Finished routine that copies the buffer to the process output")

		waitGroup.Done()
	}()

	go func() {
		Logger.Infof("SCANNING TIME")

		scanner := bufio.NewScanner(lineReadr)
		for scanner.Scan() {
			fmt.Printf("line %s\n", scanner.Text()) // Println will add back the final '\n'
		}

		if err := scanner.Err(); err != nil {
			Logger.Errorf("ZOMG ERR %s", err)
		}

		Logger.Infof("BOO FIN")

		waitGroup.Done()
	}()

	// Wait until the process has finished. The returned error is nil if the command runs,
	// has no problems copying stdin, stdout, and stderr, and exits with a zero exit status.
	waitResult := p.command.Wait()

	// The process is no longer running at this point
	p.Running = false

	// Find the exit status of the script
	p.ExitStatus = getExitStatus(waitResult)

	Logger.Infof("Process with PID: %d finished with Exit Status: %s", p.Pid, p.ExitStatus)

	// Sometimes (in docker containers) io.Copy never seems to finish. This is a mega
	// hack around it. If it doesn't finish after 1 second, just continue.
	Logger.Debug("Waiting for io.Copy and incremental output to finish")
	err := timeoutWait(&waitGroup)
	if err != nil {
		Logger.Errorf("Timed out waiting for wait group: (%T: %v)", err, err)
	}

	// Copy the final output back to the process
	p.Output = buffer.String()

	// No error occured so we can return nil
	return nil
}

func (p *Process) Kill() error {
	// Send a sigterm
	err := p.signal(syscall.SIGTERM)
	if err != nil {
		return err
	}

	// Make a chanel that we'll use as a timeout
	c := make(chan int, 1)
	checking := true

	// Start a routine that checks to see if the process
	// is still alive.
	go func() {
		for checking {
			Logger.Debugf("Checking to see if PID: %d is still alive", p.Pid)

			foundProcess, err := os.FindProcess(p.Pid)

			// Can't find the process at all
			if err != nil {
				Logger.Debugf("Could not find process with PID: %d", p.Pid)

				break
			}

			// We have some information about the procss
			if foundProcess != nil {
				processState, err := foundProcess.Wait()

				if err != nil || processState.Exited() {
					Logger.Debugf("Process with PID: %d has exited.", p.Pid)

					break
				}
			}

			// Retry in a moment
			sleepTime := time.Duration(1 * time.Second)
			time.Sleep(sleepTime)
		}

		c <- 1
	}()

	// Timeout this process after 3 seconds
	select {
	case _ = <-c:
		// Was successfully terminated
	case <-time.After(10 * time.Second):
		// Stop checking in the routine above
		checking = false

		// Forcefully kill the thing
		err = p.signal(syscall.SIGKILL)

		if err != nil {
			return err
		}
	}

	return nil
}

func (p *Process) signal(sig os.Signal) error {
	Logger.Debugf("Sending signal: %s to PID: %d", sig.String(), p.Pid)

	err := p.command.Process.Signal(syscall.SIGTERM)
	if err != nil {
		Logger.Errorf("Failed to send signal: %s to PID: %d (%T: %v)", sig.String(), p.Pid, err, err)
		return err
	}

	return nil
}

// https://github.com/hnakamur/commango/blob/fe42b1cf82bf536ce7e24dceaef6656002e03743/os/executil/executil.go#L29
// TODO: Can this be better?
func getExitStatus(waitResult error) string {
	exitStatus := -1

	if waitResult != nil {
		if err, ok := waitResult.(*exec.ExitError); ok {
			if s, ok := err.Sys().(syscall.WaitStatus); ok {
				exitStatus = s.ExitStatus()
			} else {
				Logger.Error("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
			}
		}
	} else {
		exitStatus = 0
	}

	return fmt.Sprintf("%d", exitStatus)
}

func timeoutWait(waitGroup *sync.WaitGroup) error {
	// Make a chanel that we'll use as a timeout
	c := make(chan int, 1)

	// Start waiting for the routines to finish
	go func() {
		waitGroup.Wait()
		c <- 1
	}()

	select {
	case _ = <-c:
		return nil
	case <-time.After(5 * time.Second):
		return errors.New("Timeout")
	}

	return nil
}
<|endoftext|>


<commit_before>package buildstore

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"strings"

	"sourcegraph.com/sourcegraph/repo"

	"github.com/kr/fs"
	"github.com/sourcegraph/rwvfs"
)

const (
	BuildDataDirName = ".sourcegraph-data"
)

var (
	// localDirs stores the OS filesystem path that each local repository store
	// is rooted at. It is used to construct the full, non-VFS path to files
	// within local VFSes.
	localDirs = make(map[*RepositoryStore]string)
)

type MultiStore struct {
	walkableRWVFS
}

func New(fs rwvfs.FileSystem) *MultiStore {
	return &MultiStore{walkableRWVFS{fs}}
}

func (s *MultiStore) RepositoryStore(repoURI repo.URI) (*RepositoryStore, error) {
	path := filepath.Clean(string(repoURI))
	err := rwvfs.MkdirAll(s, path)
	if err != nil {
		return nil, err
	}
	return newRepositoryStore(walkableRWVFS{rwvfs.Sub(s.walkableRWVFS, path)}), nil
}

type RepositoryStore struct {
	walkableRWVFS
}

func newRepositoryStore(fs rwvfs.FileSystem) *RepositoryStore {
	return &RepositoryStore{walkableRWVFS{fs}}
}

func NewRepositoryStore(repoDir string) (*RepositoryStore, error) {
	storeDir, err := filepath.Abs(filepath.Join(repoDir, BuildDataDirName))

	err = os.Mkdir(storeDir, 0700)
	if os.IsExist(err) {
		err = nil
	}
	if err != nil {
		return nil, err
	}

	s := newRepositoryStore(walkableRWVFS{rwvfs.OS(storeDir)})

	localDirs[s] = storeDir

	return s, nil
}

// RootDir returns the OS filesystem path that s's VFS is rooted at, if
// it is a local store (that uses the OS filesystem). If s is a
// non-OS-filesystem VFS, an error is returned.
func RootDir(s *RepositoryStore) (string, error) {
	if dir, present := localDirs[s]; present {
		return dir, nil
	}
	return "", fmt.Errorf("store VFS is not an OS filesystem VFS")
}

type BuildDataFileInfo struct {
	CommitID string
	Path     string
	Size     int64
	ModTime  time.Time
	DataType string
}

func (s *RepositoryStore) CommitPath(commitID string) string { return commitID }

func (s *RepositoryStore) FilePath(commitID, path string) string {
	return filepath.Join(s.CommitPath(commitID), path)
}

func (s *RepositoryStore) ListCommits() ([]string, error) {
	files, err := s.ReadDir(".")
	if err != nil {
		return nil, err
	}

	var commits []string
	for _, f := range files {
		if f.IsDir() {
			commits = append(commits, f.Name())
		}
	}
	return commits, nil
}

const CachedRepositoryConfigFilename = "cached-config.json"

var dataFilesIgnoreBasenames = map[string]struct{}{
	CachedRepositoryConfigFilename: struct{}{},
}

func (s *RepositoryStore) DataFiles(path string) ([]*BuildDataFileInfo, error) {
	var files []*BuildDataFileInfo
	walker := fs.WalkFS(path, s.walkableRWVFS)
	for walker.Step() {
		fi := walker.Stat()
		if fi == nil {
			continue
		}
		if fi.IsDir() {
			continue
		}

		path := strings.TrimPrefix(walker.Path(), "/")

		if _, ignore := dataFilesIgnoreBasenames[filepath.Base(path)]; ignore {
			continue
		}

		parts := strings.SplitN(path, "/", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("bad build data file path: %q", walker.Path())
		}
		commitID, path := parts[0], parts[1]

		dataTypeName, _ := DataType(path)

		files = append(files, &BuildDataFileInfo{
			CommitID: commitID,
			Path:     path,
			Size:     fi.Size(),
			ModTime:  fi.ModTime(),
			DataType: dataTypeName,
		})
	}
	return files, nil
}

func (s *RepositoryStore) DataFilesForCommit(commitID string) ([]*BuildDataFileInfo, error) {
	return s.DataFiles(s.CommitPath(commitID))
}

func (s *RepositoryStore) AllDataFiles() ([]*BuildDataFileInfo, error) {
	return s.DataFiles(".")
}

type walkableRWVFS struct{ rwvfs.FileSystem }

func (_ walkableRWVFS) Join(elem ...string) string { return filepath.Join(elem...) }
<commit_msg>new filename<commit_after>package buildstore

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"strings"

	"sourcegraph.com/sourcegraph/repo"

	"github.com/kr/fs"
	"github.com/sourcegraph/rwvfs"
)

const (
	BuildDataDirName = ".sourcegraph-data"
)

var (
	// localDirs stores the OS filesystem path that each local repository store
	// is rooted at. It is used to construct the full, non-VFS path to files
	// within local VFSes.
	localDirs = make(map[*RepositoryStore]string)
)

type MultiStore struct {
	walkableRWVFS
}

func New(fs rwvfs.FileSystem) *MultiStore {
	return &MultiStore{walkableRWVFS{fs}}
}

func (s *MultiStore) RepositoryStore(repoURI repo.URI) (*RepositoryStore, error) {
	path := filepath.Clean(string(repoURI))
	err := rwvfs.MkdirAll(s, path)
	if err != nil {
		return nil, err
	}
	return newRepositoryStore(walkableRWVFS{rwvfs.Sub(s.walkableRWVFS, path)}), nil
}

type RepositoryStore struct {
	walkableRWVFS
}

func newRepositoryStore(fs rwvfs.FileSystem) *RepositoryStore {
	return &RepositoryStore{walkableRWVFS{fs}}
}

func NewRepositoryStore(repoDir string) (*RepositoryStore, error) {
	storeDir, err := filepath.Abs(filepath.Join(repoDir, BuildDataDirName))

	err = os.Mkdir(storeDir, 0700)
	if os.IsExist(err) {
		err = nil
	}
	if err != nil {
		return nil, err
	}

	s := newRepositoryStore(walkableRWVFS{rwvfs.OS(storeDir)})

	localDirs[s] = storeDir

	return s, nil
}

// RootDir returns the OS filesystem path that s's VFS is rooted at, if
// it is a local store (that uses the OS filesystem). If s is a
// non-OS-filesystem VFS, an error is returned.
func RootDir(s *RepositoryStore) (string, error) {
	if dir, present := localDirs[s]; present {
		return dir, nil
	}
	return "", fmt.Errorf("store VFS is not an OS filesystem VFS")
}

type BuildDataFileInfo struct {
	CommitID string
	Path     string
	Size     int64
	ModTime  time.Time
	DataType string
}

func (s *RepositoryStore) CommitPath(commitID string) string { return commitID }

func (s *RepositoryStore) FilePath(commitID, path string) string {
	return filepath.Join(s.CommitPath(commitID), path)
}

func (s *RepositoryStore) ListCommits() ([]string, error) {
	files, err := s.ReadDir(".")
	if err != nil {
		return nil, err
	}

	var commits []string
	for _, f := range files {
		if f.IsDir() {
			commits = append(commits, f.Name())
		}
	}
	return commits, nil
}

const CachedRepositoryConfigFilename = "config.json"

var dataFilesIgnoreBasenames = map[string]struct{}{
	CachedRepositoryConfigFilename: struct{}{},
}

func (s *RepositoryStore) DataFiles(path string) ([]*BuildDataFileInfo, error) {
	var files []*BuildDataFileInfo
	walker := fs.WalkFS(path, s.walkableRWVFS)
	for walker.Step() {
		fi := walker.Stat()
		if fi == nil {
			continue
		}
		if fi.IsDir() {
			continue
		}

		path := strings.TrimPrefix(walker.Path(), "/")

		if _, ignore := dataFilesIgnoreBasenames[filepath.Base(path)]; ignore {
			continue
		}

		parts := strings.SplitN(path, "/", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("bad build data file path: %q", walker.Path())
		}
		commitID, path := parts[0], parts[1]

		dataTypeName, _ := DataType(path)

		files = append(files, &BuildDataFileInfo{
			CommitID: commitID,
			Path:     path,
			Size:     fi.Size(),
			ModTime:  fi.ModTime(),
			DataType: dataTypeName,
		})
	}
	return files, nil
}

func (s *RepositoryStore) DataFilesForCommit(commitID string) ([]*BuildDataFileInfo, error) {
	return s.DataFiles(s.CommitPath(commitID))
}

func (s *RepositoryStore) AllDataFiles() ([]*BuildDataFileInfo, error) {
	return s.DataFiles(".")
}

type walkableRWVFS struct{ rwvfs.FileSystem }

func (_ walkableRWVFS) Join(elem ...string) string { return filepath.Join(elem...) }
<|endoftext|>


<commit_before>package gocbcore

import (
	"encoding/binary"
	"sync/atomic"
	"time"

	"github.com/golang/snappy"
)

func isCompressibleOp(command commandCode) bool {
	switch command {
	case cmdSet:
		fallthrough
	case cmdAdd:
		fallthrough
	case cmdReplace:
		fallthrough
	case cmdAppend:
		fallthrough
	case cmdPrepend:
		return true
	}
	return false
}

type memdClient struct {
	parent       *Agent
	conn         memdConn
	opList       memdOpMap
	errorMap     *kvErrorMap
	features     []HelloFeature
	closeNotify  chan bool
	dcpAckSize   int
	dcpFlowRecv  int
	lastActivity int64
	connId       string
	closed       bool
}

func newMemdClient(parent *Agent, conn memdConn) *memdClient {
	client := memdClient{
		parent:      parent,
		conn:        conn,
		closeNotify: make(chan bool),
		connId:      parent.clientId + "/" + formatCbUid(randomCbUid()),
	}
	client.run()
	return &client
}

func (client *memdClient) SupportsFeature(feature HelloFeature) bool {
	return checkSupportsFeature(client.features, feature)
}

func (client *memdClient) EnableDcpBufferAck(bufferAckSize int) {
	client.dcpAckSize = bufferAckSize
}

func (client *memdClient) maybeSendDcpBufferAck(packet *memdPacket) {
	packetLen := 24 + len(packet.Extras) + len(packet.Key) + len(packet.Value)

	client.dcpFlowRecv += packetLen
	if client.dcpFlowRecv < client.dcpAckSize {
		return
	}

	ackAmt := client.dcpFlowRecv

	extrasBuf := make([]byte, 4)
	binary.BigEndian.PutUint32(extrasBuf, uint32(ackAmt))

	err := client.conn.WritePacket(&memdPacket{
		Magic:  reqMagic,
		Opcode: cmdDcpBufferAck,
		Extras: extrasBuf,
	})
	if err != nil {
		logWarnf("Failed to dispatch DCP buffer ack: %s", err)
	}

	client.dcpFlowRecv -= ackAmt
}

func (client *memdClient) SetErrorMap(errorMap *kvErrorMap) {
	client.errorMap = errorMap
}

func (client *memdClient) Address() string {
	return client.conn.RemoteAddr()
}

func (client *memdClient) CloseNotify() chan bool {
	return client.closeNotify
}

func (client *memdClient) SendRequest(req *memdQRequest) error {
	addSuccess := client.opList.Add(req)
	if !addSuccess {
		return ErrCancelled
	}

	packet := &req.memdPacket
	if client.SupportsFeature(FeatureSnappy) {
		isCompressed := (packet.Datatype & uint8(DatatypeFlagCompressed)) != 0
		packetSize := len(packet.Value)
		if !isCompressed && packetSize > client.parent.compressionMinSize && isCompressibleOp(packet.Opcode) {
			compressedValue := snappy.Encode(nil, packet.Value)
			if float64(len(compressedValue))/float64(packetSize) <= client.parent.compressionMinRatio {
				newPacket := *packet
				newPacket.Value = compressedValue
				newPacket.Datatype = newPacket.Datatype | uint8(DatatypeFlagCompressed)
				packet = &newPacket
			}
		}
	}

	logSchedf("Writing request. %s OP=0x%x. Opaque=%d", client.conn.LocalAddr(), req.Opcode, req.Opaque)

	client.parent.startNetTrace(req)

	err := client.conn.WritePacket(packet)
	if err != nil {
		logDebugf("memdClient write failure: %v", err)
		client.opList.Remove(req)
		return err
	}

	return nil
}

func (client *memdClient) resolveRequest(resp *memdQResponse) {
	opIndex := resp.Opaque

	// Find the request that goes with this response
	req := client.opList.FindAndMaybeRemove(opIndex, resp.Status != StatusSuccess)

	if req == nil {
		// There is no known request that goes with this response.  Ignore it.
		logDebugf("Received response with no corresponding request.")
		if client.parent.useZombieLogger {
			client.parent.recordZombieResponse(resp, client)
		}
		return
	}

	req.processingLock.Lock()

	if !req.Persistent {
		client.parent.stopNetTrace(req, resp, client)
	}

	isCompressed := (resp.Datatype & uint8(DatatypeFlagCompressed)) != 0
	if isCompressed {
		newValue, err := snappy.Decode(nil, resp.Value)
		if err != nil {
			req.processingLock.Unlock()
			logDebugf("Failed to decompress value from the server for key `%s`.", req.Key)
			return
		}

		resp.Value = newValue
		resp.Datatype = resp.Datatype & ^uint8(DatatypeFlagCompressed)
	}

	// Give the agent an opportunity to intercept the response first
	var err error
	if resp.Magic == resMagic {
		if resp.Status != StatusSuccess {
			if ok, foundErr := findMemdError(resp.Status); ok {
				err = foundErr
			} else {
				err = newSimpleError(resp.Status)
			}
		}
	}

	if client.parent == nil {
		req.processingLock.Unlock()
	} else {
		if !req.Persistent {
			client.parent.stopCmdTrace(req)
		}

		req.processingLock.Unlock()
		if err != ErrCancelled {
			shortCircuited, routeErr := client.parent.handleOpRoutingResp(resp, req, err)
			if shortCircuited {
				logSchedf("Routing callback intercepted response")
				return
			}
			err = routeErr
		}
	}

	// Call the requests callback handler...
	logSchedf("Dispatching response callback. OP=0x%x. Opaque=%d", resp.Opcode, resp.Opaque)
	req.tryCallback(resp, err)
}

func (client *memdClient) run() {
	dcpBufferQ := make(chan *memdQResponse)
	dcpKillSwitch := make(chan bool)
	dcpKillNotify := make(chan bool)
	go func() {
		for {
			select {
			case resp, more := <-dcpBufferQ:
				if !more {
					dcpKillNotify <- true
					return
				}

				logSchedf("Resolving response OP=0x%x. Opaque=%d", resp.Opcode, resp.Opaque)
				client.resolveRequest(resp)

				if client.dcpAckSize > 0 {
					client.maybeSendDcpBufferAck(&resp.memdPacket)
				}
			case <-dcpKillSwitch:
				close(dcpBufferQ)
			}
		}
	}()

	go func() {
		for {
			resp := &memdQResponse{
				sourceAddr:   client.conn.RemoteAddr(),
				sourceConnId: client.connId,
			}

			err := client.conn.ReadPacket(&resp.memdPacket)
			if err != nil {
				if !client.closed {
					logErrorf("memdClient read failure: %v", err)
				}
				break
			}

			atomic.StoreInt64(&client.lastActivity, time.Now().UnixNano())

			// We handle DCP no-op's directly here so we can reply immediately.
			if resp.memdPacket.Opcode == cmdDcpNoop {
				err := client.conn.WritePacket(&memdPacket{
					Magic:  resMagic,
					Opcode: cmdDcpNoop,
					Opaque: resp.Opaque,
				})
				if err != nil {
					logWarnf("Failed to dispatch DCP noop reply: %s", err)
				}
				continue
			}

			switch resp.memdPacket.Opcode {
			case cmdDcpDeletion:
				fallthrough
			case cmdDcpExpiration:
				fallthrough
			case cmdDcpMutation:
				fallthrough
			case cmdDcpSnapshotMarker:
				fallthrough
			case cmdDcpStreamEnd:
				dcpBufferQ <- resp
				continue
			default:
				logSchedf("Resolving response OP=0x%x. Opaque=%d", resp.Opcode, resp.Opaque)
				client.resolveRequest(resp)
			}
		}

		if !client.closed {
			err := client.conn.Close()
			if err != nil {
				// Lets log an error, as this is non-fatal
				logErrorf("Failed to shut down client connection (%s)", err)
			}
		}

		dcpKillSwitch <- true
		<-dcpKillNotify

		client.opList.Drain(func(req *memdQRequest) {
			req.tryCallback(nil, ErrNetwork)
		})

		close(client.closeNotify)
	}()
}

func (client *memdClient) Close() error {
	client.closed = true
	return client.conn.Close()
}
<commit_msg>GOCBC-323: Add the remote address to the writing request log message<commit_after>package gocbcore

import (
	"encoding/binary"
	"sync/atomic"
	"time"

	"github.com/golang/snappy"
)

func isCompressibleOp(command commandCode) bool {
	switch command {
	case cmdSet:
		fallthrough
	case cmdAdd:
		fallthrough
	case cmdReplace:
		fallthrough
	case cmdAppend:
		fallthrough
	case cmdPrepend:
		return true
	}
	return false
}

type memdClient struct {
	parent       *Agent
	conn         memdConn
	opList       memdOpMap
	errorMap     *kvErrorMap
	features     []HelloFeature
	closeNotify  chan bool
	dcpAckSize   int
	dcpFlowRecv  int
	lastActivity int64
	connId       string
	closed       bool
}

func newMemdClient(parent *Agent, conn memdConn) *memdClient {
	client := memdClient{
		parent:      parent,
		conn:        conn,
		closeNotify: make(chan bool),
		connId:      parent.clientId + "/" + formatCbUid(randomCbUid()),
	}
	client.run()
	return &client
}

func (client *memdClient) SupportsFeature(feature HelloFeature) bool {
	return checkSupportsFeature(client.features, feature)
}

func (client *memdClient) EnableDcpBufferAck(bufferAckSize int) {
	client.dcpAckSize = bufferAckSize
}

func (client *memdClient) maybeSendDcpBufferAck(packet *memdPacket) {
	packetLen := 24 + len(packet.Extras) + len(packet.Key) + len(packet.Value)

	client.dcpFlowRecv += packetLen
	if client.dcpFlowRecv < client.dcpAckSize {
		return
	}

	ackAmt := client.dcpFlowRecv

	extrasBuf := make([]byte, 4)
	binary.BigEndian.PutUint32(extrasBuf, uint32(ackAmt))

	err := client.conn.WritePacket(&memdPacket{
		Magic:  reqMagic,
		Opcode: cmdDcpBufferAck,
		Extras: extrasBuf,
	})
	if err != nil {
		logWarnf("Failed to dispatch DCP buffer ack: %s", err)
	}

	client.dcpFlowRecv -= ackAmt
}

func (client *memdClient) SetErrorMap(errorMap *kvErrorMap) {
	client.errorMap = errorMap
}

func (client *memdClient) Address() string {
	return client.conn.RemoteAddr()
}

func (client *memdClient) CloseNotify() chan bool {
	return client.closeNotify
}

func (client *memdClient) SendRequest(req *memdQRequest) error {
	addSuccess := client.opList.Add(req)
	if !addSuccess {
		return ErrCancelled
	}

	packet := &req.memdPacket
	if client.SupportsFeature(FeatureSnappy) {
		isCompressed := (packet.Datatype & uint8(DatatypeFlagCompressed)) != 0
		packetSize := len(packet.Value)
		if !isCompressed && packetSize > client.parent.compressionMinSize && isCompressibleOp(packet.Opcode) {
			compressedValue := snappy.Encode(nil, packet.Value)
			if float64(len(compressedValue))/float64(packetSize) <= client.parent.compressionMinRatio {
				newPacket := *packet
				newPacket.Value = compressedValue
				newPacket.Datatype = newPacket.Datatype | uint8(DatatypeFlagCompressed)
				packet = &newPacket
			}
		}
	}

	logSchedf("Writing request. %s to %s OP=0x%x. Opaque=%d", client.conn.LocalAddr(), client.Address(), req.Opcode, req.Opaque)

	client.parent.startNetTrace(req)

	err := client.conn.WritePacket(packet)
	if err != nil {
		logDebugf("memdClient write failure: %v", err)
		client.opList.Remove(req)
		return err
	}

	return nil
}

func (client *memdClient) resolveRequest(resp *memdQResponse) {
	opIndex := resp.Opaque

	// Find the request that goes with this response
	req := client.opList.FindAndMaybeRemove(opIndex, resp.Status != StatusSuccess)

	if req == nil {
		// There is no known request that goes with this response.  Ignore it.
		logDebugf("Received response with no corresponding request.")
		if client.parent.useZombieLogger {
			client.parent.recordZombieResponse(resp, client)
		}
		return
	}

	req.processingLock.Lock()

	if !req.Persistent {
		client.parent.stopNetTrace(req, resp, client)
	}

	isCompressed := (resp.Datatype & uint8(DatatypeFlagCompressed)) != 0
	if isCompressed {
		newValue, err := snappy.Decode(nil, resp.Value)
		if err != nil {
			req.processingLock.Unlock()
			logDebugf("Failed to decompress value from the server for key `%s`.", req.Key)
			return
		}

		resp.Value = newValue
		resp.Datatype = resp.Datatype & ^uint8(DatatypeFlagCompressed)
	}

	// Give the agent an opportunity to intercept the response first
	var err error
	if resp.Magic == resMagic {
		if resp.Status != StatusSuccess {
			if ok, foundErr := findMemdError(resp.Status); ok {
				err = foundErr
			} else {
				err = newSimpleError(resp.Status)
			}
		}
	}

	if client.parent == nil {
		req.processingLock.Unlock()
	} else {
		if !req.Persistent {
			client.parent.stopCmdTrace(req)
		}

		req.processingLock.Unlock()
		if err != ErrCancelled {
			shortCircuited, routeErr := client.parent.handleOpRoutingResp(resp, req, err)
			if shortCircuited {
				logSchedf("Routing callback intercepted response")
				return
			}
			err = routeErr
		}
	}

	// Call the requests callback handler...
	logSchedf("Dispatching response callback. OP=0x%x. Opaque=%d", resp.Opcode, resp.Opaque)
	req.tryCallback(resp, err)
}

func (client *memdClient) run() {
	dcpBufferQ := make(chan *memdQResponse)
	dcpKillSwitch := make(chan bool)
	dcpKillNotify := make(chan bool)
	go func() {
		for {
			select {
			case resp, more := <-dcpBufferQ:
				if !more {
					dcpKillNotify <- true
					return
				}

				logSchedf("Resolving response OP=0x%x. Opaque=%d", resp.Opcode, resp.Opaque)
				client.resolveRequest(resp)

				if client.dcpAckSize > 0 {
					client.maybeSendDcpBufferAck(&resp.memdPacket)
				}
			case <-dcpKillSwitch:
				close(dcpBufferQ)
			}
		}
	}()

	go func() {
		for {
			resp := &memdQResponse{
				sourceAddr:   client.conn.RemoteAddr(),
				sourceConnId: client.connId,
			}

			err := client.conn.ReadPacket(&resp.memdPacket)
			if err != nil {
				if !client.closed {
					logErrorf("memdClient read failure: %v", err)
				}
				break
			}

			atomic.StoreInt64(&client.lastActivity, time.Now().UnixNano())

			// We handle DCP no-op's directly here so we can reply immediately.
			if resp.memdPacket.Opcode == cmdDcpNoop {
				err := client.conn.WritePacket(&memdPacket{
					Magic:  resMagic,
					Opcode: cmdDcpNoop,
					Opaque: resp.Opaque,
				})
				if err != nil {
					logWarnf("Failed to dispatch DCP noop reply: %s", err)
				}
				continue
			}

			switch resp.memdPacket.Opcode {
			case cmdDcpDeletion:
				fallthrough
			case cmdDcpExpiration:
				fallthrough
			case cmdDcpMutation:
				fallthrough
			case cmdDcpSnapshotMarker:
				fallthrough
			case cmdDcpStreamEnd:
				dcpBufferQ <- resp
				continue
			default:
				logSchedf("Resolving response OP=0x%x. Opaque=%d", resp.Opcode, resp.Opaque)
				client.resolveRequest(resp)
			}
		}

		if !client.closed {
			err := client.conn.Close()
			if err != nil {
				// Lets log an error, as this is non-fatal
				logErrorf("Failed to shut down client connection (%s)", err)
			}
		}

		dcpKillSwitch <- true
		<-dcpKillNotify

		client.opList.Drain(func(req *memdQRequest) {
			req.tryCallback(nil, ErrNetwork)
		})

		close(client.closeNotify)
	}()
}

func (client *memdClient) Close() error {
	client.closed = true
	return client.conn.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/JamesClonk/vultr/lib"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceVultrSSHKey() *schema.Resource {
	return &schema.Resource{
		Create: resourceVultrSSHKeyCreate,
		Read:   resourceVultrSSHKeyRead,
		Update: resourceVultrSSHKeyUpdate,
		Delete: resourceVultrSSHKeyDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"public_key": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				StateFunc: func(v interface{}) string {
					// Vultr always trims the key, so to get a correct diff we
					// also need to trim.
					return strings.TrimSpace(v.(string))
				},
			},
		},
	}
}

func resourceVultrSSHKeyCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*lib.Client)

	name := d.Get("name").(string)
	publicKey := d.Get("public_key").(string)

	log.Printf("[DEBUG] SSH Key create: %s", name)

	key, err := client.CreateSSHKey(name, publicKey)
	if err != nil {
		return fmt.Errorf("Error creating SSH Key: %s", err)
	}

	d.SetId(key.ID)

	log.Printf("[INFO] SSH Key: %s", key.ID)

	return resourceVultrSSHKeyRead(d, meta)
}

func resourceVultrSSHKeyRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*lib.Client)

	// NB the API has no support for getting a key by id, so we have to get'em all.

	keys, err := client.GetSSHKeys()
	if err != nil {
		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	var key *lib.SSHKey

	for _, k := range keys {
		if k.ID == d.Id() {
			key = &k
		}
	}

	// if the key is somehow already destroyed mark as succesfully gone.
	if key == nil {
		d.SetId("")
		return nil
	}

	d.Set("name", key.Name)
	d.Set("public_key", key.Key)

	return nil
}

func resourceVultrSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*lib.Client)

	key := lib.SSHKey{
		ID:   d.Id(),
		Name: d.Get("name").(string),
		Key:  d.Get("public_key").(string),
	}

	log.Printf("[DEBUG] SSH key update: %s", key.ID)
	err := client.UpdateSSHKey(key)
	if err != nil {
		return fmt.Errorf("Failed to update SSH key: %s", err)
	}

	return resourceVultrSSHKeyRead(d, meta)
}

func resourceVultrSSHKeyDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*lib.Client)

	log.Printf("[INFO] Deleting SSH key: %s", d.Id())
	err := client.DeleteSSHKey(d.Id())
	if err != nil {
		return fmt.Errorf("Error deleting SSH key: %s", err)
	}

	return nil
}
<commit_msg>vultr_ssh_key: fix key searching<commit_after>package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/JamesClonk/vultr/lib"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceVultrSSHKey() *schema.Resource {
	return &schema.Resource{
		Create: resourceVultrSSHKeyCreate,
		Read:   resourceVultrSSHKeyRead,
		Update: resourceVultrSSHKeyUpdate,
		Delete: resourceVultrSSHKeyDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"public_key": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				StateFunc: func(v interface{}) string {
					// Vultr always trims the key, so to get a correct diff we
					// also need to trim.
					return strings.TrimSpace(v.(string))
				},
			},
		},
	}
}

func resourceVultrSSHKeyCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*lib.Client)

	name := d.Get("name").(string)
	publicKey := d.Get("public_key").(string)

	log.Printf("[DEBUG] SSH Key create: %s", name)

	key, err := client.CreateSSHKey(name, publicKey)
	if err != nil {
		return fmt.Errorf("Error creating SSH Key: %s", err)
	}

	d.SetId(key.ID)

	log.Printf("[INFO] SSH Key: %s", key.ID)

	return resourceVultrSSHKeyRead(d, meta)
}

func resourceVultrSSHKeyRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*lib.Client)

	// NB the API has no support for getting a key by id, so we have to get'em all.

	keys, err := client.GetSSHKeys()
	if err != nil {
		return fmt.Errorf("Error retrieving SSH key: %s", err)
	}

	var key *lib.SSHKey

	for _, k := range keys {
		if k.ID == d.Id() {
			key = &k
			break
		}
	}

	// if the key is somehow already destroyed mark as succesfully gone.
	if key == nil {
		d.SetId("")
		return nil
	}

	d.Set("name", key.Name)
	d.Set("public_key", key.Key)

	return nil
}

func resourceVultrSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*lib.Client)

	key := lib.SSHKey{
		ID:   d.Id(),
		Name: d.Get("name").(string),
		Key:  d.Get("public_key").(string),
	}

	log.Printf("[DEBUG] SSH key update: %s", key.ID)
	err := client.UpdateSSHKey(key)
	if err != nil {
		return fmt.Errorf("Failed to update SSH key: %s", err)
	}

	return resourceVultrSSHKeyRead(d, meta)
}

func resourceVultrSSHKeyDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*lib.Client)

	log.Printf("[INFO] Deleting SSH key: %s", d.Id())
	err := client.DeleteSSHKey(d.Id())
	if err != nil {
		return fmt.Errorf("Error deleting SSH key: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package service

import (
	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/command_registry"
	"github.com/cloudfoundry/cli/cf/errors"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/simonleung8/flags"
	"github.com/simonleung8/flags/flag"
)

type PurgeServiceInstance struct {
	ui          terminal.UI
	serviceRepo api.ServiceRepository
}

func init() {
	command_registry.Register(&PurgeServiceInstance{})
}

func (cmd *PurgeServiceInstance) MetaData() command_registry.CommandMetadata {
	fs := make(map[string]flags.FlagSet)
	fs["f"] = &cliFlags.BoolFlag{Name: "f", Usage: T("Force deletion without confirmation")}

	return command_registry.CommandMetadata{
		Name:        "purge-service-instance",
		Description: T("Recursively remove a service instance and child objects from Cloud Foundry database without making requests to a service broker"),
		Usage:       T("CF_NAME purge-service-instance SERVICE_INSTANCE") + "\n\n" + cmd.scaryWarningMessage(),
		Flags:       fs,
	}
}

func (cmd *PurgeServiceInstance) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) ([]requirements.Requirement, error) {
	if len(fc.Args()) != 1 {
		cmd.ui.Failed(T("Incorrect Usage. Requires an argument\n\n") + command_registry.Commands.CommandUsage("purge-service-instance"))
	}

	return []requirements.Requirement{
		requirementsFactory.NewLoginRequirement(),
	}, nil
}

func (cmd *PurgeServiceInstance) SetDependency(deps command_registry.Dependency, pluginCall bool) command_registry.Command {
	cmd.ui = deps.Ui
	cmd.serviceRepo = deps.RepoLocator.GetServiceRepository()
	return cmd
}

func (cmd *PurgeServiceInstance) scaryWarningMessage() string {
	return T(`WARNING: This operation assumes that the service broker responsible for this service instance is no longer available or is not responding with a 200 or 410, and the service instance has been deleted, leaving orphan records in Cloud Foundry's database. All knowledge of the service instance will be removed from Cloud Foundry, including service bindings and service keys.`)
}

func (cmd *PurgeServiceInstance) Execute(c flags.FlagContext) {
	instanceName := c.Args()[0]

	instance, err := cmd.serviceRepo.FindInstanceByName(instanceName)
	if err != nil {
		if _, ok := err.(*errors.ModelNotFoundError); ok {
			cmd.ui.Warn(T("Service instance {{.InstanceName}} not found", map[string]interface{}{"InstanceName": instanceName}))
			return
		}

		cmd.ui.Failed(err.Error())
	}

	confirmed := c.Bool("f")
	if !confirmed {
		cmd.ui.Warn(cmd.scaryWarningMessage())
		confirmed = cmd.ui.Confirm(T("Really purge service instance {{.InstanceName}} from Cloud Foundry?",
			map[string]interface{}{"InstanceName": instanceName},
		))
	}

	if !confirmed {
		return
	}
	cmd.ui.Say(T("Purging service {{.InstanceName}}...", map[string]interface{}{"InstanceName": instanceName}))
	err = cmd.serviceRepo.PurgeServiceInstance(instance)
	if err != nil {
		cmd.ui.Failed(err.Error())
	}

	cmd.ui.Ok()
}
<commit_msg>purge-service-instance: confirmed -> force<commit_after>package service

import (
	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/command_registry"
	"github.com/cloudfoundry/cli/cf/errors"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/simonleung8/flags"
	"github.com/simonleung8/flags/flag"
)

type PurgeServiceInstance struct {
	ui          terminal.UI
	serviceRepo api.ServiceRepository
}

func init() {
	command_registry.Register(&PurgeServiceInstance{})
}

func (cmd *PurgeServiceInstance) MetaData() command_registry.CommandMetadata {
	fs := make(map[string]flags.FlagSet)
	fs["f"] = &cliFlags.BoolFlag{Name: "f", Usage: T("Force deletion without confirmation")}

	return command_registry.CommandMetadata{
		Name:        "purge-service-instance",
		Description: T("Recursively remove a service instance and child objects from Cloud Foundry database without making requests to a service broker"),
		Usage:       T("CF_NAME purge-service-instance SERVICE_INSTANCE") + "\n\n" + cmd.scaryWarningMessage(),
		Flags:       fs,
	}
}

func (cmd *PurgeServiceInstance) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) ([]requirements.Requirement, error) {
	if len(fc.Args()) != 1 {
		cmd.ui.Failed(T("Incorrect Usage. Requires an argument\n\n") + command_registry.Commands.CommandUsage("purge-service-instance"))
	}

	return []requirements.Requirement{
		requirementsFactory.NewLoginRequirement(),
	}, nil
}

func (cmd *PurgeServiceInstance) SetDependency(deps command_registry.Dependency, pluginCall bool) command_registry.Command {
	cmd.ui = deps.Ui
	cmd.serviceRepo = deps.RepoLocator.GetServiceRepository()
	return cmd
}

func (cmd *PurgeServiceInstance) scaryWarningMessage() string {
	return T(`WARNING: This operation assumes that the service broker responsible for this service instance is no longer available or is not responding with a 200 or 410, and the service instance has been deleted, leaving orphan records in Cloud Foundry's database. All knowledge of the service instance will be removed from Cloud Foundry, including service bindings and service keys.`)
}

func (cmd *PurgeServiceInstance) Execute(c flags.FlagContext) {
	instanceName := c.Args()[0]

	instance, err := cmd.serviceRepo.FindInstanceByName(instanceName)
	if err != nil {
		if _, ok := err.(*errors.ModelNotFoundError); ok {
			cmd.ui.Warn(T("Service instance {{.InstanceName}} not found", map[string]interface{}{"InstanceName": instanceName}))
			return
		}

		cmd.ui.Failed(err.Error())
	}

	force := c.Bool("f")
	if !force {
		cmd.ui.Warn(cmd.scaryWarningMessage())
		confirmed := cmd.ui.Confirm(T("Really purge service instance {{.InstanceName}} from Cloud Foundry?",
			map[string]interface{}{"InstanceName": instanceName},
		))

		if !confirmed {
			return
		}
	}

	cmd.ui.Say(T("Purging service {{.InstanceName}}...", map[string]interface{}{"InstanceName": instanceName}))
	err = cmd.serviceRepo.PurgeServiceInstance(instance)
	if err != nil {
		cmd.ui.Failed(err.Error())
	}

	cmd.ui.Ok()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

var endpoint string = "http://localhost:8080"
var token string = ""

func Do(method string, postUrl string) (bodyStr string, status int, err error) {
	req, err := http.NewRequest(method, postUrl, nil)
	if err != nil {
		return
	}

	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}

	status = resp.StatusCode

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	bodyStr = string(body)

	return

}

func TestApi(t *testing.T) {

	configFile = "./main_test_config.json"

	go main()
	time.Sleep(time.Second * 1)

	Convey("Test login", t, func(c C) {

		resp, err := http.PostForm(
			endpoint+"/api/login",
			url.Values{
				"username": {"admin"},
				"password": {"test"},
			},
		)

		So(err, ShouldEqual, nil)
		So(resp.StatusCode, ShouldEqual, 200)

		body, err := ioutil.ReadAll(resp.Body)
		So(err, ShouldEqual, nil)

		var objmap map[string]string

		err = json.Unmarshal(body, &objmap)
		So(err, ShouldEqual, nil)

		//So(objmap, ShouldEqual, "")

		tokenFound, gotToken := objmap["token"]

		So(gotToken, ShouldEqual, true)
		token = tokenFound

	})

	Convey("Test scan", t, func(c C) {

		_, status, err := Do("POST", endpoint+"/api/scan")
		So(err, ShouldEqual, nil)
		So(status, ShouldEqual, 200)

	})

	Convey("Tests for albums", t, func(c C) {

		Convey("Test get albums", func(c C) {

			body, status, err := Do("GET", endpoint+"/api/albums")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

			albums := map[string][]map[string]interface{}{}
			//So(body, ShouldEqual, -1)
			//So(body, ShouldEqual, 1)
			err = json.Unmarshal([]byte(body), &albums)
			So(err, ShouldEqual, nil)
			So(len(albums["data"]), ShouldBeGreaterThanOrEqualTo, 1)
			So(albums["data"][0]["name"], ShouldEqual, "Curse the Day (Single versions)")

		})

		Convey("Test single album", func(c C) {

			body, status, err := Do("GET", endpoint+"/api/albums/1")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

			album := map[string]interface{}{}
			//So(body, ShouldEqual, -1)
			err = json.Unmarshal([]byte(body), &album)
			So(err, ShouldEqual, nil)

			So(album["id"], ShouldEqual, 1)
			So(album["name"], ShouldEqual, "Curse the Day (Single versions)")
			So(album["year"], ShouldEqual, 2016)
			So(album["cover"], ShouldEqual, "/images/8806119b2782b3f3eaa32129c20533bd.jpg")

		})

		Convey("Test albums playlist m3u8", func(c C) {

			_, status, err := Do("GET", endpoint+"/api/albums/1/playlist.m3u8?token="+token)
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

		Convey("Test albums album download", func(c C) {

			_, status, err := Do("GET", endpoint+"/api/albums/1/download?token="+token)
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

	})

	Convey("Tests for songs", t, func(c C) {

		Convey("Test song stream", func(c C) {

			_, status, err := Do("GET", endpoint+"/api/songs/1/stream")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

		Convey("Test song played", func(c C) {

			_, status, err := Do("POST", endpoint+"/api/songs/1/played")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

	})

	Convey("Tests for playlists", t, func(c C) {

		Convey("Test playlists", func(c C) {

			_, status, err := Do("GET", endpoint+"/api/playlists")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

		Convey("Test single playlist", func(c C) {

			_, status, err := Do("GET", endpoint+"/api/playlists/1")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 404)

			// Need to add one via post, then test again for code 200
			// and test DELETE of playlist

		})

	})

}
<commit_msg>Removed 'c C'<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

var endpoint string = "http://localhost:8080"
var token string = ""

func Do(method string, postUrl string) (bodyStr string, status int, err error) {
	req, err := http.NewRequest(method, postUrl, nil)
	if err != nil {
		return
	}

	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}

	status = resp.StatusCode

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	bodyStr = string(body)

	return

}

func TestApi(t *testing.T) {

	configFile = "./main_test_config.json"

	go main()
	time.Sleep(time.Second * 1)

	Convey("Test login", t, func() {

		resp, err := http.PostForm(
			endpoint+"/api/login",
			url.Values{
				"username": {"admin"},
				"password": {"test"},
			},
		)

		So(err, ShouldEqual, nil)
		So(resp.StatusCode, ShouldEqual, 200)

		body, err := ioutil.ReadAll(resp.Body)
		So(err, ShouldEqual, nil)

		var objmap map[string]string

		err = json.Unmarshal(body, &objmap)
		So(err, ShouldEqual, nil)

		//So(objmap, ShouldEqual, "")

		tokenFound, gotToken := objmap["token"]

		So(gotToken, ShouldEqual, true)
		token = tokenFound

	})

	Convey("Test scan", t, func() {

		_, status, err := Do("POST", endpoint+"/api/scan")
		So(err, ShouldEqual, nil)
		So(status, ShouldEqual, 200)

	})

	Convey("Tests for albums", t, func() {

		Convey("Test get albums", func() {

			body, status, err := Do("GET", endpoint+"/api/albums")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

			albums := map[string][]map[string]interface{}{}
			//So(body, ShouldEqual, -1)
			//So(body, ShouldEqual, 1)
			err = json.Unmarshal([]byte(body), &albums)
			So(err, ShouldEqual, nil)
			So(len(albums["data"]), ShouldBeGreaterThanOrEqualTo, 1)
			So(albums["data"][0]["name"], ShouldEqual, "Curse the Day (Single versions)")

		})

		Convey("Test single album", func() {

			body, status, err := Do("GET", endpoint+"/api/albums/1")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

			album := map[string]interface{}{}
			//So(body, ShouldEqual, -1)
			err = json.Unmarshal([]byte(body), &album)
			So(err, ShouldEqual, nil)

			So(album["id"], ShouldEqual, 1)
			So(album["name"], ShouldEqual, "Curse the Day (Single versions)")
			So(album["year"], ShouldEqual, 2016)
			So(album["cover"], ShouldEqual, "/images/8806119b2782b3f3eaa32129c20533bd.jpg")

		})

		Convey("Test albums playlist m3u8", func() {

			_, status, err := Do("GET", endpoint+"/api/albums/1/playlist.m3u8?token="+token)
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

		Convey("Test albums album download", func() {

			_, status, err := Do("GET", endpoint+"/api/albums/1/download?token="+token)
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

	})

	Convey("Tests for songs", t, func() {

		Convey("Test song stream", func() {

			_, status, err := Do("GET", endpoint+"/api/songs/1/stream")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

		Convey("Test song played", func() {

			_, status, err := Do("POST", endpoint+"/api/songs/1/played")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

	})

	Convey("Tests for playlists", t, func() {

		Convey("Test playlists", func() {

			_, status, err := Do("GET", endpoint+"/api/playlists")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 200)

		})

		Convey("Test single playlist", func() {

			_, status, err := Do("GET", endpoint+"/api/playlists/1")
			So(err, ShouldEqual, nil)
			So(status, ShouldEqual, 404)

			// Need to add one via post, then test again for code 200
			// and test DELETE of playlist

		})

	})

}
<|endoftext|>