<commit_before>package endpoint

import (
	"net"
	"reflect"
	"strconv"
	"testing"
	"time"

	"github.com/weaveworks/tcptracer-bpf/pkg/tracer"
)

func newMockEbpfTracker() *EbpfTracker {
	return &EbpfTracker{
		ready: true,
		dead:  false,

		openConnections: map[fourTuple]ebpfConnection{},
	}
}

func TestHandleConnection(t *testing.T) {
	var (
		ServerPid  uint32 = 42
		ClientPid  uint32 = 43
		ServerIP          = net.IP("127.0.0.1")
		ClientIP          = net.IP("127.0.0.2")
		ServerPort uint16 = 12345
		ClientPort uint16 = 6789
		NetNS      uint32 = 123456789

		IPv4ConnectEvent = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventConnect,
			Pid:   ClientPid,
			Comm:  "cmd",
			SAddr: ClientIP,
			DAddr: ServerIP,
			SPort: ClientPort,
			DPort: ServerPort,
			NetNS: NetNS,
		}

		IPv4ConnectEbpfConnection = ebpfConnection{
			tuple: fourTuple{
				fromAddr: ClientIP.String(),
				toAddr:   ServerIP.String(),
				fromPort: ClientPort,
				toPort:   ServerPort,
			},
			networkNamespace: strconv.Itoa(int(NetNS)),
			incoming:         false,
			pid:              int(ClientPid),
		}

		IPv4ConnectCloseEvent = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventClose,
			Pid:   ClientPid,
			Comm:  "cmd",
			SAddr: ClientIP,
			DAddr: ServerIP,
			SPort: ClientPort,
			DPort: ServerPort,
			NetNS: NetNS,
		}

		IPv4AcceptEvent = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventAccept,
			Pid:   ServerPid,
			Comm:  "cmd",
			SAddr: ServerIP,
			DAddr: ClientIP,
			SPort: ServerPort,
			DPort: ClientPort,
			NetNS: NetNS,
		}

		IPv4AcceptEbpfConnection = ebpfConnection{
			tuple: fourTuple{
				fromAddr: ServerIP.String(),
				toAddr:   ClientIP.String(),
				fromPort: ServerPort,
				toPort:   ClientPort,
			},
			networkNamespace: strconv.Itoa(int(NetNS)),
			incoming:         true,
			pid:              int(ServerPid),
		}

		IPv4AcceptCloseEvent = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventClose,
			Pid:   ClientPid,
			Comm:  "cmd",
			SAddr: ServerIP,
			DAddr: ClientIP,
			SPort: ServerPort,
			DPort: ClientPort,
			NetNS: NetNS,
		}
	)

	mockEbpfTracker := newMockEbpfTracker()

	tuple := fourTuple{IPv4ConnectEvent.SAddr.String(), IPv4ConnectEvent.DAddr.String(), uint16(IPv4ConnectEvent.SPort), uint16(IPv4ConnectEvent.DPort)}
	mockEbpfTracker.handleConnection(IPv4ConnectEvent.Type, tuple, int(IPv4ConnectEvent.Pid), strconv.FormatUint(uint64(IPv4ConnectEvent.NetNS), 10))
	if !reflect.DeepEqual(mockEbpfTracker.openConnections[tuple], IPv4ConnectEbpfConnection) {
		t.Errorf("Connection mismatch connect event\nTarget connection:%v\nParsed connection:%v",
			IPv4ConnectEbpfConnection, mockEbpfTracker.openConnections[tuple])
	}

	tuple = fourTuple{IPv4ConnectCloseEvent.SAddr.String(), IPv4ConnectCloseEvent.DAddr.String(), uint16(IPv4ConnectCloseEvent.SPort), uint16(IPv4ConnectCloseEvent.DPort)}
	mockEbpfTracker.handleConnection(IPv4ConnectCloseEvent.Type, tuple, int(IPv4ConnectCloseEvent.Pid), strconv.FormatUint(uint64(IPv4ConnectCloseEvent.NetNS), 10))
	if len(mockEbpfTracker.openConnections) != 0 {
		t.Errorf("Connection mismatch close event\nConnection to close:%v",
			mockEbpfTracker.openConnections[tuple])
	}

	mockEbpfTracker = newMockEbpfTracker()

	tuple = fourTuple{IPv4AcceptEvent.SAddr.String(), IPv4AcceptEvent.DAddr.String(), uint16(IPv4AcceptEvent.SPort), uint16(IPv4AcceptEvent.DPort)}
	mockEbpfTracker.handleConnection(IPv4AcceptEvent.Type, tuple, int(IPv4AcceptEvent.Pid), strconv.FormatUint(uint64(IPv4AcceptEvent.NetNS), 10))
	if !reflect.DeepEqual(mockEbpfTracker.openConnections[tuple], IPv4AcceptEbpfConnection) {
		t.Errorf("Connection mismatch connect event\nTarget connection:%v\nParsed connection:%v",
			IPv4AcceptEbpfConnection, mockEbpfTracker.openConnections[tuple])
	}

	tuple = fourTuple{IPv4AcceptCloseEvent.SAddr.String(), IPv4AcceptCloseEvent.DAddr.String(), uint16(IPv4AcceptCloseEvent.SPort), uint16(IPv4AcceptCloseEvent.DPort)}
	mockEbpfTracker.handleConnection(IPv4AcceptCloseEvent.Type, tuple, int(IPv4AcceptCloseEvent.Pid), strconv.FormatUint(uint64(IPv4AcceptCloseEvent.NetNS), 10))

	if len(mockEbpfTracker.openConnections) != 0 {
		t.Errorf("Connection mismatch close event\nConnection to close:%v",
			mockEbpfTracker.openConnections)
	}
}

func TestWalkConnections(t *testing.T) {
	var (
		cnt         int
		activeTuple = fourTuple{
			fromAddr: "",
			toAddr:   "",
			fromPort: 0,
			toPort:   0,
		}

		inactiveTuple = fourTuple{
			fromAddr: "",
			toAddr:   "",
			fromPort: 0,
			toPort:   0,
		}
	)
	mockEbpfTracker := newMockEbpfTracker()
	mockEbpfTracker.openConnections[activeTuple] = ebpfConnection{
		tuple:            activeTuple,
		networkNamespace: "12345",
		incoming:         true,
		pid:              0,
	}
	mockEbpfTracker.closedConnections = append(mockEbpfTracker.closedConnections,
		ebpfConnection{
			tuple:            inactiveTuple,
			networkNamespace: "12345",
			incoming:         false,
			pid:              0,
		})
	mockEbpfTracker.walkConnections(func(e ebpfConnection) {
		cnt++
	})
	if cnt != 2 {
		t.Errorf("walkConnections found %v instead of 2 connections", cnt)
	}
}

func TestInvalidTimeStampDead(t *testing.T) {
	var (
		cnt        int
		ClientPid  uint32 = 43
		ServerIP          = net.IP("127.0.0.1")
		ClientIP          = net.IP("127.0.0.2")
		ServerPort uint16 = 12345
		ClientPort uint16 = 6789
		NetNS      uint32 = 123456789
		event             = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventConnect,
			Pid:   ClientPid,
			Comm:  "cmd",
			SAddr: ClientIP,
			DAddr: ServerIP,
			SPort: ClientPort,
			DPort: ServerPort,
			NetNS: NetNS,
		}
	)
	mockEbpfTracker := newMockEbpfTracker()
	event.Timestamp = 0
	mockEbpfTracker.TCPEventV4(event)
	event2 := event
	event2.SPort = 1
	event2.Timestamp = 2
	mockEbpfTracker.TCPEventV4(event2)
	mockEbpfTracker.walkConnections(func(e ebpfConnection) {
		cnt++
	})
	if cnt != 2 {
		t.Errorf("walkConnections found %v instead of 2 connections", cnt)
	}
	if mockEbpfTracker.isDead() {
		t.Errorf("expected ebpfTracker to be alive after events with valid order")
	}
	cnt = 0
	event.Timestamp = 1
	mockEbpfTracker.TCPEventV4(event)
	mockEbpfTracker.walkConnections(func(e ebpfConnection) {
		cnt++
	})
	if cnt != 2 {
		t.Errorf("walkConnections found %v instead of 2 connections", cnt)
	}
	// EbpfTracker is marked as dead asynchronously.
	deadline := time.Now().Add(5 * time.Second)
	for time.Now().Before(deadline) {
		if mockEbpfTracker.isDead() {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
	if !mockEbpfTracker.isDead() {
		t.Errorf("expected ebpfTracker to be set to dead after events with wrong order")
	}
}
<commit_msg>ebpf: add tests for `isKernelSupported()`<commit_after>package endpoint

import (
	"net"
	"reflect"
	"strconv"
	"testing"
	"time"

	"github.com/weaveworks/tcptracer-bpf/pkg/tracer"

	"github.com/weaveworks/scope/probe/host"
)

func newMockEbpfTracker() *EbpfTracker {
	return &EbpfTracker{
		ready: true,
		dead:  false,

		openConnections: map[fourTuple]ebpfConnection{},
	}
}

func TestHandleConnection(t *testing.T) {
	var (
		ServerPid  uint32 = 42
		ClientPid  uint32 = 43
		ServerIP          = net.IP("127.0.0.1")
		ClientIP          = net.IP("127.0.0.2")
		ServerPort uint16 = 12345
		ClientPort uint16 = 6789
		NetNS      uint32 = 123456789

		IPv4ConnectEvent = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventConnect,
			Pid:   ClientPid,
			Comm:  "cmd",
			SAddr: ClientIP,
			DAddr: ServerIP,
			SPort: ClientPort,
			DPort: ServerPort,
			NetNS: NetNS,
		}

		IPv4ConnectEbpfConnection = ebpfConnection{
			tuple: fourTuple{
				fromAddr: ClientIP.String(),
				toAddr:   ServerIP.String(),
				fromPort: ClientPort,
				toPort:   ServerPort,
			},
			networkNamespace: strconv.Itoa(int(NetNS)),
			incoming:         false,
			pid:              int(ClientPid),
		}

		IPv4ConnectCloseEvent = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventClose,
			Pid:   ClientPid,
			Comm:  "cmd",
			SAddr: ClientIP,
			DAddr: ServerIP,
			SPort: ClientPort,
			DPort: ServerPort,
			NetNS: NetNS,
		}

		IPv4AcceptEvent = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventAccept,
			Pid:   ServerPid,
			Comm:  "cmd",
			SAddr: ServerIP,
			DAddr: ClientIP,
			SPort: ServerPort,
			DPort: ClientPort,
			NetNS: NetNS,
		}

		IPv4AcceptEbpfConnection = ebpfConnection{
			tuple: fourTuple{
				fromAddr: ServerIP.String(),
				toAddr:   ClientIP.String(),
				fromPort: ServerPort,
				toPort:   ClientPort,
			},
			networkNamespace: strconv.Itoa(int(NetNS)),
			incoming:         true,
			pid:              int(ServerPid),
		}

		IPv4AcceptCloseEvent = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventClose,
			Pid:   ClientPid,
			Comm:  "cmd",
			SAddr: ServerIP,
			DAddr: ClientIP,
			SPort: ServerPort,
			DPort: ClientPort,
			NetNS: NetNS,
		}
	)

	mockEbpfTracker := newMockEbpfTracker()

	tuple := fourTuple{IPv4ConnectEvent.SAddr.String(), IPv4ConnectEvent.DAddr.String(), uint16(IPv4ConnectEvent.SPort), uint16(IPv4ConnectEvent.DPort)}
	mockEbpfTracker.handleConnection(IPv4ConnectEvent.Type, tuple, int(IPv4ConnectEvent.Pid), strconv.FormatUint(uint64(IPv4ConnectEvent.NetNS), 10))
	if !reflect.DeepEqual(mockEbpfTracker.openConnections[tuple], IPv4ConnectEbpfConnection) {
		t.Errorf("Connection mismatch connect event\nTarget connection:%v\nParsed connection:%v",
			IPv4ConnectEbpfConnection, mockEbpfTracker.openConnections[tuple])
	}

	tuple = fourTuple{IPv4ConnectCloseEvent.SAddr.String(), IPv4ConnectCloseEvent.DAddr.String(), uint16(IPv4ConnectCloseEvent.SPort), uint16(IPv4ConnectCloseEvent.DPort)}
	mockEbpfTracker.handleConnection(IPv4ConnectCloseEvent.Type, tuple, int(IPv4ConnectCloseEvent.Pid), strconv.FormatUint(uint64(IPv4ConnectCloseEvent.NetNS), 10))
	if len(mockEbpfTracker.openConnections) != 0 {
		t.Errorf("Connection mismatch close event\nConnection to close:%v",
			mockEbpfTracker.openConnections[tuple])
	}

	mockEbpfTracker = newMockEbpfTracker()

	tuple = fourTuple{IPv4AcceptEvent.SAddr.String(), IPv4AcceptEvent.DAddr.String(), uint16(IPv4AcceptEvent.SPort), uint16(IPv4AcceptEvent.DPort)}
	mockEbpfTracker.handleConnection(IPv4AcceptEvent.Type, tuple, int(IPv4AcceptEvent.Pid), strconv.FormatUint(uint64(IPv4AcceptEvent.NetNS), 10))
	if !reflect.DeepEqual(mockEbpfTracker.openConnections[tuple], IPv4AcceptEbpfConnection) {
		t.Errorf("Connection mismatch connect event\nTarget connection:%v\nParsed connection:%v",
			IPv4AcceptEbpfConnection, mockEbpfTracker.openConnections[tuple])
	}

	tuple = fourTuple{IPv4AcceptCloseEvent.SAddr.String(), IPv4AcceptCloseEvent.DAddr.String(), uint16(IPv4AcceptCloseEvent.SPort), uint16(IPv4AcceptCloseEvent.DPort)}
	mockEbpfTracker.handleConnection(IPv4AcceptCloseEvent.Type, tuple, int(IPv4AcceptCloseEvent.Pid), strconv.FormatUint(uint64(IPv4AcceptCloseEvent.NetNS), 10))

	if len(mockEbpfTracker.openConnections) != 0 {
		t.Errorf("Connection mismatch close event\nConnection to close:%v",
			mockEbpfTracker.openConnections)
	}
}

func TestWalkConnections(t *testing.T) {
	var (
		cnt         int
		activeTuple = fourTuple{
			fromAddr: "",
			toAddr:   "",
			fromPort: 0,
			toPort:   0,
		}

		inactiveTuple = fourTuple{
			fromAddr: "",
			toAddr:   "",
			fromPort: 0,
			toPort:   0,
		}
	)
	mockEbpfTracker := newMockEbpfTracker()
	mockEbpfTracker.openConnections[activeTuple] = ebpfConnection{
		tuple:            activeTuple,
		networkNamespace: "12345",
		incoming:         true,
		pid:              0,
	}
	mockEbpfTracker.closedConnections = append(mockEbpfTracker.closedConnections,
		ebpfConnection{
			tuple:            inactiveTuple,
			networkNamespace: "12345",
			incoming:         false,
			pid:              0,
		})
	mockEbpfTracker.walkConnections(func(e ebpfConnection) {
		cnt++
	})
	if cnt != 2 {
		t.Errorf("walkConnections found %v instead of 2 connections", cnt)
	}
}

func TestInvalidTimeStampDead(t *testing.T) {
	var (
		cnt        int
		ClientPid  uint32 = 43
		ServerIP          = net.IP("127.0.0.1")
		ClientIP          = net.IP("127.0.0.2")
		ServerPort uint16 = 12345
		ClientPort uint16 = 6789
		NetNS      uint32 = 123456789
		event             = tracer.TcpV4{
			CPU:   0,
			Type:  tracer.EventConnect,
			Pid:   ClientPid,
			Comm:  "cmd",
			SAddr: ClientIP,
			DAddr: ServerIP,
			SPort: ClientPort,
			DPort: ServerPort,
			NetNS: NetNS,
		}
	)
	mockEbpfTracker := newMockEbpfTracker()
	event.Timestamp = 0
	mockEbpfTracker.TCPEventV4(event)
	event2 := event
	event2.SPort = 1
	event2.Timestamp = 2
	mockEbpfTracker.TCPEventV4(event2)
	mockEbpfTracker.walkConnections(func(e ebpfConnection) {
		cnt++
	})
	if cnt != 2 {
		t.Errorf("walkConnections found %v instead of 2 connections", cnt)
	}
	if mockEbpfTracker.isDead() {
		t.Errorf("expected ebpfTracker to be alive after events with valid order")
	}
	cnt = 0
	event.Timestamp = 1
	mockEbpfTracker.TCPEventV4(event)
	mockEbpfTracker.walkConnections(func(e ebpfConnection) {
		cnt++
	})
	if cnt != 2 {
		t.Errorf("walkConnections found %v instead of 2 connections", cnt)
	}
	// EbpfTracker is marked as dead asynchronously.
	deadline := time.Now().Add(5 * time.Second)
	for time.Now().Before(deadline) {
		if mockEbpfTracker.isDead() {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
	if !mockEbpfTracker.isDead() {
		t.Errorf("expected ebpfTracker to be set to dead after events with wrong order")
	}
}

func TestIsKernelSupported(t *testing.T) {
	var release, version string
	oldGetKernelReleaseAndVersion := host.GetKernelReleaseAndVersion
	defer func() {
		host.GetKernelReleaseAndVersion = oldGetKernelReleaseAndVersion
	}()
	host.GetKernelReleaseAndVersion = func() (string, string, error) { return release, version, nil }
	testVersions := []struct {
		release   string
		version   string
		supported bool
	}{
		{
			"4.1",
			"",
			false,
		},
		{
			"4.4",
			"",
			true,
		},
		{
			"4.4-custom",
			"",
			true,
		},
		{
			"4.4.127",
			"",
			true,
		},
		{
			"4.4.0-119-generic",
			"#143-Ubuntu SMP Mon Apr 2 16:08:24 UTC 2018",
			false,
		},
		{
			"4.4.0-116-generic",
			"#140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018",
			true,
		},
		{
			"4.13.0-38-generic",
			"#43-Ubuntu SMP Wed Mar 14 15:20:44 UTC 2018",
			true,
		},
		{
			"4.13.0-119-generic",
			"#43-Ubuntu SMP Wed Apr 1 00:00:00 UTC 2018",
			true,
		},
		{
			"4.9.0-6-amd64",
			"#1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02)",
			true,
		},
	}
	for _, tv := range testVersions {
		release = tv.release
		version = tv.version
		err := isKernelSupported()
		if tv.supported && err != nil {
			t.Errorf("expected kernel release %q version %q to be supported but got error: %v", release, version, err)
		}
		if !tv.supported && err == nil {
			t.Errorf("expected kernel release %q version %q to not be supported", release, version)
		}
	}
}
<|endoftext|>


<commit_before>// +build !linux,!darwin,!windows,!s390,!ppc64le

// limited GOOS due to modernc.org/libc/unistd

package sqlite

func init() {
	// filer.Stores = append(filer.Stores, &SqliteStore{})
}
<commit_msg>also failed on mips64<commit_after>// +build !linux,!darwin,!windows,!s390,!ppc64le,!mips64

// limited GOOS due to modernc.org/libc/unistd

package sqlite

func init() {
	// filer.Stores = append(filer.Stores, &SqliteStore{})
}
<|endoftext|>


<commit_before>// Code generated by MockGen. DO NOT EDIT.
// Source: account.go

// Package mocks is a generated GoMock package.
package mocks

import (
	reflect "reflect"

	do "github.com/digitalocean/doctl/do"
	gomock "github.com/golang/mock/gomock"
)

// MockAccountService is a mock of AccountService interface
type MockAccountService struct {
	ctrl     *gomock.Controller
	recorder *MockAccountServiceMockRecorder
}

// MockAccountServiceMockRecorder is the mock recorder for MockAccountService
type MockAccountServiceMockRecorder struct {
	mock *MockAccountService
}

// NewMockAccountService creates a new mock instance
func NewMockAccountService(ctrl *gomock.Controller) *MockAccountService {
	mock := &MockAccountService{ctrl: ctrl}
	mock.recorder = &MockAccountServiceMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockAccountService) EXPECT() *MockAccountServiceMockRecorder {
	return m.recorder
}

// Get mocks base method
func (m *MockAccountService) Get() (*do.Account, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "Get")
	ret0, _ := ret[0].(*do.Account)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// Get indicates an expected call of Get
func (mr *MockAccountServiceMockRecorder) Get() *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockAccountService)(nil).Get))
}

// RateLimit mocks base method
func (m *MockAccountService) RateLimit() (*do.RateLimit, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "RateLimit")
	ret0, _ := ret[0].(*do.RateLimit)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// RateLimit indicates an expected call of RateLimit
func (mr *MockAccountServiceMockRecorder) RateLimit() *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RateLimit", reflect.TypeOf((*MockAccountService)(nil).RateLimit))
}
<commit_msg>Regenerate mocks<commit_after>// Code generated by MockGen. DO NOT EDIT.
// Source: account.go

// Package mocks is a generated GoMock package.
package mocks

import (
	do "github.com/digitalocean/doctl/do"
	gomock "github.com/golang/mock/gomock"
	reflect "reflect"
)

// MockAccountService is a mock of AccountService interface
type MockAccountService struct {
	ctrl     *gomock.Controller
	recorder *MockAccountServiceMockRecorder
}

// MockAccountServiceMockRecorder is the mock recorder for MockAccountService
type MockAccountServiceMockRecorder struct {
	mock *MockAccountService
}

// NewMockAccountService creates a new mock instance
func NewMockAccountService(ctrl *gomock.Controller) *MockAccountService {
	mock := &MockAccountService{ctrl: ctrl}
	mock.recorder = &MockAccountServiceMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockAccountService) EXPECT() *MockAccountServiceMockRecorder {
	return m.recorder
}

// Get mocks base method
func (m *MockAccountService) Get() (*do.Account, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "Get")
	ret0, _ := ret[0].(*do.Account)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// Get indicates an expected call of Get
func (mr *MockAccountServiceMockRecorder) Get() *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockAccountService)(nil).Get))
}

// RateLimit mocks base method
func (m *MockAccountService) RateLimit() (*do.RateLimit, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "RateLimit")
	ret0, _ := ret[0].(*do.RateLimit)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// RateLimit indicates an expected call of RateLimit
func (mr *MockAccountServiceMockRecorder) RateLimit() *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RateLimit", reflect.TypeOf((*MockAccountService)(nil).RateLimit))
}
<|endoftext|>


<commit_before>package processors

import (
	"strconv"

	"github.com/nanobox-io/nanobox/models"
)

func ConfigureSet(key, val string) error {
	config, _ := models.LoadConfig()

	switch key {
	case "provider":
		config.Provider = val
	case "mount-type", "mount_type":
		config.MountType = val
	case "netfs_mount_opts", "netfs-mount-opts", "mount_options", "mount-options":
		config.NetfsMountOpts = val
	case "cpus", "CPUs":
		config.CPUs, _ = strconv.Atoi(val)
	case "ram", "RAM":
		config.RAM, _ = strconv.Atoi(val)
	case "disk":
		config.Disk, _ = strconv.Atoi(val)
	case "external_network_space", "external-network-space":
		config.ExternalNetworkSpace = val
	case "docker_machine_network_space", "docker-machine-network-space":
		config.DockerMachineNetworkSpace = val
	case "native_network_space", "native-network-space":
		config.NativeNetworkSpace = val
	case "ssh_key", "ssh-key":
		config.SshKey = val
	case "ssh_encrypted_keys", "ssh-encrypted-keys":
		config.SshEncryptedKeys = val == "true" || val == "t" || val == "1"
	case "lock_port", "lock-port":
		config.LockPort, _ = strconv.Atoi(val)
	case "ci-mode", "ci_mode":
		config.CIMode = val == "true" || val == "t" || val == "1"
	case "ci-sync-verbose", "ci_sync_verbose":
		config.CISyncVerbose = val == "true" || val == "t" || val == "1"
	case "anonymous":
		config.Anonymous = val == "true" || val == "t" || val == "1"
	}

	return config.Save()
}
<commit_msg>Add a configuration key<commit_after>package processors

import (
	"strconv"

	"github.com/nanobox-io/nanobox/models"
)

func ConfigureSet(key, val string) error {
	config, _ := models.LoadConfig()

	switch key {
	case "provider":
		config.Provider = val
	case "mount-type", "mount_type":
		config.MountType = val
	case "netfs_mount_opts", "netfs-mount-opts", "mount_options", "mount-options":
		config.NetfsMountOpts = val
	case "cpus", "CPUs":
		config.CPUs, _ = strconv.Atoi(val)
	case "ram", "RAM":
		config.RAM, _ = strconv.Atoi(val)
	case "disk":
		config.Disk, _ = strconv.Atoi(val)
	case "external_network_space", "external-network-space":
		config.ExternalNetworkSpace = val
	case "docker_machine_network_space", "docker-machine-network-space":
		config.DockerMachineNetworkSpace = val
	case "native_network_space", "native-network-space":
		config.NativeNetworkSpace = val
	case "ssh_key", "ssh-key":
		config.SshKey = val
	case "ssh_encrypted_keys", "ssh-encrypted-keys", "use_encrypted_keys", "use-encrypted-keys":
		config.SshEncryptedKeys = val == "true" || val == "t" || val == "1"
	case "lock_port", "lock-port":
		config.LockPort, _ = strconv.Atoi(val)
	case "ci-mode", "ci_mode":
		config.CIMode = val == "true" || val == "t" || val == "1"
	case "ci-sync-verbose", "ci_sync_verbose":
		config.CISyncVerbose = val == "true" || val == "t" || val == "1"
	case "anonymous":
		config.Anonymous = val == "true" || val == "t" || val == "1"
	}

	return config.Save()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	docker "github.com/fsouza/go-dockerclient"
)

func inspectContainer(containerNameOrID string) (*docker.Container, error) {
	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return nil, fmt.Errorf("unable to connect to docker: %s", err)
	}

	container, err := c.InspectContainer(containerNameOrID)
	if err != nil {
		return nil, fmt.Errorf("unable to inspect container %s: %s", containerNameOrID, err)
	}
	return container, nil
}

func containerPid(containerID string) (int, error) {
	container, err := inspectContainer(containerID)
	if err != nil {
		return 0, err

	}

	if container.State.Pid == 0 {
		return 0, fmt.Errorf("container %s not running", containerID)
	}
	return container.State.Pid, nil
}

func containerID(args []string) error {
	if len(args) < 1 {
		cmdUsage("container-id", "<container-name-or-short-id>")
	}

	container, err := inspectContainer(args[0])
	if err != nil {
		return err
	}

	fmt.Print(container.ID)
	return nil
}

func containerState(args []string) error {
	if len(args) < 1 {
		cmdUsage("container-state", "<container-id> [<image-prefix>]")
	}

	container, err := inspectContainer(args[0])
	if err != nil {
		return err
	}

	if len(args) == 1 {
		fmt.Print(container.State.StateString())
	} else {
		imagePrefix := args[1]
		if strings.HasPrefix(container.Config.Image, imagePrefix) {
			fmt.Print(container.State.StateString())
		} else {
			if container.State.Running {
				fmt.Print("running image mismatch: ", container.Config.Image)
			} else {
				fmt.Print("image mismatch: ", container.Config.Image)
			}
		}
	}
	return nil
}

func containerFQDN(args []string) error {
	if len(args) < 1 {
		cmdUsage("container-fqdn", "<container-id>")
	}

	container, err := inspectContainer(args[0])
	if err != nil {
		return err
	}

	fmt.Print(container.Config.Hostname, ".", container.Config.Domainname)
	return nil
}

func runContainer(args []string) error {
	env := []string{}
	name := ""
	net := ""
	pid := ""
	privileged := false
	restart := docker.NeverRestart()
	volumes := []string{}
	volumesFrom := []string{}

	done := false
	for i := 0; i < len(args) && !done; {
		switch args[i] {
		case "-e", "--env":
			if v := os.Getenv(args[i+1]); v != "" {
				env = append(env, args[i+1]+"="+v)
			} else {
				env = append(env, args[i+1])
			}
			args = append(args[:i], args[i+2:]...)
		case "--name":
			name = args[i+1]
			args = append(args[:i], args[i+2:]...)
		case "--net":
			net = args[i+1]
			args = append(args[:i], args[i+2:]...)
		case "--pid":
			pid = args[i+1]
			args = append(args[:i], args[i+2:]...)
		case "--privileged":
			privileged = true
			args = append(args[:i], args[i+1:]...)
		case "--restart":
			restart = docker.RestartPolicy{Name: args[i+1]}
			args = append(args[:i], args[i+2:]...)
		case "-v", "--volume":
			// Must dedup binds, otherwise, container fails creation
			skip := false
			for _, v := range volumes {
				if v == args[i+1] {
					skip = true
				}
			}
			if !skip {
				volumes = append(volumes, args[i+1])
			}
			args = append(args[:i], args[i+2:]...)
		case "--volumes-from":
			volumesFrom = append(volumesFrom, args[i+1])
			args = append(args[:i], args[i+2:]...)
		default:
			done = true
		}
	}

	if len(args) < 2 {
		cmdUsage("run-container", `[options] <image> <cmd> [[<cmd-options>] <cmd-arg1> [<cmd-arg2> ...]]

  -e, --env list                       Set environment variables
      --name string                    Assign a name to the container
      --net string                     Network Mode
      --pid string                     PID namespace to use
      --privileged                     Give extended privileges to this container
      --restart string                 Restart policy to apply when a container exits (default "no")
  -v, --volume list                    Bind mount a volume
      --volumes-from list              Mount volumes from the specified container(s)
`)
	}

	image := args[0]
	cmds := args[1:]

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	config := docker.Config{Image: image, Env: env, Cmd: cmds}
	hostConfig := docker.HostConfig{NetworkMode: net, PidMode: pid, Privileged: privileged, RestartPolicy: restart, Binds: volumes, VolumesFrom: volumesFrom}
	container, err := c.CreateContainer(docker.CreateContainerOptions{Name: name, Config: &config, HostConfig: &hostConfig})
	if err != nil {
		return fmt.Errorf("unable to create container: %s", err)
	}

	err = c.StartContainer(container.ID, &hostConfig)
	if err != nil {
		return fmt.Errorf("unable to start container: %s", err)
	}

	fmt.Print(container.ID)
	return nil
}

func listContainers(args []string) error {
	if len(args) > 1 {
		cmdUsage("list-containers", "[<label>]")
	}

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	opts := docker.ListContainersOptions{All: true}
	if len(args) == 1 {
		label := args[0]
		opts.Filters = map[string][]string{"label": {label}}
	}

	containers, err := c.ListContainers(opts)
	if err != nil {
		return fmt.Errorf("unable to list containers: %s", err)
	}

	for _, container := range containers {
		fmt.Println(container.ID)
	}
	return nil
}

func stopContainer(args []string) error {
	if len(args) < 1 {
		cmdUsage("stop-container", "<container-id> [<container-id2> ...]")
	}

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	for _, containerID := range args {
		err = c.StopContainer(containerID, 10)
		if err != nil {
			return fmt.Errorf("unable to stop container %s: %s", containerID, err)
		}
	}
	return nil
}

func killContainer(args []string) error {
	if len(args) < 1 {
		cmdUsage("kill-container", "<container-id> [<container-id2> ...]")
	}

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	for _, containerID := range args {
		err = c.KillContainer(docker.KillContainerOptions{ID: containerID, Signal: docker.SIGKILL})
		if err != nil {
			return fmt.Errorf("unable to stop container %s: %s", containerID, err)
		}
	}
	return nil
}

func removeContainer(args []string) error {
	if len(args) < 1 {
		cmdUsage("remove-container", "[-f | --force]  [-v | --volumes] <container-id> [<container-id2> ...]")
	}

	force := false
	volumes := false
	for i := 0; i < len(args); {
		switch args[i] {
		case "--force":
		case "-f":
			force = true
			args = append(args[:i], args[i+1:]...)
		case "--volumes":
		case "-v":
			volumes = true
			args = append(args[:i], args[i+1:]...)
		default:
			i++
		}
	}

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	for _, containerID := range args {
		err = c.RemoveContainer(docker.RemoveContainerOptions{
			ID: containerID, Force: force, RemoveVolumes: volumes})
		if err != nil {
			return fmt.Errorf("unable to stop container %s: %s", containerID, err)
		}
	}
	return nil
}
<commit_msg>Support --label flag on run-container command<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	docker "github.com/fsouza/go-dockerclient"
)

func inspectContainer(containerNameOrID string) (*docker.Container, error) {
	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return nil, fmt.Errorf("unable to connect to docker: %s", err)
	}

	container, err := c.InspectContainer(containerNameOrID)
	if err != nil {
		return nil, fmt.Errorf("unable to inspect container %s: %s", containerNameOrID, err)
	}
	return container, nil
}

func containerPid(containerID string) (int, error) {
	container, err := inspectContainer(containerID)
	if err != nil {
		return 0, err

	}

	if container.State.Pid == 0 {
		return 0, fmt.Errorf("container %s not running", containerID)
	}
	return container.State.Pid, nil
}

func containerID(args []string) error {
	if len(args) < 1 {
		cmdUsage("container-id", "<container-name-or-short-id>")
	}

	container, err := inspectContainer(args[0])
	if err != nil {
		return err
	}

	fmt.Print(container.ID)
	return nil
}

func containerState(args []string) error {
	if len(args) < 1 {
		cmdUsage("container-state", "<container-id> [<image-prefix>]")
	}

	container, err := inspectContainer(args[0])
	if err != nil {
		return err
	}

	if len(args) == 1 {
		fmt.Print(container.State.StateString())
	} else {
		imagePrefix := args[1]
		if strings.HasPrefix(container.Config.Image, imagePrefix) {
			fmt.Print(container.State.StateString())
		} else {
			if container.State.Running {
				fmt.Print("running image mismatch: ", container.Config.Image)
			} else {
				fmt.Print("image mismatch: ", container.Config.Image)
			}
		}
	}
	return nil
}

func containerFQDN(args []string) error {
	if len(args) < 1 {
		cmdUsage("container-fqdn", "<container-id>")
	}

	container, err := inspectContainer(args[0])
	if err != nil {
		return err
	}

	fmt.Print(container.Config.Hostname, ".", container.Config.Domainname)
	return nil
}

func runContainer(args []string) error {
	env := []string{}
	labels := map[string]string{}
	name := ""
	net := ""
	pid := ""
	privileged := false
	restart := docker.NeverRestart()
	volumes := []string{}
	volumesFrom := []string{}

	done := false
	for i := 0; i < len(args) && !done; {
		switch args[i] {
		case "-e", "--env":
			if v := os.Getenv(args[i+1]); v != "" {
				env = append(env, args[i+1]+"="+v)
			} else {
				env = append(env, args[i+1])
			}
			args = append(args[:i], args[i+2:]...)
		case "-l", "--label":
			key, value := parseLabel(args[i+1])
			labels[key] = value
			args = append(args[:i], args[i+2:]...)
		case "--name":
			name = args[i+1]
			args = append(args[:i], args[i+2:]...)
		case "--net":
			net = args[i+1]
			args = append(args[:i], args[i+2:]...)
		case "--pid":
			pid = args[i+1]
			args = append(args[:i], args[i+2:]...)
		case "--privileged":
			privileged = true
			args = append(args[:i], args[i+1:]...)
		case "--restart":
			restart = docker.RestartPolicy{Name: args[i+1]}
			args = append(args[:i], args[i+2:]...)
		case "-v", "--volume":
			// Must dedup binds, otherwise, container fails creation
			skip := false
			for _, v := range volumes {
				if v == args[i+1] {
					skip = true
				}
			}
			if !skip {
				volumes = append(volumes, args[i+1])
			}
			args = append(args[:i], args[i+2:]...)
		case "--volumes-from":
			volumesFrom = append(volumesFrom, args[i+1])
			args = append(args[:i], args[i+2:]...)
		default:
			done = true
		}
	}

	if len(args) < 2 {
		cmdUsage("run-container", `[options] <image> <cmd> [[<cmd-options>] <cmd-arg1> [<cmd-arg2> ...]]

  -e, --env list                       Set environment variables
      --name string                    Assign a name to the container
      --net string                     Network Mode
      --pid string                     PID namespace to use
      --privileged                     Give extended privileges to this container
      --restart string                 Restart policy to apply when a container exits (default "no")
  -v, --volume list                    Bind mount a volume
      --volumes-from list              Mount volumes from the specified container(s)
`)
	}

	image := args[0]
	cmds := args[1:]

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	config := docker.Config{Image: image, Env: env, Cmd: cmds, Labels: labels}
	hostConfig := docker.HostConfig{NetworkMode: net, PidMode: pid, Privileged: privileged, RestartPolicy: restart, Binds: volumes, VolumesFrom: volumesFrom}
	container, err := c.CreateContainer(docker.CreateContainerOptions{Name: name, Config: &config, HostConfig: &hostConfig})
	if err != nil {
		return fmt.Errorf("unable to create container: %s", err)
	}

	err = c.StartContainer(container.ID, &hostConfig)
	if err != nil {
		return fmt.Errorf("unable to start container: %s", err)
	}

	fmt.Print(container.ID)
	return nil
}

func parseLabel(s string) (key, value string) {
	pos := strings.Index(s, "=")
	if pos == -1 { // no value - set it to blank
		return s, ""
	}
	return s[:pos], s[pos+1:]
}

func listContainers(args []string) error {
	if len(args) > 1 {
		cmdUsage("list-containers", "[<label>]")
	}

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	opts := docker.ListContainersOptions{All: true}
	if len(args) == 1 {
		label := args[0]
		opts.Filters = map[string][]string{"label": {label}}
	}

	containers, err := c.ListContainers(opts)
	if err != nil {
		return fmt.Errorf("unable to list containers: %s", err)
	}

	for _, container := range containers {
		fmt.Println(container.ID)
	}
	return nil
}

func stopContainer(args []string) error {
	if len(args) < 1 {
		cmdUsage("stop-container", "<container-id> [<container-id2> ...]")
	}

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	for _, containerID := range args {
		err = c.StopContainer(containerID, 10)
		if err != nil {
			return fmt.Errorf("unable to stop container %s: %s", containerID, err)
		}
	}
	return nil
}

func killContainer(args []string) error {
	if len(args) < 1 {
		cmdUsage("kill-container", "<container-id> [<container-id2> ...]")
	}

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	for _, containerID := range args {
		err = c.KillContainer(docker.KillContainerOptions{ID: containerID, Signal: docker.SIGKILL})
		if err != nil {
			return fmt.Errorf("unable to stop container %s: %s", containerID, err)
		}
	}
	return nil
}

func removeContainer(args []string) error {
	if len(args) < 1 {
		cmdUsage("remove-container", "[-f | --force]  [-v | --volumes] <container-id> [<container-id2> ...]")
	}

	force := false
	volumes := false
	for i := 0; i < len(args); {
		switch args[i] {
		case "--force":
		case "-f":
			force = true
			args = append(args[:i], args[i+1:]...)
		case "--volumes":
		case "-v":
			volumes = true
			args = append(args[:i], args[i+1:]...)
		default:
			i++
		}
	}

	c, err := docker.NewVersionedClientFromEnv("1.18")
	if err != nil {
		return fmt.Errorf("unable to connect to docker: %s", err)
	}

	for _, containerID := range args {
		err = c.RemoveContainer(docker.RemoveContainerOptions{
			ID: containerID, Force: force, RemoveVolumes: volumes})
		if err != nil {
			return fmt.Errorf("unable to stop container %s: %s", containerID, err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package pingdom

import (
	"testing"

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

func TestHttpCheckPutParams(t *testing.T) {
	check := HttpCheck{
		Name:     "fake check",
		Hostname: "example.com",
		Url:      "/foo",
		RequestHeaders: map[string]string{
			"User-Agent": "Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
			"Pragma":     "no-cache",
		},
		Username:       "user",
		Password:       "pass",
		IntegrationIds: []int{33333333, 44444444},
		UserIds:        []int{123, 456},
		TeamIds:        []int{789},
	}
	want := map[string]string{
		"name":             "fake check",
		"host":             "example.com",
		"paused":           "false",
		"resolution":       "0",
		"notifyagainevery": "0",
		"notifywhenbackup": "false",
		"url":              "/foo",
		"requestheader0":   "Pragma:no-cache",
		"requestheader1":   "User-Agent:Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
		"auth":             "user:pass",
		"encryption":       "false",
		"shouldnotcontain": "",
		"postdata":         "",
		"integrationids":   "33333333,44444444",
		"tags":             "",
		"probe_filters":    "",
		"userids":          "123,456",
		"teamids":          "789",
	}

	params := check.PutParams()
	assert.Equal(t, want, params)
}

func TestHttpCheckPostParams(t *testing.T) {
	check := HttpCheck{
		Name:     "fake check",
		Hostname: "example.com",
		Url:      "/foo",
		RequestHeaders: map[string]string{
			"User-Agent": "Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
			"Pragma":     "no-cache",
		},
		Username:       "user",
		Password:       "pass",
		IntegrationIds: []int{33333333, 44444444},
		UserIds:        []int{123, 456},
		TeamIds:        []int{789},
	}
	want := map[string]string{
		"name":             "fake check",
		"host":             "example.com",
		"paused":           "false",
		"resolution":       "0",
		"notifyagainevery": "0",
		"notifywhenbackup": "false",
		"type":             "http",
		"url":              "/foo",
		"requestheader0":   "Pragma:no-cache",
		"requestheader1":   "User-Agent:Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
		"auth":             "user:pass",
		"encryption":       "false",
		"integrationids":   "33333333,44444444",
		"userids":          "123,456",
		"teamids":          "789",
	}

	params := check.PostParams()
	assert.Equal(t, want, params)
}

func TestHttpCheckValid(t *testing.T) {
	check := HttpCheck{Name: "fake check", Hostname: "example.com", Resolution: 15}
	assert.NoError(t, check.Valid())

	badCheck := HttpCheck{Name: "fake check", Hostname: "example.com"}
	assert.Error(t, badCheck.Valid())

	badContainsCheck := HttpCheck{
		Name:             "fake check",
		Hostname:         "example.com",
		Resolution:       15,
		ShouldContain:    "foo",
		ShouldNotContain: "bar",
	}
	assert.Error(t, badContainsCheck.Valid())
}

func TestPingCheckPostParams(t *testing.T) {
	check := PingCheck{
		Name:           "fake check",
		Hostname:       "example.com",
		IntegrationIds: []int{33333333, 44444444},
		UserIds:        []int{123, 456},
		TeamIds:        []int{789},
	}
	want := map[string]string{
		"name":             "fake check",
		"host":             "example.com",
		"paused":           "false",
		"resolution":       "0",
		"notifyagainevery": "0",
		"notifywhenbackup": "false",
		"type":             "ping",
		"integrationids":   "33333333,44444444",
		"probe_filters":    "",
		"userids":          "123,456",
		"teamids":          "789",
	}

	params := check.PostParams()
	assert.Equal(t, want, params)
}

func TestPingCheckValid(t *testing.T) {
	check := PingCheck{Name: "fake check", Hostname: "example.com", Resolution: 15}
	assert.NoError(t, check.Valid())

	badCheck := PingCheck{Name: "fake check", Hostname: "example.com"}
	assert.Error(t, badCheck.Valid())
}

func TestTCPCheckPostParams(t *testing.T) {
	check := TCPCheck{
		Name:           "fake check",
		Hostname:       "example.com",
		IntegrationIds: []int{33333333, 44444444},
		UserIds:        []int{123, 456},
		TeamIds:        []int{789},
		Port:           8080,
		StringToSend:   "Hello World",
		StringToExpect: "Hi there",
	}
	want := map[string]string{
		"name":             "fake check",
		"host":             "example.com",
		"paused":           "false",
		"resolution":       "0",
		"notifyagainevery": "0",
		"notifywhenbackup": "false",
		"type":             "tcp",
		"integrationids":   "33333333,44444444",
		"probe_filters":    "",
		"userids":          "123,456",
		"teamids":          "789",
		"port":             "8080",
		"stringtosend":     "Hello World",
		"stringtoexpect":   "Hi there",
	}

	params := check.PostParams()
	assert.Equal(t, want, params)
}

func TestTCPCheckValid(t *testing.T) {
	check := TCPCheck{Name: "fake check", Hostname: "example.com", Resolution: 15, Port: 8080}
	assert.NoError(t, check.Valid())

	badCheck := TCPCheck{Name: "fake check", Hostname: "example.com", Resolution: 15}
	assert.Error(t, badCheck.Valid())
}
<commit_msg>Empty, optional parameters should not be passed to the API<commit_after>package pingdom

import (
	"testing"

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

func TestHttpCheckPutParams(t *testing.T) {
	check := HttpCheck{
		Name:     "fake check",
		Hostname: "example.com",
		Url:      "/foo",
		RequestHeaders: map[string]string{
			"User-Agent": "Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
			"Pragma":     "no-cache",
		},
		Username:       "user",
		Password:       "pass",
		IntegrationIds: []int{33333333, 44444444},
		UserIds:        []int{123, 456},
		TeamIds:        []int{789},
	}
	want := map[string]string{
		"name":             "fake check",
		"host":             "example.com",
		"paused":           "false",
		"resolution":       "0",
		"notifyagainevery": "0",
		"notifywhenbackup": "false",
		"url":              "/foo",
		"requestheader0":   "Pragma:no-cache",
		"requestheader1":   "User-Agent:Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
		"auth":             "user:pass",
		"encryption":       "false",
		"shouldnotcontain": "",
		"postdata":         "",
		"integrationids":   "33333333,44444444",
		"tags":             "",
		"probe_filters":    "",
		"userids":          "123,456",
		"teamids":          "789",
	}

	params := check.PutParams()
	assert.Equal(t, want, params)
}

func TestHttpCheckPostParams(t *testing.T) {
	check := HttpCheck{
		Name:     "fake check",
		Hostname: "example.com",
		Url:      "/foo",
		RequestHeaders: map[string]string{
			"User-Agent": "Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
			"Pragma":     "no-cache",
		},
		Username:       "user",
		Password:       "pass",
		IntegrationIds: []int{33333333, 44444444},
		UserIds:        []int{123, 456},
		TeamIds:        []int{789},
	}
	want := map[string]string{
		"name":             "fake check",
		"host":             "example.com",
		"paused":           "false",
		"resolution":       "0",
		"notifyagainevery": "0",
		"notifywhenbackup": "false",
		"type":             "http",
		"url":              "/foo",
		"requestheader0":   "Pragma:no-cache",
		"requestheader1":   "User-Agent:Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
		"auth":             "user:pass",
		"encryption":       "false",
		"integrationids":   "33333333,44444444",
		"userids":          "123,456",
		"teamids":          "789",
	}

	params := check.PostParams()
	assert.Equal(t, want, params)
}

func TestHttpCheckValid(t *testing.T) {
	check := HttpCheck{Name: "fake check", Hostname: "example.com", Resolution: 15}
	assert.NoError(t, check.Valid())

	badCheck := HttpCheck{Name: "fake check", Hostname: "example.com"}
	assert.Error(t, badCheck.Valid())

	badContainsCheck := HttpCheck{
		Name:             "fake check",
		Hostname:         "example.com",
		Resolution:       15,
		ShouldContain:    "foo",
		ShouldNotContain: "bar",
	}
	assert.Error(t, badContainsCheck.Valid())
}

func TestPingCheckPostParams(t *testing.T) {
	check := PingCheck{
		Name:           "fake check",
		Hostname:       "example.com",
		IntegrationIds: []int{33333333, 44444444},
		UserIds:        []int{123, 456},
		TeamIds:        []int{789},
	}
	want := map[string]string{
		"name":             "fake check",
		"host":             "example.com",
		"paused":           "false",
		"resolution":       "0",
		"notifyagainevery": "0",
		"notifywhenbackup": "false",
		"type":             "ping",
		"integrationids":   "33333333,44444444",
		"userids":          "123,456",
		"teamids":          "789",
	}

	params := check.PostParams()
	assert.Equal(t, want, params)
}

func TestPingCheckValid(t *testing.T) {
	check := PingCheck{Name: "fake check", Hostname: "example.com", Resolution: 15}
	assert.NoError(t, check.Valid())

	badCheck := PingCheck{Name: "fake check", Hostname: "example.com"}
	assert.Error(t, badCheck.Valid())
}

func TestTCPCheckPostParams(t *testing.T) {
	check := TCPCheck{
		Name:           "fake check",
		Hostname:       "example.com",
		IntegrationIds: []int{33333333, 44444444},
		UserIds:        []int{123, 456},
		TeamIds:        []int{789},
		Port:           8080,
		StringToSend:   "Hello World",
		StringToExpect: "Hi there",
	}
	want := map[string]string{
		"name":             "fake check",
		"host":             "example.com",
		"paused":           "false",
		"resolution":       "0",
		"notifyagainevery": "0",
		"notifywhenbackup": "false",
		"type":             "tcp",
		"integrationids":   "33333333,44444444",
		"userids":          "123,456",
		"teamids":          "789",
		"port":             "8080",
		"stringtosend":     "Hello World",
		"stringtoexpect":   "Hi there",
	}

	params := check.PostParams()
	assert.Equal(t, want, params)
}

func TestTCPCheckValid(t *testing.T) {
	check := TCPCheck{Name: "fake check", Hostname: "example.com", Resolution: 15, Port: 8080}
	assert.NoError(t, check.Valid())

	badCheck := TCPCheck{Name: "fake check", Hostname: "example.com", Resolution: 15}
	assert.Error(t, badCheck.Valid())
}
<|endoftext|>


<commit_before>package metric

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"edgeg.io/gtm/scm"
)

type FileLog struct {
	SourceFile string
	TimeSpent  int
	Timeline   map[int64]int
	Status     string
}

func (f *FileLog) SortEpochs() []int64 {
	keys := []int64{}
	for k := range f.Timeline {
		keys = append(keys, k)
	}
	sort.Sort(ByEpoch(keys))
	return keys
}

func NewFileLog(filePath string, total int, timeline map[int64]int, status string) (FileLog, error) {
	return FileLog{SourceFile: filePath, TimeSpent: total, Timeline: timeline, Status: status}, nil
}

type TimeLog struct {
	Files []FileLog
}

func marshalTimeLog(tl TimeLog) string {
	s := fmt.Sprintf("[ver:%s,total:%d]\n", "1", tl.Total())
	for _, fl := range tl.Files {
		s += fmt.Sprintf("%s:%d,", fl.SourceFile, fl.TimeSpent)
		for _, e := range fl.SortEpochs() {
			s += fmt.Sprintf("%d:%d,", e, fl.Timeline[e])
		}
		s += fmt.Sprintf("%s", fl.Status)
	}
	return s
}

func unMarshalTimeLog(s string) (TimeLog, error) {
	var (
		version  string
		fileLogs = []FileLog{}
	)

	reHeader := regexp.MustCompile(`\[ver:\d+,total:\d+]`)
	reHeaderVals := regexp.MustCompile(`\d+`)

	lines := strings.Split(s, "\n")
	for lineIdx := 0; lineIdx < len(lines); lineIdx++ {
		switch {
		case strings.TrimSpace(lines[lineIdx]) == "":
			version = ""
		case reHeader.MatchString(lines[lineIdx]):
			if matches := reHeaderVals.FindAllString(lines[lineIdx], 2); matches != nil && len(matches) == 2 {
				version = matches[0]
			} else {
				return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, header format invalid, %s", lines[lineIdx])
			}
		case version == "1":
			fieldGroups := strings.Split(lines[lineIdx], ",")
			if len(fieldGroups) < 3 {
				return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", lines[lineIdx])
			}

			var (
				filePath     string
				fileTotal    int
				fileStatus   string
				fileTimeline = map[int64]int{}
			)

			for groupIdx := range fieldGroups {
				fieldVals := strings.Split(fieldGroups[groupIdx], ":")
				switch {
				case groupIdx == 0 && len(fieldVals) == 2:
					// file name and total
					filePath = fieldVals[0]
					t, err := strconv.Atoi(fieldVals[1])
					if err != nil {
						return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", err)
					}
					fileTotal = t
				case groupIdx == len(fieldGroups)-1 && len(fieldVals) == 1:
					fileStatus = fieldVals[0]
				case len(fieldVals) == 2:
					e, err := strconv.ParseInt(fieldVals[0], 10, 64)
					if err != nil {
						return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", err)
					}
					t, err := strconv.Atoi(fieldVals[1])
					if err != nil {
						return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", err)
					}
					fileTimeline[e] = t
				default:
					// error
				}
			}
			fl, err := NewFileLog(filePath, fileTotal, fileTimeline, fileStatus)
			if err != nil {
				return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", err)
			}
			fileLogs = append(fileLogs, fl)

		default:
			return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, unknown version %s", version)
		}
	}
	return TimeLog{Files: fileLogs}, nil
}

func (t TimeLog) Total() int {
	total := 0
	for _, fm := range t.Files {
		total += fm.TimeSpent
	}
	return total
}

type FileLogByTime []FileLog

func (a FileLogByTime) Len() int           { return len(a) }
func (a FileLogByTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a FileLogByTime) Less(i, j int) bool { return a[i].TimeSpent < a[j].TimeSpent }

func NewTimeLog(metricMap map[string]FileMetric, commitMap map[string]FileMetric) (TimeLog, error) {
	fls := []FileLog{}
	for _, fm := range commitMap {
		fm.Downsample()
		fls = append(fls, FileLog{SourceFile: fm.SourceFile, TimeSpent: fm.TimeSpent, Timeline: fm.Timeline, Status: "m"})
	}

	for fileID, fm := range metricMap {
		if _, ok := commitMap[fileID]; !ok {
			// looking at only files not in commit
			modified, err := scm.GitModified(fm.SourceFile)
			if err != nil {
				return TimeLog{}, err
			}
			if fm.GitTracked && !modified {
				// source file is tracked by git and is not modified
				fm.Downsample()
				fls = append(fls, FileLog{SourceFile: fm.SourceFile, TimeSpent: fm.TimeSpent, Timeline: fm.Timeline, Status: "r"})
			}
		}
	}
	sort.Sort(sort.Reverse(FileLogByTime(fls)))
	return TimeLog{Files: fls}, nil
}
<commit_msg>Raise an error on marshalling if format not recognized<commit_after>package metric

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"edgeg.io/gtm/scm"
)

type FileLog struct {
	SourceFile string
	TimeSpent  int
	Timeline   map[int64]int
	Status     string
}

func (f *FileLog) SortEpochs() []int64 {
	keys := []int64{}
	for k := range f.Timeline {
		keys = append(keys, k)
	}
	sort.Sort(ByEpoch(keys))
	return keys
}

func NewFileLog(filePath string, total int, timeline map[int64]int, status string) (FileLog, error) {
	return FileLog{SourceFile: filePath, TimeSpent: total, Timeline: timeline, Status: status}, nil
}

type TimeLog struct {
	Files []FileLog
}

func marshalTimeLog(tl TimeLog) string {
	s := fmt.Sprintf("[ver:%s,total:%d]\n", "1", tl.Total())
	for _, fl := range tl.Files {
		s += fmt.Sprintf("%s:%d,", fl.SourceFile, fl.TimeSpent)
		for _, e := range fl.SortEpochs() {
			s += fmt.Sprintf("%d:%d,", e, fl.Timeline[e])
		}
		s += fmt.Sprintf("%s", fl.Status)
	}
	return s
}

func unMarshalTimeLog(s string) (TimeLog, error) {
	var (
		version  string
		fileLogs = []FileLog{}
	)

	reHeader := regexp.MustCompile(`\[ver:\d+,total:\d+]`)
	reHeaderVals := regexp.MustCompile(`\d+`)

	lines := strings.Split(s, "\n")
	for lineIdx := 0; lineIdx < len(lines); lineIdx++ {
		switch {
		case strings.TrimSpace(lines[lineIdx]) == "":
			version = ""
		case reHeader.MatchString(lines[lineIdx]):
			if matches := reHeaderVals.FindAllString(lines[lineIdx], 2); matches != nil && len(matches) == 2 {
				version = matches[0]
			} else {
				return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, header format invalid, %s", lines[lineIdx])
			}
		case version == "1":
			fieldGroups := strings.Split(lines[lineIdx], ",")
			if len(fieldGroups) < 3 {
				return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", lines[lineIdx])
			}

			var (
				filePath     string
				fileTotal    int
				fileStatus   string
				fileTimeline = map[int64]int{}
			)

			for groupIdx := range fieldGroups {
				fieldVals := strings.Split(fieldGroups[groupIdx], ":")
				switch {
				case groupIdx == 0 && len(fieldVals) == 2:
					// file name and total, filename:total
					filePath = fieldVals[0]
					t, err := strconv.Atoi(fieldVals[1])
					if err != nil {
						return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", err)
					}
					fileTotal = t
				case groupIdx == len(fieldGroups)-1 && len(fieldVals) == 1:
					// file status of m or r
					fileStatus = fieldVals[0]
				case len(fieldVals) == 2:
					// epoch timeline, epoch:total
					e, err := strconv.ParseInt(fieldVals[0], 10, 64)
					if err != nil {
						return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", err)
					}
					t, err := strconv.Atoi(fieldVals[1])
					if err != nil {
						return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", err)
					}
					fileTimeline[e] = t
				default:
					// error
					return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid")
				}
			}
			fl, err := NewFileLog(filePath, fileTotal, fileTimeline, fileStatus)
			if err != nil {
				return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, format invalid, %s", err)
			}
			fileLogs = append(fileLogs, fl)

		default:
			return TimeLog{}, fmt.Errorf("Unable to unmarshal time logged, unknown version %s", version)
		}
	}
	return TimeLog{Files: fileLogs}, nil
}

func (t TimeLog) Total() int {
	total := 0
	for _, fm := range t.Files {
		total += fm.TimeSpent
	}
	return total
}

type FileLogByTime []FileLog

func (a FileLogByTime) Len() int           { return len(a) }
func (a FileLogByTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a FileLogByTime) Less(i, j int) bool { return a[i].TimeSpent < a[j].TimeSpent }

func NewTimeLog(metricMap map[string]FileMetric, commitMap map[string]FileMetric) (TimeLog, error) {
	fls := []FileLog{}
	for _, fm := range commitMap {
		fm.Downsample()
		fls = append(fls, FileLog{SourceFile: fm.SourceFile, TimeSpent: fm.TimeSpent, Timeline: fm.Timeline, Status: "m"})
	}

	for fileID, fm := range metricMap {
		if _, ok := commitMap[fileID]; !ok {
			// looking at only files not in commit
			modified, err := scm.GitModified(fm.SourceFile)
			if err != nil {
				return TimeLog{}, err
			}
			if fm.GitTracked && !modified {
				// source file is tracked by git and is not modified
				fm.Downsample()
				fls = append(fls, FileLog{SourceFile: fm.SourceFile, TimeSpent: fm.TimeSpent, Timeline: fm.Timeline, Status: "r"})
			}
		}
	}
	sort.Sort(sort.Reverse(FileLogByTime(fls)))
	return TimeLog{Files: fls}, nil
}
<|endoftext|>


<commit_before>/*

Package luautil implements utility for gopher-lua.

*/
package luautil // import "a4.io/blobstash/pkg/apps/luautil"

import (
	"encoding/json"
	"errors"

	"github.com/yuin/gopher-lua"
)

// TableToMap convert a `*lua.LTable` to a `map[string]interface{}`
func TableToMap(table *lua.LTable) map[string]interface{} {
	res, _ := tomap(table, map[*lua.LTable]bool{})
	return res
}

func tomap(table *lua.LTable, visited map[*lua.LTable]bool) (map[string]interface{}, []interface{}) {
	res := map[string]interface{}{}
	var arrres []interface{}
	nkey := false
	table.ForEach(func(key lua.LValue, value lua.LValue) {
		_, numberKey := key.(lua.LNumber)
		if numberKey {
			nkey = true
		}
		switch converted := value.(type) {
		case lua.LBool, lua.LNumber, lua.LString:
			if nkey {
				arrres = append(arrres, converted)
			} else {
				res[key.String()] = converted
			}
		case lua.LChannel:
			panic("no channel")
		case *lua.LFunction:
			panic("no function")
		case *lua.LNilType:
			res[key.String()] = converted
		case *lua.LState:
			panic("no LState")
		case *lua.LTable:
			var arr []interface{}
			obj := map[string]interface{}{}

			if visited[converted] {
				panic("nested table")
			}
			visited[converted] = true

			converted.ForEach(func(k lua.LValue, v lua.LValue) {
				_, numberKey := k.(lua.LNumber)
				// if numberKey, then convert to a slice of interface
				subtable, istable := v.(*lua.LTable)
				if numberKey {
					if istable {
						rtable, rarr := tomap(subtable, visited)
						if rarr != nil {
							arr = append(arr, rarr)
						} else {
							arr = append(arr, rtable)
						}
						// arr = append(arr, tomap(subtable, visited))
					} else {
						arr = append(arr, v)
					}
				} else {
					if istable {
						rtable, rarr := tomap(subtable, visited)
						if rarr != nil {
							obj[k.(lua.LString).String()] = rarr
						} else {
							obj[k.(lua.LString).String()] = rtable
						}
					} else {
						obj[k.(lua.LString).String()] = v
					}
				}
			})
			if len(arr) > 0 {
				if nkey {
					arrres = append(arrres, arr)
				} else {
					res[key.String()] = arr
				}
			} else {
				if nkey {
					arrres = append(arrres, obj)
				} else {
					res[key.String()] = obj
				}
			}
		}
	})
	return res, arrres
}

// Convert a Lua table to JSON
// Adapted from https://github.com/layeh/gopher-json/blob/master/util.go (Public domain)
func ToJSON(value lua.LValue) []byte {
	var data []byte
	var err error
	switch converted := value.(type) {
	case lua.LBool:
		data, err = json.Marshal(converted)
	case lua.LChannel:
		err = errors.New("ToJSON: cannot marshal channel")
	case lua.LNumber:
		data, err = json.Marshal(converted)
	case *lua.LFunction:
		err = errors.New("ToJSON: cannot marshal function")
	case *lua.LNilType:
		data, err = json.Marshal(converted)
	case *lua.LState:
		err = errors.New("ToJSON: cannot marshal LState")
	case lua.LString:
		data, err = json.Marshal(converted)
	case *lua.LTable:
		data, err = json.Marshal(TableToMap(converted))
	case *lua.LUserData:
		err = errors.New("ToJSON: cannot marshal user data")
		// TODO: call metatable __tostring?
	}
	if err != nil {
		panic(err)
	}
	return data
}

// Convert the JSON to a Lua object ready to be pushed
// Adapted from https://github.com/layeh/gopher-json/blob/master/util.go (Public domain)
func FromJSON(L *lua.LState, js []byte) lua.LValue {
	var res interface{}
	if err := json.Unmarshal(js, &res); err != nil {
		panic(err)
	}
	return fromJSON(L, res)
}

func InterfaceToLValue(L *lua.LState, value interface{}) lua.LValue {
	return fromJSON(L, value)
}

func fromJSON(L *lua.LState, value interface{}) lua.LValue {
	switch converted := value.(type) {
	case bool:
		return lua.LBool(converted)
	case int:
		return lua.LNumber(converted)
	case int64:
		return lua.LNumber(converted)
	case float64:
		return lua.LNumber(converted)
	case string:
		return lua.LString(converted)
	case []interface{}:
		arr := L.CreateTable(len(converted), 0)
		for _, item := range converted {
			arr.Append(fromJSON(L, item))
		}
		return arr
	case map[string]interface{}:
		tbl := L.CreateTable(0, len(converted))
		for key, item := range converted {
			tbl.RawSetH(lua.LString(key), fromJSON(L, item))
		}
		return tbl
	}
	return lua.LNil
}
<commit_msg>apps/luautil: bugfixes/cleanup<commit_after>/*

Package luautil implements utility for gopher-lua.

*/
package luautil // import "a4.io/blobstash/pkg/apps/luautil"

import (
	"encoding/json"
	"errors"

	"github.com/yuin/gopher-lua"
)

// TableToMap convert a `*lua.LTable` to a `map[string]interface{}`
func TableToMap(table *lua.LTable) map[string]interface{} {
	res, _ := tomap(table, map[*lua.LTable]bool{})
	return res
}

func tomap(table *lua.LTable, visited map[*lua.LTable]bool) (map[string]interface{}, []interface{}) {
	res := map[string]interface{}{}
	var arrres []interface{}
	nkey := false
	table.ForEach(func(key lua.LValue, value lua.LValue) {
		_, numberKey := key.(lua.LNumber)
		if numberKey {
			nkey = true
		}
		switch converted := value.(type) {
		case lua.LBool:
			val := false
			if converted == lua.LTrue {
				val = true
			}
			if nkey {
				arrres = append(arrres, val)
			} else {
				res[key.String()] = val
			}
		case lua.LString:
			if nkey {
				arrres = append(arrres, string(converted))
			} else {
				res[key.String()] = string(converted)
			}
		case lua.LNumber:
			if nkey {
				arrres = append(arrres, float64(converted))
			} else {
				res[key.String()] = float64(converted)
			}
		case lua.LChannel:
			panic("no channel")
		case *lua.LFunction:
			panic("no function")
		case *lua.LNilType:
			res[key.String()] = converted
		case *lua.LState:
			panic("no LState")
		case *lua.LTable:
			var arr []interface{}
			obj := map[string]interface{}{}

			if visited[converted] {
				panic("nested table")
			}
			visited[converted] = true

			converted.ForEach(func(k lua.LValue, v lua.LValue) {
				_, numberKey := k.(lua.LNumber)
				// if numberKey, then convert to a slice of interface
				subtable, istable := v.(*lua.LTable)
				if numberKey {
					if istable {
						rtable, rarr := tomap(subtable, visited)
						if rarr != nil {
							arr = append(arr, rarr)
						} else {
							arr = append(arr, rtable)
						}
						// arr = append(arr, tomap(subtable, visited))
					} else {
						arr = append(arr, v)
					}
				} else {
					if istable {
						rtable, rarr := tomap(subtable, visited)
						if rarr != nil {
							obj[k.(lua.LString).String()] = rarr
						} else {
							obj[k.(lua.LString).String()] = rtable
						}
					} else {
						obj[k.(lua.LString).String()] = v
					}
				}
			})
			if len(arr) > 0 {
				if nkey {
					arrres = append(arrres, arr)
				} else {
					res[key.String()] = arr
				}
			} else {
				if nkey {
					arrres = append(arrres, obj)
				} else {
					res[key.String()] = obj
				}
			}
		}
	})
	return res, arrres
}

// Convert a Lua table to JSON
// Adapted from https://github.com/layeh/gopher-json/blob/master/util.go (Public domain)
func ToJSON(value lua.LValue) []byte {
	var data []byte
	var err error
	switch converted := value.(type) {
	case lua.LBool:
		data, err = json.Marshal(converted)
	case lua.LChannel:
		err = errors.New("ToJSON: cannot marshal channel")
	case lua.LNumber:
		data, err = json.Marshal(converted)
	case *lua.LFunction:
		err = errors.New("ToJSON: cannot marshal function")
	case *lua.LNilType:
		data, err = json.Marshal(converted)
	case *lua.LState:
		err = errors.New("ToJSON: cannot marshal LState")
	case lua.LString:
		data, err = json.Marshal(converted)
	case *lua.LTable:
		data, err = json.Marshal(TableToMap(converted))
	case *lua.LUserData:
		err = errors.New("ToJSON: cannot marshal user data")
		// TODO: call metatable __tostring?
	}
	if err != nil {
		panic(err)
	}
	return data
}

// Convert the JSON to a Lua object ready to be pushed
// Adapted from https://github.com/layeh/gopher-json/blob/master/util.go (Public domain)
func FromJSON(L *lua.LState, js []byte) lua.LValue {
	var res interface{}
	if err := json.Unmarshal(js, &res); err != nil {
		panic(err)
	}
	return fromJSON(L, res)
}

func InterfaceToLValue(L *lua.LState, value interface{}) lua.LValue {
	return fromJSON(L, value)
}

func fromJSON(L *lua.LState, value interface{}) lua.LValue {
	switch converted := value.(type) {
	case bool:
		return lua.LBool(converted)
	case int:
		return lua.LNumber(converted)
	case int64:
		return lua.LNumber(converted)
	case float64:
		return lua.LNumber(converted)
	case string:
		return lua.LString(converted)
	case []interface{}:
		arr := L.CreateTable(len(converted), 0)
		for _, item := range converted {
			arr.Append(fromJSON(L, item))
		}
		return arr
	case map[string]interface{}:
		tbl := L.CreateTable(0, len(converted))
		for key, item := range converted {
			tbl.RawSetH(lua.LString(key), fromJSON(L, item))
		}
		return tbl
	default:
		panic("unsupported type")
	}
	return lua.LNil
}
<|endoftext|>


<commit_before>package std

import "github.com/tisp-lang/tisp/src/lib/core"

// Y is Y combinator which takes a function whose first argument is itself
// applied to the combinator.
var Y = core.NewLazyFunction(
	core.NewSignature(
		[]string{"function"}, nil, "",
		nil, nil, "",
	),
	func(ts ...*core.Thunk) core.Value {
		xfxx := core.PApp(core.Partial, fxx, ts[0])
		return core.PApp(xfxx, xfxx)
	})

var fxx = core.NewLazyFunction(
	core.NewSignature(
		[]string{"f", "x"}, nil, "",
		nil, nil, "",
	),
	func(ts ...*core.Thunk) core.Value {
		return core.PApp(core.Partial, ts[0], core.PApp(ts[1], ts[1]))
	})
<commit_msg>Note on using Y combinators in Go source code<commit_after>package std

import "github.com/tisp-lang/tisp/src/lib/core"

// Y is Y combinator which takes a function whose first argument is itself
// applied to the combinator.
// Using Y combinator to define built-in functions in Go source is dangerous
// because top-level recursive functions generate infinitely nested closures.
// (i.e. closure{f, x} where x will also be evaluated as closure{f, x}.)
var Y = core.NewLazyFunction(
	core.NewSignature(
		[]string{"function"}, nil, "",
		nil, nil, "",
	),
	func(ts ...*core.Thunk) core.Value {
		xfxx := core.PApp(core.Partial, fxx, ts[0])
		return core.PApp(xfxx, xfxx)
	})

var fxx = core.NewLazyFunction(
	core.NewSignature(
		[]string{"f", "x"}, nil, "",
		nil, nil, "",
	),
	func(ts ...*core.Thunk) core.Value {
		return core.PApp(core.Partial, ts[0], core.PApp(ts[1], ts[1]))
	})
<|endoftext|>


<commit_before>package cluster

import (
	"errors"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/coreos/coreos-kubernetes/multi-node/aws/pkg/config"
)

const minimalConfigYaml = `
externalDNSName: test.staging.core-os.net
keyName: test-key-name
region: us-west-1
availabilityZone: us-west-1c
clusterName: test-cluster-name
kmsKeyArn: "arn:aws:kms:us-west-1:xxxxxxxxx:key/xxxxxxxxxxxxxxxxxxx"
`

type VPC struct {
	cidr        string
	subnetCidrs []string
}

type dummyEC2Service struct {
	VPCs     map[string]VPC
	KeyPairs map[string]bool
}

func (svc dummyEC2Service) DescribeVpcs(input *ec2.DescribeVpcsInput) (*ec2.DescribeVpcsOutput, error) {
	output := ec2.DescribeVpcsOutput{}
	for _, vpcID := range input.VpcIds {
		if vpc, ok := svc.VPCs[*vpcID]; ok {
			output.Vpcs = append(output.Vpcs, &ec2.Vpc{
				VpcId:     vpcID,
				CidrBlock: aws.String(vpc.cidr),
			})
		}
	}

	return &output, nil
}

func (svc dummyEC2Service) DescribeSubnets(input *ec2.DescribeSubnetsInput) (*ec2.DescribeSubnetsOutput, error) {
	output := ec2.DescribeSubnetsOutput{}

	var vpcIds []string
	for _, filter := range input.Filters {
		if *filter.Name == "vpc-id" {
			for _, value := range filter.Values {
				vpcIds = append(vpcIds, *value)
			}
		}
	}

	for _, vpcID := range vpcIds {
		if vpc, ok := svc.VPCs[vpcID]; ok {
			for _, subnetCidr := range vpc.subnetCidrs {
				output.Subnets = append(
					output.Subnets,
					&ec2.Subnet{CidrBlock: aws.String(subnetCidr)},
				)
			}
		}
	}

	return &output, nil
}

func (svc dummyEC2Service) DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (*ec2.DescribeKeyPairsOutput, error) {
	output := &ec2.DescribeKeyPairsOutput{}

	for _, keyName := range input.KeyNames {
		if _, ok := svc.KeyPairs[*keyName]; ok {
			output.KeyPairs = append(output.KeyPairs, &ec2.KeyPairInfo{
				KeyName: keyName,
			})
		} else {
			return nil, awserr.New("InvalidKeyPair.NotFound", "", errors.New(""))
		}
	}

	return output, nil
}

func TestExistingVPCValidation(t *testing.T) {

	goodExistingVPCConfigs := []string{
		``, //Tests default create VPC mode, which bypasses existing VPC validation
		`
vpcCIDR: 10.5.0.0/16
vpcId: vpc-xxx1
routeTableId: rtb-xxxxxx
instanceCIDR: 10.5.11.0/24
controllerIP: 10.5.11.10
`, `
vpcCIDR: 192.168.1.0/24
vpcId: vpc-xxx2
instanceCIDR: 192.168.1.50/28
controllerIP: 192.168.1.50
`,
	}

	badExistingVPCConfigs := []string{
		`
vpcCIDR: 10.0.0.0/16
vpcId: vpc-xxx3 #vpc does not exist
instanceCIDR: 10.0.0.0/24
controllerIP: 10.0.0.50
routeTableId: rtb-xxxxxx
`, `
vpcCIDR: 10.10.0.0/16 #vpc cidr does match existing vpc-xxx1
vpcId: vpc-xxx1
instanceCIDR: 10.10.0.0/24
controllerIP: 10.10.0.50
routeTableId: rtb-xxxxxx
`, `
vpcCIDR: 10.5.0.0/16
instanceCIDR: 10.5.2.0/28 #instance cidr conflicts with existing subnet
controllerIP: 10.5.2.10
vpcId: vpc-xxx1
routeTableId: rtb-xxxxxx
`, `
vpcCIDR: 192.168.1.0/24
instanceCIDR: 192.168.1.100/26 #instance cidr conflicts with existing subnet
controllerIP: 192.168.1.80
vpcId: vpc-xxx2
routeTableId: rtb-xxxxxx
`,
	}

	ec2Service := dummyEC2Service{
		VPCs: map[string]VPC{
			"vpc-xxx1": {
				cidr: "10.5.0.0/16",
				subnetCidrs: []string{
					"10.5.1.0/24",
					"10.5.2.0/24",
					"10.5.10.100/29",
				},
			},
			"vpc-xxx2": {
				cidr: "192.168.1.0/24",
				subnetCidrs: []string{
					"192.168.1.100/28",
					"192.168.1.150/28",
					"192.168.1.200/28",
				},
			},
		},
	}

	validateCluster := func(networkConfig string) error {
		configBody := minimalConfigYaml + networkConfig
		clusterConfig, err := config.ClusterFromBytes([]byte(configBody))
		if err != nil {
			t.Errorf("could not get valid cluster config: %v", err)
			return nil
		}

		cluster := &Cluster{
			Cluster: *clusterConfig,
		}

		return cluster.validateExistingVPCState(ec2Service)
	}

	for _, networkConfig := range goodExistingVPCConfigs {
		if err := validateCluster(networkConfig); err != nil {
			t.Errorf("Correct config tested invalid: %s\n%s", err, networkConfig)
		}
	}

	for _, networkConfig := range badExistingVPCConfigs {
		if err := validateCluster(networkConfig); err == nil {
			t.Errorf("Incorrect config tested valid, expected error:\n%s", networkConfig)
		}
	}
}

func TestValidateKeyPair(t *testing.T) {

	clusterConfig, err := config.ClusterFromBytes([]byte(minimalConfigYaml))
	if err != nil {
		t.Errorf("could not get valid cluster config: %v", err)
	}

	c := &Cluster{Cluster: *clusterConfig}

	ec2Svc := dummyEC2Service{}
	ec2Svc.KeyPairs = map[string]bool{
		c.KeyName: true,
	}

	if err := c.validateKeyPair(ec2Svc); err != nil {
		t.Errorf("returned an error for valid key")
	}

	c.KeyName = "invalidKeyName"
	if err := c.validateKeyPair(ec2Svc); err == nil {
		t.Errorf("failed to catch invalid key \"%s\"", c.KeyName)
	}
}

type Zone struct {
	Id  string
	DNS string
}

type dummyR53Service struct {
	HostedZones        []Zone
	ResourceRecordSets map[string]string
}

func (r53 dummyR53Service) ListHostedZonesByName(input *route53.ListHostedZonesByNameInput) (*route53.ListHostedZonesByNameOutput, error) {
	output := &route53.ListHostedZonesByNameOutput{}
	for _, zone := range r53.HostedZones {
		if zone.DNS == config.WithTrailingDot(*input.DNSName) {
			output.HostedZones = append(output.HostedZones, &route53.HostedZone{
				Name: aws.String(zone.DNS),
				Id:   aws.String(zone.Id),
			})
		}
	}
	return output, nil
}

func (r53 dummyR53Service) ListResourceRecordSets(input *route53.ListResourceRecordSetsInput) (*route53.ListResourceRecordSetsOutput, error) {
	output := &route53.ListResourceRecordSetsOutput{}
	if name, ok := r53.ResourceRecordSets[*input.HostedZoneId]; ok {
		output.ResourceRecordSets = []*route53.ResourceRecordSet{
			&route53.ResourceRecordSet{
				Name: aws.String(name),
			},
		}
	}
	return output, nil
}

func TestValidateDNSConfig(t *testing.T) {
	dnsConfig := `
createRecordSet: true
recordSetTTL: 60
hostedZone: staging.core-os.net
`

	configBody := minimalConfigYaml + dnsConfig
	clusterConfig, err := config.ClusterFromBytes([]byte(configBody))
	if err != nil {
		t.Errorf("could not get valid cluster config: %v", err)
	}
	c := &Cluster{Cluster: *clusterConfig}

	r53 := dummyR53Service{
		HostedZones: []Zone{
			Zone{
				Id:  "staging_id",
				DNS: "staging.core-os.net.",
			},
		},
		ResourceRecordSets: map[string]string{
			"staging_id": "existing-record.staging.core-os.net.",
		},
	}

	if err := c.validateDNSConfig(r53); err != nil {
		t.Errorf("returned error for valid config: %v", err)
	}

	c.HostedZone = "non-existant-zone"
	if err := c.validateDNSConfig(r53); err == nil {
		t.Errorf("failed to catch non-existent hosted zone")
	}

	c.HostedZone = "staging.core-os.net"
	c.ExternalDNSName = "existing-record.staging.core-os.net"
	if err := c.validateDNSConfig(r53); err == nil {
		t.Errorf("failed to catch already existing ExternalDNSName")
	}
}
<commit_msg>kube-aws: unit test for stackTags map<commit_after>package cluster

import (
	"errors"
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/coreos/coreos-kubernetes/multi-node/aws/pkg/config"
)

const minimalConfigYaml = `
externalDNSName: test.staging.core-os.net
keyName: test-key-name
region: us-west-1
availabilityZone: us-west-1c
clusterName: test-cluster-name
kmsKeyArn: "arn:aws:kms:us-west-1:xxxxxxxxx:key/xxxxxxxxxxxxxxxxxxx"
`

type VPC struct {
	cidr        string
	subnetCidrs []string
}

type dummyEC2Service struct {
	VPCs     map[string]VPC
	KeyPairs map[string]bool
}

func (svc dummyEC2Service) DescribeVpcs(input *ec2.DescribeVpcsInput) (*ec2.DescribeVpcsOutput, error) {
	output := ec2.DescribeVpcsOutput{}
	for _, vpcID := range input.VpcIds {
		if vpc, ok := svc.VPCs[*vpcID]; ok {
			output.Vpcs = append(output.Vpcs, &ec2.Vpc{
				VpcId:     vpcID,
				CidrBlock: aws.String(vpc.cidr),
			})
		}
	}

	return &output, nil
}

func (svc dummyEC2Service) DescribeSubnets(input *ec2.DescribeSubnetsInput) (*ec2.DescribeSubnetsOutput, error) {
	output := ec2.DescribeSubnetsOutput{}

	var vpcIds []string
	for _, filter := range input.Filters {
		if *filter.Name == "vpc-id" {
			for _, value := range filter.Values {
				vpcIds = append(vpcIds, *value)
			}
		}
	}

	for _, vpcID := range vpcIds {
		if vpc, ok := svc.VPCs[vpcID]; ok {
			for _, subnetCidr := range vpc.subnetCidrs {
				output.Subnets = append(
					output.Subnets,
					&ec2.Subnet{CidrBlock: aws.String(subnetCidr)},
				)
			}
		}
	}

	return &output, nil
}

func (svc dummyEC2Service) DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (*ec2.DescribeKeyPairsOutput, error) {
	output := &ec2.DescribeKeyPairsOutput{}

	for _, keyName := range input.KeyNames {
		if _, ok := svc.KeyPairs[*keyName]; ok {
			output.KeyPairs = append(output.KeyPairs, &ec2.KeyPairInfo{
				KeyName: keyName,
			})
		} else {
			return nil, awserr.New("InvalidKeyPair.NotFound", "", errors.New(""))
		}
	}

	return output, nil
}

func TestExistingVPCValidation(t *testing.T) {

	goodExistingVPCConfigs := []string{
		``, //Tests default create VPC mode, which bypasses existing VPC validation
		`
vpcCIDR: 10.5.0.0/16
vpcId: vpc-xxx1
routeTableId: rtb-xxxxxx
instanceCIDR: 10.5.11.0/24
controllerIP: 10.5.11.10
`, `
vpcCIDR: 192.168.1.0/24
vpcId: vpc-xxx2
instanceCIDR: 192.168.1.50/28
controllerIP: 192.168.1.50
`,
	}

	badExistingVPCConfigs := []string{
		`
vpcCIDR: 10.0.0.0/16
vpcId: vpc-xxx3 #vpc does not exist
instanceCIDR: 10.0.0.0/24
controllerIP: 10.0.0.50
routeTableId: rtb-xxxxxx
`, `
vpcCIDR: 10.10.0.0/16 #vpc cidr does match existing vpc-xxx1
vpcId: vpc-xxx1
instanceCIDR: 10.10.0.0/24
controllerIP: 10.10.0.50
routeTableId: rtb-xxxxxx
`, `
vpcCIDR: 10.5.0.0/16
instanceCIDR: 10.5.2.0/28 #instance cidr conflicts with existing subnet
controllerIP: 10.5.2.10
vpcId: vpc-xxx1
routeTableId: rtb-xxxxxx
`, `
vpcCIDR: 192.168.1.0/24
instanceCIDR: 192.168.1.100/26 #instance cidr conflicts with existing subnet
controllerIP: 192.168.1.80
vpcId: vpc-xxx2
routeTableId: rtb-xxxxxx
`,
	}

	ec2Service := dummyEC2Service{
		VPCs: map[string]VPC{
			"vpc-xxx1": {
				cidr: "10.5.0.0/16",
				subnetCidrs: []string{
					"10.5.1.0/24",
					"10.5.2.0/24",
					"10.5.10.100/29",
				},
			},
			"vpc-xxx2": {
				cidr: "192.168.1.0/24",
				subnetCidrs: []string{
					"192.168.1.100/28",
					"192.168.1.150/28",
					"192.168.1.200/28",
				},
			},
		},
	}

	validateCluster := func(networkConfig string) error {
		configBody := minimalConfigYaml + networkConfig
		clusterConfig, err := config.ClusterFromBytes([]byte(configBody))
		if err != nil {
			t.Errorf("could not get valid cluster config: %v", err)
			return nil
		}

		cluster := &Cluster{
			Cluster: *clusterConfig,
		}

		return cluster.validateExistingVPCState(ec2Service)
	}

	for _, networkConfig := range goodExistingVPCConfigs {
		if err := validateCluster(networkConfig); err != nil {
			t.Errorf("Correct config tested invalid: %s\n%s", err, networkConfig)
		}
	}

	for _, networkConfig := range badExistingVPCConfigs {
		if err := validateCluster(networkConfig); err == nil {
			t.Errorf("Incorrect config tested valid, expected error:\n%s", networkConfig)
		}
	}
}

func TestValidateKeyPair(t *testing.T) {

	clusterConfig, err := config.ClusterFromBytes([]byte(minimalConfigYaml))
	if err != nil {
		t.Errorf("could not get valid cluster config: %v", err)
	}

	c := &Cluster{Cluster: *clusterConfig}

	ec2Svc := dummyEC2Service{}
	ec2Svc.KeyPairs = map[string]bool{
		c.KeyName: true,
	}

	if err := c.validateKeyPair(ec2Svc); err != nil {
		t.Errorf("returned an error for valid key")
	}

	c.KeyName = "invalidKeyName"
	if err := c.validateKeyPair(ec2Svc); err == nil {
		t.Errorf("failed to catch invalid key \"%s\"", c.KeyName)
	}
}

type Zone struct {
	Id  string
	DNS string
}

type dummyR53Service struct {
	HostedZones        []Zone
	ResourceRecordSets map[string]string
}

func (r53 dummyR53Service) ListHostedZonesByName(input *route53.ListHostedZonesByNameInput) (*route53.ListHostedZonesByNameOutput, error) {
	output := &route53.ListHostedZonesByNameOutput{}
	for _, zone := range r53.HostedZones {
		if zone.DNS == config.WithTrailingDot(*input.DNSName) {
			output.HostedZones = append(output.HostedZones, &route53.HostedZone{
				Name: aws.String(zone.DNS),
				Id:   aws.String(zone.Id),
			})
		}
	}
	return output, nil
}

func (r53 dummyR53Service) ListResourceRecordSets(input *route53.ListResourceRecordSetsInput) (*route53.ListResourceRecordSetsOutput, error) {
	output := &route53.ListResourceRecordSetsOutput{}
	if name, ok := r53.ResourceRecordSets[*input.HostedZoneId]; ok {
		output.ResourceRecordSets = []*route53.ResourceRecordSet{
			&route53.ResourceRecordSet{
				Name: aws.String(name),
			},
		}
	}
	return output, nil
}

func TestValidateDNSConfig(t *testing.T) {
	dnsConfig := `
createRecordSet: true
recordSetTTL: 60
hostedZone: staging.core-os.net
`

	configBody := minimalConfigYaml + dnsConfig
	clusterConfig, err := config.ClusterFromBytes([]byte(configBody))
	if err != nil {
		t.Errorf("could not get valid cluster config: %v", err)
	}
	c := &Cluster{Cluster: *clusterConfig}

	r53 := dummyR53Service{
		HostedZones: []Zone{
			Zone{
				Id:  "staging_id",
				DNS: "staging.core-os.net.",
			},
		},
		ResourceRecordSets: map[string]string{
			"staging_id": "existing-record.staging.core-os.net.",
		},
	}

	if err := c.validateDNSConfig(r53); err != nil {
		t.Errorf("returned error for valid config: %v", err)
	}

	c.HostedZone = "non-existant-zone"
	if err := c.validateDNSConfig(r53); err == nil {
		t.Errorf("failed to catch non-existent hosted zone")
	}

	c.HostedZone = "staging.core-os.net"
	c.ExternalDNSName = "existing-record.staging.core-os.net"
	if err := c.validateDNSConfig(r53); err == nil {
		t.Errorf("failed to catch already existing ExternalDNSName")
	}
}

func TestStackTags(t *testing.T) {
	testCases := []struct {
		expectedTags []*cloudformation.Tag
		clusterYaml  string
	}{
		{
			expectedTags: []*cloudformation.Tag{},
			clusterYaml: `
#no stackTags set
`,
		},
		{
			expectedTags: []*cloudformation.Tag{
				&cloudformation.Tag{
					Key:   aws.String("KeyA"),
					Value: aws.String("ValueA"),
				},
				&cloudformation.Tag{
					Key:   aws.String("KeyB"),
					Value: aws.String("ValueB"),
				},
				&cloudformation.Tag{
					Key:   aws.String("KeyC"),
					Value: aws.String("ValueC"),
				},
			},
			clusterYaml: `
stackTags:
  KeyA: ValueA
  KeyB: ValueB
  KeyC: ValueC
`,
		},
	}

	for _, testCase := range testCases {

		configBody := minimalConfigYaml + testCase.clusterYaml
		clusterConfig, err := config.ClusterFromBytes([]byte(configBody))
		if err != nil {
			t.Errorf("could not get valid cluster config: %v", err)
			continue
		}

		cluster := &Cluster{
			Cluster: *clusterConfig,
		}

		cfSvc := &dummyCloudformationService{
			ExpectedTags: testCase.expectedTags,
		}

		_, err = cluster.createStack(cfSvc, "")

		if err != nil {
			t.Errorf("error creating cluster: %v\nfor test case %+v", err, testCase)
		}
	}
}

type dummyCloudformationService struct {
	ExpectedTags []*cloudformation.Tag
}

func (cfSvc *dummyCloudformationService) CreateStack(req *cloudformation.CreateStackInput) (*cloudformation.CreateStackOutput, error) {

	if len(cfSvc.ExpectedTags) != len(req.Tags) {
		return nil, fmt.Errorf(
			"expected tag count does not match supplied tag count\nexpected=%v, supplied=%v",
			cfSvc.ExpectedTags,
			req.Tags,
		)
	}

	matchCnt := 0
	for _, eTag := range cfSvc.ExpectedTags {
		for _, tag := range req.Tags {
			if *tag.Key == *eTag.Key && *tag.Value == *eTag.Value {
				matchCnt++
				break
			}
		}
	}

	if matchCnt != len(cfSvc.ExpectedTags) {
		return nil, fmt.Errorf(
			"not all tags matched\nexpected=%v, observed=%v",
			cfSvc.ExpectedTags,
			req.Tags,
		)
	}

	resp := &cloudformation.CreateStackOutput{
		StackId: req.StackName,
	}

	return resp, nil
}
<|endoftext|>


<commit_before>// Copyright 2015-present Oursky Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package db

import (
	"context"
	"database/sql"

	sq "github.com/Masterminds/squirrel"
	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"

	"github.com/skygeario/skygear-server/pkg/core/errors"
)

type SQLExecutor struct {
	context   context.Context
	dbContext Context
}

func NewSQLExecutor(ctx context.Context, dbContext Context) SQLExecutor {
	return SQLExecutor{
		context:   ctx,
		dbContext: dbContext,
	}
}

func (e *SQLExecutor) ExecWith(sqlizeri sq.Sqlizer) (sql.Result, error) {
	db, err := e.dbContext.DB()
	if err != nil {
		return nil, err
	}
	sql, args, err := sqlizeri.ToSql()
	if err != nil {
		return nil, err
	}
	result, err := db.ExecContext(e.context, sql, args...)
	if err != nil {
		if isWriteConflict(err) {
			panic(ErrWriteConflict)
		}
		return nil, errors.WithDetails(err, errors.Details{"sql": errors.SafeDetail.Value(sql)})
	}
	return result, nil
}

func (e *SQLExecutor) QueryWith(sqlizeri sq.Sqlizer) (*sqlx.Rows, error) {
	db, err := e.dbContext.DB()
	if err != nil {
		return nil, err
	}
	sql, args, err := sqlizeri.ToSql()
	if err != nil {
		return nil, err
	}
	result, err := db.QueryxContext(e.context, sql, args...)
	if err != nil {
		if isWriteConflict(err) {
			panic(ErrWriteConflict)
		}
		return nil, errors.WithDetails(err, errors.Details{"sql": errors.SafeDetail.Value(sql)})
	}
	return result, nil
}

func (e *SQLExecutor) QueryRowWith(sqlizeri sq.Sqlizer) (*sqlx.Row, error) {
	db, err := e.dbContext.DB()
	if err != nil {
		return nil, err
	}
	sql, args, err := sqlizeri.ToSql()
	if err != nil {
		if isWriteConflict(err) {
			panic(ErrWriteConflict)
		}
		return nil, errors.WithDetails(err, errors.Details{"sql": errors.SafeDetail.Value(sql)})
	}
	return db.QueryRowxContext(e.context, sql, args...), nil
}

func isWriteConflict(err error) bool {
	var pqErr *pq.Error
	if errors.As(err, &pqErr) {
		return pqErr.Code == "40001" || pqErr.Code == "40P01"
	}
	return false
}
<commit_msg>Add notes on errors considered as write conflict<commit_after>// Copyright 2015-present Oursky Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package db

import (
	"context"
	"database/sql"

	sq "github.com/Masterminds/squirrel"
	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"

	"github.com/skygeario/skygear-server/pkg/core/errors"
)

type SQLExecutor struct {
	context   context.Context
	dbContext Context
}

func NewSQLExecutor(ctx context.Context, dbContext Context) SQLExecutor {
	return SQLExecutor{
		context:   ctx,
		dbContext: dbContext,
	}
}

func (e *SQLExecutor) ExecWith(sqlizeri sq.Sqlizer) (sql.Result, error) {
	db, err := e.dbContext.DB()
	if err != nil {
		return nil, err
	}
	sql, args, err := sqlizeri.ToSql()
	if err != nil {
		return nil, err
	}
	result, err := db.ExecContext(e.context, sql, args...)
	if err != nil {
		if isWriteConflict(err) {
			panic(ErrWriteConflict)
		}
		return nil, errors.WithDetails(err, errors.Details{"sql": errors.SafeDetail.Value(sql)})
	}
	return result, nil
}

func (e *SQLExecutor) QueryWith(sqlizeri sq.Sqlizer) (*sqlx.Rows, error) {
	db, err := e.dbContext.DB()
	if err != nil {
		return nil, err
	}
	sql, args, err := sqlizeri.ToSql()
	if err != nil {
		return nil, err
	}
	result, err := db.QueryxContext(e.context, sql, args...)
	if err != nil {
		if isWriteConflict(err) {
			panic(ErrWriteConflict)
		}
		return nil, errors.WithDetails(err, errors.Details{"sql": errors.SafeDetail.Value(sql)})
	}
	return result, nil
}

func (e *SQLExecutor) QueryRowWith(sqlizeri sq.Sqlizer) (*sqlx.Row, error) {
	db, err := e.dbContext.DB()
	if err != nil {
		return nil, err
	}
	sql, args, err := sqlizeri.ToSql()
	if err != nil {
		if isWriteConflict(err) {
			panic(ErrWriteConflict)
		}
		return nil, errors.WithDetails(err, errors.Details{"sql": errors.SafeDetail.Value(sql)})
	}
	return db.QueryRowxContext(e.context, sql, args...), nil
}

func isWriteConflict(err error) bool {
	var pqErr *pq.Error
	if errors.As(err, &pqErr) {
		// 40001: serialization_failure
		// 40P01: deadlock_detected
		return pqErr.Code == "40001" || pqErr.Code == "40P01"
	}
	return false
}
<|endoftext|>