<commit_before>package bird

import (
	"bufio"
	"errors"
	"fmt"
	"net"
	"reflect"
	"regexp"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"
)

// Check for Word_<IP> where every octate is seperated by "_", regardless of IP protocols
// Example match: "Mesh_192_168_56_101" or "Mesh_fd80_24e2_f998_72d7__2"
var bgpPeerRegex = regexp.MustCompile(`^(Global|Node|Mesh)_(.+)$`)

// Mapping the BIRD type extracted from the peer name to the display type.
var bgpTypeMap = map[string]string{
	"Global": "global",
	"Mesh":   "node-to-node mesh",
	"Node":   "node specific",
}

// Timeout for querying BIRD
var birdTimeOut = 2 * time.Second

// Expected BIRD protocol table columns
var birdExpectedHeadings = []string{"name", "proto", "table", "state", "since", "info"}

func GRInProgress(ipv string) (bool, error) {
	birdSuffix := ""
	if ipv == "6" {
		birdSuffix = "6"
	}

	// Try connecting to the BIRD socket in `/var/run/calico/` first to get the data
	c, err := net.Dial("unix", fmt.Sprintf("/var/run/calico/bird%s.ctl", birdSuffix))
	if err != nil {
		// If that fails, try connecting to BIRD socket in `/var/run/bird` (which is the
		// default socket location for BIRD install) for non-containerized installs
		log.Debugln("Failed to connect to BIRD socket in /var/run/calico, trying /var/run/bird")
		c, err = net.Dial("unix", fmt.Sprintf("/var/run/bird/bird%s.ctl", birdSuffix))
		if err != nil {
			return false, fmt.Errorf("Error querying BIRD: unable to connect to BIRDv%s socket: %v", ipv, err)
		}
	}
	defer c.Close()

	// To query the current state of the BGP peers, we connect to the BIRD
	// socket and send a "show protocols" message.  BIRD responds with
	// peer data in a table format.
	//
	// Send the request.
	_, err = c.Write([]byte("show status\n"))
	if err != nil {
		return false, fmt.Errorf("Error executing command: unable to write to BIRD socket: %s", err)
	}

	// The following is sample output from BIRD
	//
	// 0001 BIRD v0.3.2+birdv1.6.3 ready.
	// 1000-BIRD v0.3.2+birdv1.6.3
	// 1011-Router ID is 172.18.0.4
	// 	 Current server time is 2018-04-03 22:59:51
	// 	 Last reboot on 2018-04-03 22:59:26
	// 	 Last reconfiguration on 2018-04-03 22:59:26
	// 0024-Graceful restart recovery in progress
	// 	   Waiting for 1 protocols to recover
	// 	   Wait timer is 215/240
	// 0013 Daemon is up and running
	scanner := bufio.NewScanner(c)

	// Set a time-out for reading from the socket connection.
	c.SetReadDeadline(time.Now().Add(birdTimeOut))

	for scanner.Scan() {
		// Process the next line that has been read by the scanner.
		str := scanner.Text()
		log.Debugf("Read: %s\n", str)
		if strings.HasPrefix(str, "0013") {
			// "0013" code is the final line and indicates that BIRD is ready,
			break
		} else if strings.HasPrefix(str, "0001") {
			// "0001" code means BIRD is ready.
		} else if strings.HasPrefix(str, "1000") {
			// "1000" code shows the BIRD version
		} else if strings.HasPrefix(str, "1011") {
			// "1011" shows uptime
		} else if strings.HasPrefix(str, "0024") {
			// "0024" code indicates the start of a graceful restart status report.
			// This means a GR is in progress.
			return true, nil
		} else if strings.HasPrefix(str, " ") {
			// Row starting with a " " is another row of data.
		} else {
			// Format of row is unexpected.
			return false, fmt.Errorf("unexpected output line from BIRD: '%s'", str)
		}

		// Before reading the next line, adjust the time-out for
		// reading from the socket connection.
		c.SetReadDeadline(time.Now().Add(birdTimeOut))
	}

	return false, scanner.Err()
}

// bgpPeer is a structure containing details about a BGP peer.
type bgpPeer struct {
	PeerIP   string
	PeerType string
	State    string
	Since    string
	BGPState string
	Info     string
}

func GetPeers(ipv string) ([]bgpPeer, error) {
	log.Debugf("Print BIRD peers for IPv%s", ipv)
	birdSuffix := ""
	if ipv == "6" {
		birdSuffix = "6"
	}

	// Try connecting to the BIRD socket in `/var/run/calico/` first to get the data
	c, err := net.Dial("unix", "/var/run/calico/bird.ctl")
	if err != nil {
		// If that fails, try connecting to BIRD socket in `/var/run/bird` (which is the
		// default socket location for BIRD install) for non-containerized installs
		log.Debugln("Failed to connect to BIRD socket in /var/run/calico, trying /var/run/bird")
		c, err = net.Dial("unix", fmt.Sprintf("/var/run/bird/bird%s.ctl", birdSuffix))
		if err != nil {
			return nil, fmt.Errorf("Error querying BIRD: unable to connect to BIRDv%s socket: %v", ipv, err)
		}
	}
	defer c.Close()

	// To query the current state of the BGP peers, we connect to the BIRD
	// socket and send a "show protocols" message.  BIRD responds with
	// peer data in a table format.
	//
	// Send the request.
	_, err = c.Write([]byte("show protocols\n"))
	if err != nil {
		return nil, fmt.Errorf("Error executing command: unable to write to BIRD socket: %s\n", err)
	}

	// Scan the output and collect parsed BGP peers
	log.Debugln("Reading output from BIRD")
	peers, err := scanBIRDPeers(ipv, c)
	if err != nil {
		return nil, fmt.Errorf("Error executing command: %v", err)
	}

	return peers, nil
}

// scanBIRDPeers scans through BIRD output to return a slice of bgpPeer
// structs.
//
// We split this out from the main printBIRDPeers() function to allow us to
// test this processing in isolation.
func scanBIRDPeers(ipv string, conn net.Conn) ([]bgpPeer, error) {
	// Determine the separator to use for an IP address, based on the
	// IP version.
	ipSep := "."
	if ipv == "6" {
		ipSep = ":"
	}

	// The following is sample output from BIRD
	//
	// 	0001 BIRD 1.5.0 ready.
	// 	2002-name     proto    table    state  since       info
	// 	1002-kernel1  Kernel   master   up     2016-11-21
	//  	 device1  Device   master   up     2016-11-21
	//  	 direct1  Direct   master   up     2016-11-21
	//  	 Mesh_172_17_8_102 BGP      master   up     2016-11-21  Established
	// 	0000
	scanner := bufio.NewScanner(conn)
	peers := []bgpPeer{}

	// Set a time-out for reading from the socket connection.
	conn.SetReadDeadline(time.Now().Add(birdTimeOut))

	for scanner.Scan() {
		// Process the next line that has been read by the scanner.
		str := scanner.Text()
		log.Debugf("Read: %s\n", str)

		if strings.HasPrefix(str, "0000") {
			// "0000" means end of data
			break
		} else if strings.HasPrefix(str, "0001") {
			// "0001" code means BIRD is ready.
		} else if strings.HasPrefix(str, "2002") {
			// "2002" code means start of headings
			f := strings.Fields(str[5:])
			if !reflect.DeepEqual(f, birdExpectedHeadings) {
				return nil, errors.New("unknown BIRD table output format")
			}
		} else if strings.HasPrefix(str, "1002") {
			// "1002" code means first row of data.
			peer := bgpPeer{}
			if peer.unmarshalBIRD(str[5:], ipSep) {
				peers = append(peers, peer)
			}
		} else if strings.HasPrefix(str, " ") {
			// Row starting with a " " is another row of data.
			peer := bgpPeer{}
			if peer.unmarshalBIRD(str[1:], ipSep) {
				peers = append(peers, peer)
			}
		} else {
			// Format of row is unexpected.
			return nil, fmt.Errorf("unexpected output line from BIRD: '%s'", str)
		}

		// Before reading the next line, adjust the time-out for
		// reading from the socket connection.
		conn.SetReadDeadline(time.Now().Add(birdTimeOut))
	}

	return peers, scanner.Err()
}

// Unmarshal a peer from a line in the BIRD protocol output.  Returns true if
// successful, false otherwise.
func (b *bgpPeer) unmarshalBIRD(line, ipSep string) bool {
	// Split into fields.  We expect at least 6 columns:
	// 	name, proto, table, state, since and info.
	// The info column contains the BGP state plus possibly some additional
	// info (which will be columns > 6).
	//
	// Peer names will be of the format described by bgpPeerRegex.
	log.Debugf("Parsing line: %s", line)
	columns := strings.Fields(line)
	if len(columns) < 6 {
		log.Debugf("Not a valid line: fewer than 6 columns")
		return false
	}
	if columns[1] != "BGP" {
		log.Debugf("Not a valid line: protocol is not BGP")
		return false
	}

	// Check the name of the peer is of the correct format.  This regex
	// returns two components:
	// -  A type (Global|Node|Mesh) which we can map to a display type
	// -  An IP address (with _ separating the octets)
	sm := bgpPeerRegex.FindStringSubmatch(columns[0])
	if len(sm) != 3 {
		log.Debugf("Not a valid line: peer name '%s' is not correct format", columns[0])
		return false
	}
	var ok bool
	b.PeerIP = strings.Replace(sm[2], "_", ipSep, -1)
	if b.PeerType, ok = bgpTypeMap[sm[1]]; !ok {
		log.Debugf("Not a valid line: peer type '%s' is not recognized", sm[1])
		return false
	}

	// Store remaining columns (piecing back together the info string)
	b.State = columns[3]
	b.Since = columns[4]
	b.BGPState = columns[5]
	if len(columns) > 6 {
		b.Info = strings.Join(columns[6:], " ")
	}

	return true
}
<commit_msg>Fix bird6 readiness<commit_after>package bird

import (
	"bufio"
	"errors"
	"fmt"
	"net"
	"reflect"
	"regexp"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"
)

// Check for Word_<IP> where every octate is seperated by "_", regardless of IP protocols
// Example match: "Mesh_192_168_56_101" or "Mesh_fd80_24e2_f998_72d7__2"
var bgpPeerRegex = regexp.MustCompile(`^(Global|Node|Mesh)_(.+)$`)

// Mapping the BIRD type extracted from the peer name to the display type.
var bgpTypeMap = map[string]string{
	"Global": "global",
	"Mesh":   "node-to-node mesh",
	"Node":   "node specific",
}

// Timeout for querying BIRD
var birdTimeOut = 2 * time.Second

// Expected BIRD protocol table columns
var birdExpectedHeadings = []string{"name", "proto", "table", "state", "since", "info"}

func GRInProgress(ipv string) (bool, error) {
	birdSuffix := ""
	if ipv == "6" {
		birdSuffix = "6"
	}

	// Try connecting to the BIRD socket in `/var/run/calico/` first to get the data
	c, err := net.Dial("unix", fmt.Sprintf("/var/run/calico/bird%s.ctl", birdSuffix))
	if err != nil {
		// If that fails, try connecting to BIRD socket in `/var/run/bird` (which is the
		// default socket location for BIRD install) for non-containerized installs
		log.Debugln("Failed to connect to BIRD socket in /var/run/calico, trying /var/run/bird")
		c, err = net.Dial("unix", fmt.Sprintf("/var/run/bird/bird%s.ctl", birdSuffix))
		if err != nil {
			return false, fmt.Errorf("Error querying BIRD: unable to connect to BIRDv%s socket: %v", ipv, err)
		}
	}
	defer c.Close()

	// To query the current state of the BGP peers, we connect to the BIRD
	// socket and send a "show protocols" message.  BIRD responds with
	// peer data in a table format.
	//
	// Send the request.
	_, err = c.Write([]byte("show status\n"))
	if err != nil {
		return false, fmt.Errorf("Error executing command: unable to write to BIRD socket: %s", err)
	}

	// The following is sample output from BIRD
	//
	// 0001 BIRD v0.3.2+birdv1.6.3 ready.
	// 1000-BIRD v0.3.2+birdv1.6.3
	// 1011-Router ID is 172.18.0.4
	// 	 Current server time is 2018-04-03 22:59:51
	// 	 Last reboot on 2018-04-03 22:59:26
	// 	 Last reconfiguration on 2018-04-03 22:59:26
	// 0024-Graceful restart recovery in progress
	// 	   Waiting for 1 protocols to recover
	// 	   Wait timer is 215/240
	// 0013 Daemon is up and running
	scanner := bufio.NewScanner(c)

	// Set a time-out for reading from the socket connection.
	c.SetReadDeadline(time.Now().Add(birdTimeOut))

	for scanner.Scan() {
		// Process the next line that has been read by the scanner.
		str := scanner.Text()
		log.Debugf("Read: %s\n", str)
		if strings.HasPrefix(str, "0013") {
			// "0013" code is the final line and indicates that BIRD is ready,
			break
		} else if strings.HasPrefix(str, "0001") {
			// "0001" code means BIRD is ready.
		} else if strings.HasPrefix(str, "1000") {
			// "1000" code shows the BIRD version
		} else if strings.HasPrefix(str, "1011") {
			// "1011" shows uptime
		} else if strings.HasPrefix(str, "0024") {
			// "0024" code indicates the start of a graceful restart status report.
			// This means a GR is in progress.
			return true, nil
		} else if strings.HasPrefix(str, " ") {
			// Row starting with a " " is another row of data.
		} else {
			// Format of row is unexpected.
			return false, fmt.Errorf("unexpected output line from BIRD: '%s'", str)
		}

		// Before reading the next line, adjust the time-out for
		// reading from the socket connection.
		c.SetReadDeadline(time.Now().Add(birdTimeOut))
	}

	return false, scanner.Err()
}

// bgpPeer is a structure containing details about a BGP peer.
type bgpPeer struct {
	PeerIP   string
	PeerType string
	State    string
	Since    string
	BGPState string
	Info     string
}

func GetPeers(ipv string) ([]bgpPeer, error) {
	log.Debugf("Print BIRD peers for IPv%s", ipv)
	birdSuffix := ""
	if ipv == "6" {
		birdSuffix = "6"
	}

	// Try connecting to the BIRD socket in `/var/run/calico/` first to get the data
	c, err := net.Dial("unix", fmt.Sprintf("/var/run/calico/bird%s.ctl", birdSuffix))
	if err != nil {
		// If that fails, try connecting to BIRD socket in `/var/run/bird` (which is the
		// default socket location for BIRD install) for non-containerized installs
		log.Debugln("Failed to connect to BIRD socket in /var/run/calico, trying /var/run/bird")
		c, err = net.Dial("unix", fmt.Sprintf("/var/run/bird/bird%s.ctl", birdSuffix))
		if err != nil {
			return nil, fmt.Errorf("Error querying BIRD: unable to connect to BIRDv%s socket: %v", ipv, err)
		}
	}
	defer c.Close()

	// To query the current state of the BGP peers, we connect to the BIRD
	// socket and send a "show protocols" message.  BIRD responds with
	// peer data in a table format.
	//
	// Send the request.
	_, err = c.Write([]byte("show protocols\n"))
	if err != nil {
		return nil, fmt.Errorf("Error executing command: unable to write to BIRD socket: %s\n", err)
	}

	// Scan the output and collect parsed BGP peers
	log.Debugln("Reading output from BIRD")
	peers, err := scanBIRDPeers(ipv, c)
	if err != nil {
		return nil, fmt.Errorf("Error executing command: %v", err)
	}

	return peers, nil
}

// scanBIRDPeers scans through BIRD output to return a slice of bgpPeer
// structs.
//
// We split this out from the main printBIRDPeers() function to allow us to
// test this processing in isolation.
func scanBIRDPeers(ipv string, conn net.Conn) ([]bgpPeer, error) {
	// Determine the separator to use for an IP address, based on the
	// IP version.
	ipSep := "."
	if ipv == "6" {
		ipSep = ":"
	}

	// The following is sample output from BIRD
	//
	// 	0001 BIRD 1.5.0 ready.
	// 	2002-name     proto    table    state  since       info
	// 	1002-kernel1  Kernel   master   up     2016-11-21
	//  	 device1  Device   master   up     2016-11-21
	//  	 direct1  Direct   master   up     2016-11-21
	//  	 Mesh_172_17_8_102 BGP      master   up     2016-11-21  Established
	// 	0000
	scanner := bufio.NewScanner(conn)
	peers := []bgpPeer{}

	// Set a time-out for reading from the socket connection.
	conn.SetReadDeadline(time.Now().Add(birdTimeOut))

	for scanner.Scan() {
		// Process the next line that has been read by the scanner.
		str := scanner.Text()
		log.Debugf("Read: %s\n", str)

		if strings.HasPrefix(str, "0000") {
			// "0000" means end of data
			break
		} else if strings.HasPrefix(str, "0001") {
			// "0001" code means BIRD is ready.
		} else if strings.HasPrefix(str, "2002") {
			// "2002" code means start of headings
			f := strings.Fields(str[5:])
			if !reflect.DeepEqual(f, birdExpectedHeadings) {
				return nil, errors.New("unknown BIRD table output format")
			}
		} else if strings.HasPrefix(str, "1002") {
			// "1002" code means first row of data.
			peer := bgpPeer{}
			if peer.unmarshalBIRD(str[5:], ipSep) {
				peers = append(peers, peer)
			}
		} else if strings.HasPrefix(str, " ") {
			// Row starting with a " " is another row of data.
			peer := bgpPeer{}
			if peer.unmarshalBIRD(str[1:], ipSep) {
				peers = append(peers, peer)
			}
		} else {
			// Format of row is unexpected.
			return nil, fmt.Errorf("unexpected output line from BIRD: '%s'", str)
		}

		// Before reading the next line, adjust the time-out for
		// reading from the socket connection.
		conn.SetReadDeadline(time.Now().Add(birdTimeOut))
	}

	return peers, scanner.Err()
}

// Unmarshal a peer from a line in the BIRD protocol output.  Returns true if
// successful, false otherwise.
func (b *bgpPeer) unmarshalBIRD(line, ipSep string) bool {
	// Split into fields.  We expect at least 6 columns:
	// 	name, proto, table, state, since and info.
	// The info column contains the BGP state plus possibly some additional
	// info (which will be columns > 6).
	//
	// Peer names will be of the format described by bgpPeerRegex.
	log.Debugf("Parsing line: %s", line)
	columns := strings.Fields(line)
	if len(columns) < 6 {
		log.Debugf("Not a valid line: fewer than 6 columns")
		return false
	}
	if columns[1] != "BGP" {
		log.Debugf("Not a valid line: protocol is not BGP")
		return false
	}

	// Check the name of the peer is of the correct format.  This regex
	// returns two components:
	// -  A type (Global|Node|Mesh) which we can map to a display type
	// -  An IP address (with _ separating the octets)
	sm := bgpPeerRegex.FindStringSubmatch(columns[0])
	if len(sm) != 3 {
		log.Debugf("Not a valid line: peer name '%s' is not correct format", columns[0])
		return false
	}
	var ok bool
	b.PeerIP = strings.Replace(sm[2], "_", ipSep, -1)
	if b.PeerType, ok = bgpTypeMap[sm[1]]; !ok {
		log.Debugf("Not a valid line: peer type '%s' is not recognized", sm[1])
		return false
	}

	// Store remaining columns (piecing back together the info string)
	b.State = columns[3]
	b.Since = columns[4]
	b.BGPState = columns[5]
	if len(columns) > 6 {
		b.Info = strings.Join(columns[6:], " ")
	}

	return true
}
<|endoftext|>


<commit_before>package network_test

import (
	"github.com/APTrust/exchange/dpn"
	"github.com/APTrust/exchange/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"net/http"
	"net/http/httptest"
	"testing"
)

var objectTypes = []dpn.DPNObjectType{
	dpn.DPNTypeBag,
	dpn.DPNTypeDigest,
	dpn.DPNTypeFixityCheck,
	dpn.DPNTypeIngest,
	dpn.DPNTypeMember,
	dpn.DPNTypeNode,
	dpn.DPNTypeReplication,
	dpn.DPNTypeRestore,
}

func TestNewDPNResponse(t *testing.T) {
	for _, objType := range objectTypes {
		resp := dpn.NewDPNResponse(objType)
		assert.NotNil(t, resp)
		assert.Equal(t, objType, resp.ObjectType())
		assert.Equal(t, 0, resp.Count)
		assert.Nil(t, resp.Next)
		assert.Nil(t, resp.Previous)
	}
}

func TestRawResponseData(t *testing.T) {
	// nodeGetHandler is defined in dpn_rest_client_test.go
	testServer := httptest.NewServer(http.HandlerFunc(nodeGetHandler))
	defer testServer.Close()

	// configFile is defined in dpn_rest_client_test.go
	config, err := models.LoadConfigFile(configFile)
	require.Nil(t, err)
	client, err := dpn.NewDPNRestClient(
		testServer.URL,
		"",
		"",
		config.DPN.LocalNode,
		config.DPN)
	if err != nil {
		t.Error(err)
		return
	}
	resp := client.NodeGet("luna")

	// Should be able to call repeatedly without error.
	// Incorrect implementation would try to read from
	// closed network socket.
	for i := 0; i < 3; i++ {
		bytes, err := resp.RawResponseData()
		assert.NotNil(t, bytes)
		assert.NotEmpty(t, bytes)
		assert.Nil(t, err)
	}
}

func TestObjectType(t *testing.T) {
	for _, objType := range objectTypes {
		resp := dpn.NewDPNResponse(objType)
		assert.Equal(t, objType, resp.ObjectType())
	}
}

func TestHasNextPage(t *testing.T) {
	resp := dpn.NewDPNResponse(dpn.DPNTypeNode)
	assert.False(t, resp.HasNextPage())
	link := "http://example.com"
	resp.Next = &link
	assert.True(t, resp.HasNextPage())
}

func TestHasPreviousPage(t *testing.T) {
	resp := dpn.NewDPNResponse(dpn.DPNTypeNode)
	assert.False(t, resp.HasPreviousPage())
	link := "http://example.com"
	resp.Previous = &link
	assert.True(t, resp.HasPreviousPage())
}

func TestParamsForNextPage(t *testing.T) {
	resp := dpn.NewDPNResponse(dpn.DPNTypeNode)
	link := "http://example.com?name=college.edu&page=6&per_page=20"
	resp.Next = &link
	params := resp.ParamsForNextPage()
	assert.Equal(t, 3, len(params))
	assert.Equal(t, "college.edu", params.Get("name"))
	assert.Equal(t, "6", params.Get("page"))
	assert.Equal(t, "20", params.Get("per_page"))
}

func TestParamsForPreviousPage(t *testing.T) {
	resp := dpn.NewDPNResponse(dpn.DPNTypeNode)
	link := "http://example.com?name=college.edu&page=6&per_page=20"
	resp.Previous = &link
	params := resp.ParamsForPreviousPage()
	assert.Equal(t, 3, len(params))
	assert.Equal(t, "college.edu", params.Get("name"))
	assert.Equal(t, "6", params.Get("page"))
	assert.Equal(t, "20", params.Get("per_page"))
}
<commit_msg>Namespace changes<commit_after>package network_test

import (
	"github.com/APTrust/exchange/dpn"
	"github.com/APTrust/exchange/dpn/network"
	"github.com/APTrust/exchange/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"net/http"
	"net/http/httptest"
	"testing"
)

var objectTypes = []dpn.DPNObjectType{
	dpn.DPNTypeBag,
	dpn.DPNTypeDigest,
	dpn.DPNTypeFixityCheck,
	dpn.DPNTypeIngest,
	dpn.DPNTypeMember,
	dpn.DPNTypeNode,
	dpn.DPNTypeReplication,
	dpn.DPNTypeRestore,
}

func TestNewDPNResponse(t *testing.T) {
	for _, objType := range objectTypes {
		resp := network.NewDPNResponse(objType)
		assert.NotNil(t, resp)
		assert.Equal(t, objType, resp.ObjectType())
		assert.Equal(t, 0, resp.Count)
		assert.Nil(t, resp.Next)
		assert.Nil(t, resp.Previous)
	}
}

func TestRawResponseData(t *testing.T) {
	// nodeGetHandler is defined in dpn_rest_client_test.go
	testServer := httptest.NewServer(http.HandlerFunc(nodeGetHandler))
	defer testServer.Close()

	// configFile is defined in dpn_rest_client_test.go
	config, err := models.LoadConfigFile(configFile)
	require.Nil(t, err)
	client, err := network.NewDPNRestClient(
		testServer.URL,
		"",
		"",
		config.DPN.LocalNode,
		config.DPN)
	if err != nil {
		t.Error(err)
		return
	}
	resp := client.NodeGet("luna")

	// Should be able to call repeatedly without error.
	// Incorrect implementation would try to read from
	// closed network socket.
	for i := 0; i < 3; i++ {
		bytes, err := resp.RawResponseData()
		assert.NotNil(t, bytes)
		assert.NotEmpty(t, bytes)
		assert.Nil(t, err)
	}
}

func TestObjectType(t *testing.T) {
	for _, objType := range objectTypes {
		resp := network.NewDPNResponse(objType)
		assert.Equal(t, objType, resp.ObjectType())
	}
}

func TestHasNextPage(t *testing.T) {
	resp := network.NewDPNResponse(dpn.DPNTypeNode)
	assert.False(t, resp.HasNextPage())
	link := "http://example.com"
	resp.Next = &link
	assert.True(t, resp.HasNextPage())
}

func TestHasPreviousPage(t *testing.T) {
	resp := network.NewDPNResponse(dpn.DPNTypeNode)
	assert.False(t, resp.HasPreviousPage())
	link := "http://example.com"
	resp.Previous = &link
	assert.True(t, resp.HasPreviousPage())
}

func TestParamsForNextPage(t *testing.T) {
	resp := network.NewDPNResponse(dpn.DPNTypeNode)
	link := "http://example.com?name=college.edu&page=6&per_page=20"
	resp.Next = &link
	params := resp.ParamsForNextPage()
	assert.Equal(t, 3, len(params))
	assert.Equal(t, "college.edu", params.Get("name"))
	assert.Equal(t, "6", params.Get("page"))
	assert.Equal(t, "20", params.Get("per_page"))
}

func TestParamsForPreviousPage(t *testing.T) {
	resp := network.NewDPNResponse(dpn.DPNTypeNode)
	link := "http://example.com?name=college.edu&page=6&per_page=20"
	resp.Previous = &link
	params := resp.ParamsForPreviousPage()
	assert.Equal(t, 3, len(params))
	assert.Equal(t, "college.edu", params.Get("name"))
	assert.Equal(t, "6", params.Get("page"))
	assert.Equal(t, "20", params.Get("per_page"))
}
<|endoftext|>


<commit_before>package hoverfly_test

import (
	"bytes"
	"encoding/json"
	"io/ioutil"

	"github.com/SpectoLabs/hoverfly/functional-tests"
	"github.com/dghubble/sling"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("/api/v2/simulation/schema", func() {

	var (
		hoverfly *functional_tests.Hoverfly
	)

	BeforeEach(func() {
		hoverfly = functional_tests.NewHoverfly()
		hoverfly.Start()
	})

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

	Context("GET", func() {

		It("Should get the JSON schema", func() {
			req := sling.New().Get("http://localhost:" + hoverfly.GetAdminPort() + "/api/v2/simulation/schema")
			res := functional_tests.DoRequest(req)
			Expect(res.StatusCode).To(Equal(200))

			fileBytes, _ := ioutil.ReadFile("../../schema.json")
			fileBuffer := new(bytes.Buffer)
			json.Compact(fileBuffer, fileBytes)

			responseJson, err := ioutil.ReadAll(res.Body)
			Expect(err).To(BeNil())

			Expect(responseJson).To(Equal(fileBuffer.Bytes()))
		})
	})
})
<commit_msg>Update functional test to error correctly if a needed file is missing<commit_after>package hoverfly_test

import (
	"bytes"
	"encoding/json"
	"io/ioutil"

	"github.com/SpectoLabs/hoverfly/functional-tests"
	"github.com/dghubble/sling"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("/api/v2/simulation/schema", func() {

	var (
		hoverfly *functional_tests.Hoverfly
	)

	BeforeEach(func() {
		hoverfly = functional_tests.NewHoverfly()
		hoverfly.Start()
	})

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

	Context("GET", func() {

		It("Should get the JSON schema", func() {
			req := sling.New().Get("http://localhost:" + hoverfly.GetAdminPort() + "/api/v2/simulation/schema")
			res := functional_tests.DoRequest(req)
			Expect(res.StatusCode).To(Equal(200))

			fileBytes, err := ioutil.ReadFile("../../schema.json")
			Expect(err).To(BeNil(), "schema.json not found")

			fileBuffer := new(bytes.Buffer)
			json.Compact(fileBuffer, fileBytes)

			responseJson, err := ioutil.ReadAll(res.Body)
			Expect(err).To(BeNil())

			Expect(responseJson).To(Equal(fileBuffer.Bytes()))
		})
	})
})
<|endoftext|>


<commit_before>package client

import (
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	pb "github.com/google/go-tpm-tools/proto/attest"
)

const (
	maxIssuingCertificateURLs = 3
	maxCertChainDepth         = 4
)

// AttestOpts allows for customizing the functionality of Attest.
type AttestOpts struct {
	// A unique, application-specific nonce used to guarantee freshness of the
	// attestation. This must not be empty, and should generally be long enough
	// to make brute force attacks infeasible.
	//
	// For security reasons, applications should not allow for attesting with
	// arbitrary, externally-provided nonces. The nonce should be prefixed or
	// otherwise bound (i.e. via a KDF) to application-specific data. For more
	// information on why this is an issue, see this paper on robust remote
	// attestation protocols:
	// https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.4562&rep=rep1&type=pdf
	Nonce []byte
	// TCG Canonical Event Log to add to the attestation.
	// Currently, we only support PCR replay for PCRs orthogonal to those in the
	// firmware event log, where PCRs 0-9 and 14 are often measured. If the two
	// logs overlap, server-side verification using this library may fail.
	CanonicalEventLog []byte
	// Indicates whether the AK certificate chain should be retrieved for validation.
	// If true, Key.Attest() will contruct the certificate chain by making GET requests to
	// the contents of Key.cert.IssuingCertificateURL.
	FetchCertChain bool
}

// Given a certificate, iterates through its IssuingCertificateURLs and returns the certificate
// that signed it. Returns an error if a valid signing certificate is not found.
func fetchIssuingCertificate(cert *x509.Certificate) (*x509.Certificate, error) {
	if cert == nil {
		return nil, fmt.Errorf("provided certificate is nil")
	}
	// For each URL, fetch and parse the certificate, then verify whether it signed cert.
	// If successful, return the parsed certificate. If any step in this process fails, try the next url.
	for _, url := range cert.IssuingCertificateURL {
		resp, err := http.Get(url)
		if err != nil {
			log.Printf("failed to retrieve certificate at %v: %v\n", url, err)
			continue
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			log.Printf("certificate retrieval from %s returned non-OK status: %v\n", url, resp.StatusCode)
			continue
		}
		certBytes, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Printf("failed to read response body from %s: %v\n", url, err)
			continue
		}

		parsedCert, err := x509.ParseCertificate(certBytes)
		if err != nil {
			log.Printf("failed to parse response from %s into a certificate: %v\n", url, err)
			continue
		}

		// Check if the parsed certificate signed the current one.
		err = cert.CheckSignatureFrom(parsedCert)
		if err == nil {
			return parsedCert, nil
		}
	}

	return nil, fmt.Errorf("did not find valid signing certificate")
}

// Constructs the certificate chain for the key's certificate, using the provided HTTP client.
func (k *Key) getCertificateChain() ([][]byte, error) {
	if len(k.cert.IssuingCertificateURL) > maxIssuingCertificateURLs {
		return nil, fmt.Errorf("key cert contains too many issuing URLS: got %v, expect no more than %v", len(k.cert.IssuingCertificateURL), maxIssuingCertificateURLs)
	}

	var certs [][]byte
	currentCert := k.cert
	for i := 0; i < maxCertChainDepth; i++ {
		issuingCert, err := fetchIssuingCertificate(currentCert)
		if err != nil {
			return nil, fmt.Errorf("error retrieving certificate chain: %w", err)
		}

		certs = append(certs, issuingCert.Raw)
		// Stop searching if no IssuingCertificateURLs found.
		if len(issuingCert.IssuingCertificateURL) == 0 {
			break
		}

		currentCert = issuingCert
	}

	return certs, nil
}

// Attest generates an Attestation containing the TCG Event Log and a Quote over
// all PCR banks. The provided nonce can be used to guarantee freshness of the
// attestation. This function will return an error if the key is not a
// restricted signing key.
//
// AttestOpts is used for additional configuration of the Attestation process.
// This is primarily used to pass the attestation's nonce:
//
//   attestation, err := key.Attest(client.AttestOpts{Nonce: my_nonce})
func (k *Key) Attest(opts AttestOpts) (*pb.Attestation, error) {
	if len(opts.Nonce) == 0 {
		return nil, fmt.Errorf("provided nonce must not be empty")
	}
	sels, err := implementedPCRs(k.rw)
	if err != nil {
		return nil, err
	}

	attestation := pb.Attestation{}
	if attestation.AkPub, err = k.PublicArea().Encode(); err != nil {
		return nil, fmt.Errorf("failed to encode public area: %w", err)
	}
	attestation.AkCert = k.CertDERBytes()
	for _, sel := range sels {
		quote, err := k.Quote(sel, opts.Nonce)
		if err != nil {
			return nil, err
		}
		attestation.Quotes = append(attestation.Quotes, quote)
	}
	if attestation.EventLog, err = GetEventLog(k.rw); err != nil {
		return nil, fmt.Errorf("failed to retrieve TCG Event Log: %w", err)
	}
	if len(opts.CanonicalEventLog) != 0 {
		attestation.CanonicalEventLog = opts.CanonicalEventLog
	}

	// Construct certficate chain.
	if opts.FetchCertChain {
		if k.cert == nil {
			return nil, fmt.Errorf("cert chain fetching option was set but no AK cert found in key")
		}

		attestation.IntermediateCerts, err = k.getCertificateChain()
		if err != nil {
			return nil, fmt.Errorf("error creating intermediate cert chain: %w", err)
		}
	}

	return &attestation, nil
}
<commit_msg>Fix lint error (typo)<commit_after>package client

import (
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	pb "github.com/google/go-tpm-tools/proto/attest"
)

const (
	maxIssuingCertificateURLs = 3
	maxCertChainDepth         = 4
)

// AttestOpts allows for customizing the functionality of Attest.
type AttestOpts struct {
	// A unique, application-specific nonce used to guarantee freshness of the
	// attestation. This must not be empty, and should generally be long enough
	// to make brute force attacks infeasible.
	//
	// For security reasons, applications should not allow for attesting with
	// arbitrary, externally-provided nonces. The nonce should be prefixed or
	// otherwise bound (i.e. via a KDF) to application-specific data. For more
	// information on why this is an issue, see this paper on robust remote
	// attestation protocols:
	// https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.4562&rep=rep1&type=pdf
	Nonce []byte
	// TCG Canonical Event Log to add to the attestation.
	// Currently, we only support PCR replay for PCRs orthogonal to those in the
	// firmware event log, where PCRs 0-9 and 14 are often measured. If the two
	// logs overlap, server-side verification using this library may fail.
	CanonicalEventLog []byte
	// Indicates whether the AK certificate chain should be retrieved for validation.
	// If true, Key.Attest() will construct the certificate chain by making GET requests to
	// the contents of Key.cert.IssuingCertificateURL.
	FetchCertChain bool
}

// Given a certificate, iterates through its IssuingCertificateURLs and returns the certificate
// that signed it. Returns an error if a valid signing certificate is not found.
func fetchIssuingCertificate(cert *x509.Certificate) (*x509.Certificate, error) {
	if cert == nil {
		return nil, fmt.Errorf("provided certificate is nil")
	}
	// For each URL, fetch and parse the certificate, then verify whether it signed cert.
	// If successful, return the parsed certificate. If any step in this process fails, try the next url.
	for _, url := range cert.IssuingCertificateURL {
		resp, err := http.Get(url)
		if err != nil {
			log.Printf("failed to retrieve certificate at %v: %v\n", url, err)
			continue
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			log.Printf("certificate retrieval from %s returned non-OK status: %v\n", url, resp.StatusCode)
			continue
		}
		certBytes, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Printf("failed to read response body from %s: %v\n", url, err)
			continue
		}

		parsedCert, err := x509.ParseCertificate(certBytes)
		if err != nil {
			log.Printf("failed to parse response from %s into a certificate: %v\n", url, err)
			continue
		}

		// Check if the parsed certificate signed the current one.
		err = cert.CheckSignatureFrom(parsedCert)
		if err == nil {
			return parsedCert, nil
		}
	}

	return nil, fmt.Errorf("did not find valid signing certificate")
}

// Constructs the certificate chain for the key's certificate, using the provided HTTP client.
func (k *Key) getCertificateChain() ([][]byte, error) {
	if len(k.cert.IssuingCertificateURL) > maxIssuingCertificateURLs {
		return nil, fmt.Errorf("key cert contains too many issuing URLS: got %v, expect no more than %v", len(k.cert.IssuingCertificateURL), maxIssuingCertificateURLs)
	}

	var certs [][]byte
	currentCert := k.cert
	for i := 0; i < maxCertChainDepth; i++ {
		issuingCert, err := fetchIssuingCertificate(currentCert)
		if err != nil {
			return nil, fmt.Errorf("error retrieving certificate chain: %w", err)
		}

		certs = append(certs, issuingCert.Raw)
		// Stop searching if no IssuingCertificateURLs found.
		if len(issuingCert.IssuingCertificateURL) == 0 {
			break
		}

		currentCert = issuingCert
	}

	return certs, nil
}

// Attest generates an Attestation containing the TCG Event Log and a Quote over
// all PCR banks. The provided nonce can be used to guarantee freshness of the
// attestation. This function will return an error if the key is not a
// restricted signing key.
//
// AttestOpts is used for additional configuration of the Attestation process.
// This is primarily used to pass the attestation's nonce:
//
//   attestation, err := key.Attest(client.AttestOpts{Nonce: my_nonce})
func (k *Key) Attest(opts AttestOpts) (*pb.Attestation, error) {
	if len(opts.Nonce) == 0 {
		return nil, fmt.Errorf("provided nonce must not be empty")
	}
	sels, err := implementedPCRs(k.rw)
	if err != nil {
		return nil, err
	}

	attestation := pb.Attestation{}
	if attestation.AkPub, err = k.PublicArea().Encode(); err != nil {
		return nil, fmt.Errorf("failed to encode public area: %w", err)
	}
	attestation.AkCert = k.CertDERBytes()
	for _, sel := range sels {
		quote, err := k.Quote(sel, opts.Nonce)
		if err != nil {
			return nil, err
		}
		attestation.Quotes = append(attestation.Quotes, quote)
	}
	if attestation.EventLog, err = GetEventLog(k.rw); err != nil {
		return nil, fmt.Errorf("failed to retrieve TCG Event Log: %w", err)
	}
	if len(opts.CanonicalEventLog) != 0 {
		attestation.CanonicalEventLog = opts.CanonicalEventLog
	}

	// Construct certficate chain.
	if opts.FetchCertChain {
		if k.cert == nil {
			return nil, fmt.Errorf("cert chain fetching option was set but no AK cert found in key")
		}

		attestation.IntermediateCerts, err = k.getCertificateChain()
		if err != nil {
			return nil, fmt.Errorf("error creating intermediate cert chain: %w", err)
		}
	}

	return &attestation, nil
}
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"fmt"
	"time"

	"strconv"

	"sourcegraph.com/sourcegraph/api_router"
	"sourcegraph.com/sourcegraph/srcgraph/db_common"
	"sourcegraph.com/sourcegraph/srcgraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/task2"
)

// BuildsService communicates with the build-related endpoints in the
// Sourcegraph API.
type BuildsService interface {
	// Get fetches a build.
	Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error)

	// List builds.
	List(opt *BuildListOptions) ([]*Build, Response, error)

	// ListByRepository lists builds for a repository.
	ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error)

	// Create a new build. The build will run asynchronously (Create does not
	// wait for it to return. To monitor the build's status, use Get.)
	Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error)

	// ListBuildTasks lists the tasks associated with a build.
	ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error)
}

type buildsService struct {
	client *Client
}

var _ BuildsService = &buildsService{}

type BuildSpec struct {
	BID int64
}

func (s *BuildSpec) RouteVars() map[string]string {
	return map[string]string{"BID": fmt.Sprintf("%d", s.BID)}
}

// A Build represents a scheduled, completed, or failed repository analysis and
// import job.
type Build struct {
	BID       int64
	Repo      repo.RID
	CommitID  string             `db:"commit_id"`
	CreatedAt time.Time          `db:"created_at"`
	StartedAt db_common.NullTime `db:"started_at"`
	EndedAt   db_common.NullTime `db:"ended_at"`
	Success   bool
	Failure   bool

	// Host is the hostname of the machine that is working on this build.
	Host string

	// Tries is the number of times that this build has started to run. Builds
	// may be retried after a failure or timeout, in which case Tries > 1.
	Tries int

	BuildConfig

	// RepoURI is populated (as a convenience) in results by Get and List but
	// should not be set when creating builds (it will be ignored).
	RepoURI repo.URI `db:"repo_uri" json:",omitempty"`
}

// IDString returns a succinct string that uniquely identifies this build.
func (b *Build) IDString() string { return buildIDString(b.BID) }

func buildIDString(bid int64) string { return "B" + strconv.FormatInt(bid, 36) }

// LogURL is the URL to the logs for this build.
func (b *Build) LogURL() string {
	return task2.LogURLForTag(b.IDString())
}

// A BuildTask represents an individual step of a build.
type BuildTask struct {
	TID int64

	// BID is the build that this task is a part of.
	BID int64

	UnitType string
	Unit     string

	Title string

	StartedAt db_common.NullTime `db:"started_at"`
	EndedAt   db_common.NullTime `db:"ended_at"`

	Success bool
	Failure bool
}

// IDString returns a succinct string that uniquely identifies this build task.
func (t *BuildTask) IDString() string {
	return buildIDString(t.BID) + "-T" + strconv.FormatInt(t.TID, 36)
}

// LogURL is the URL to the logs for this task.
func (t *BuildTask) LogURL() string {
	return task2.LogURLForTag(t.IDString())
}

// BuildConfig configures a repository build.
type BuildConfig struct {
	// Import is whether to import the build data into the database when the
	// build is complete. The data must be imported for Sourcegraph's web app or
	// API to use it, except that unimported build data is available through the
	// BuildData service. (TODO(sqs): BuildData isn't yet implemented.)
	Import bool

	// Queue is whether this build should be enqueued. If enqueued, any worker
	// may begin running this build. If not enqueued, it is up to the client to
	// run the build and update it accordingly.
	Queue bool
}

var ErrBuildNotFound = errors.New("build not found")

type BuildGetOptions struct{}

func (s *buildsService) Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error) {
	url, err := s.client.url(api_router.Build, build.RouteVars(), opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var build_ *Build
	resp, err := s.client.Do(req, &build_)
	if err != nil {
		return nil, resp, err
	}

	return build_, nil, nil
}

type BuildListOptions struct {
	Queued    bool `url:",omitempty"`
	Active    bool `url:",omitempty"`
	Ended     bool `url:",omitempty"`
	Succeeded bool `url:",omitempty"`
	Failed    bool `url:",omitempty"`

	ListOptions
}

func (s *buildsService) List(opt *BuildListOptions) ([]*Build, Response, error) {
	url, err := s.client.url(api_router.Builds, nil, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var builds []*Build
	resp, err := s.client.Do(req, &builds)
	if err != nil {
		return nil, resp, err
	}

	return builds, resp, nil
}

type BuildListByRepositoryOptions struct {
	BuildListOptions
	Rev string `url:",omitempty"`
}

func (s *buildsService) ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error) {
	url, err := s.client.url(api_router.RepositoryBuilds, map[string]string{"RepoURI": repo.URI}, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var builds []*Build
	resp, err := s.client.Do(req, &builds)
	if err != nil {
		return nil, resp, err
	}

	return builds, resp, nil
}

func (s *buildsService) Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error) {
	url, err := s.client.url(api_router.RepositoryBuildsCreate, repo.RouteVars(), nil)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("POST", url.String(), conf)
	if err != nil {
		return nil, nil, err
	}

	var build *Build
	resp, err := s.client.Do(req, &build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, nil
}

type BuildTaskListOptions struct{ ListOptions }

func (s *buildsService) ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error) {
	url, err := s.client.url(api_router.BuildTasks, build.RouteVars(), opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var tasks []*BuildTask
	resp, err := s.client.Do(req, &tasks)
	if err != nil {
		return nil, resp, err
	}

	return tasks, resp, nil
}

type MockBuildsService struct {
	Get_              func(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error)
	List_             func(opt *BuildListOptions) ([]*Build, Response, error)
	ListByRepository_ func(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error)
	Create_           func(repo RepositorySpec, conf BuildConfig) (*Build, Response, error)
	ListBuildTasks_   func(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error)
}

var _ BuildsService = MockBuildsService{}

func (s MockBuildsService) Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error) {
	if s.Get_ == nil {
		return nil, &HTTPResponse{}, nil
	}
	return s.Get_(build, opt)
}

func (s MockBuildsService) List(opt *BuildListOptions) ([]*Build, Response, error) {
	if s.List_ == nil {
		return nil, nil, nil
	}
	return s.List_(opt)
}

func (s MockBuildsService) ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error) {
	if s.ListByRepository_ == nil {
		return nil, &HTTPResponse{}, nil
	}
	return s.ListByRepository_(repo, opt)
}

func (s MockBuildsService) Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error) {
	if s.Create_ == nil {
		return nil, nil, nil
	}
	return s.Create_(repo, conf)
}

func (s MockBuildsService) ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error) {
	if s.ListBuildTasks_ == nil {
		return nil, nil, nil
	}
	return s.ListBuildTasks_(build, opt)
}
<commit_msg>build sort<commit_after>package client

import (
	"errors"
	"fmt"
	"time"

	"strconv"

	"sourcegraph.com/sourcegraph/api_router"
	"sourcegraph.com/sourcegraph/srcgraph/db_common"
	"sourcegraph.com/sourcegraph/srcgraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/task2"
)

// BuildsService communicates with the build-related endpoints in the
// Sourcegraph API.
type BuildsService interface {
	// Get fetches a build.
	Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error)

	// List builds.
	List(opt *BuildListOptions) ([]*Build, Response, error)

	// ListByRepository lists builds for a repository.
	ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error)

	// Create a new build. The build will run asynchronously (Create does not
	// wait for it to return. To monitor the build's status, use Get.)
	Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error)

	// ListBuildTasks lists the tasks associated with a build.
	ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error)
}

type buildsService struct {
	client *Client
}

var _ BuildsService = &buildsService{}

type BuildSpec struct {
	BID int64
}

func (s *BuildSpec) RouteVars() map[string]string {
	return map[string]string{"BID": fmt.Sprintf("%d", s.BID)}
}

// A Build represents a scheduled, completed, or failed repository analysis and
// import job.
type Build struct {
	BID       int64
	Repo      repo.RID
	CommitID  string             `db:"commit_id"`
	CreatedAt time.Time          `db:"created_at"`
	StartedAt db_common.NullTime `db:"started_at"`
	EndedAt   db_common.NullTime `db:"ended_at"`
	Success   bool
	Failure   bool

	// Host is the hostname of the machine that is working on this build.
	Host string

	// Tries is the number of times that this build has started to run. Builds
	// may be retried after a failure or timeout, in which case Tries > 1.
	Tries int

	BuildConfig

	// RepoURI is populated (as a convenience) in results by Get and List but
	// should not be set when creating builds (it will be ignored).
	RepoURI repo.URI `db:"repo_uri" json:",omitempty"`
}

// IDString returns a succinct string that uniquely identifies this build.
func (b *Build) IDString() string { return buildIDString(b.BID) }

func buildIDString(bid int64) string { return "B" + strconv.FormatInt(bid, 36) }

// LogURL is the URL to the logs for this build.
func (b *Build) LogURL() string {
	return task2.LogURLForTag(b.IDString())
}

// A BuildTask represents an individual step of a build.
type BuildTask struct {
	TID int64

	// BID is the build that this task is a part of.
	BID int64

	UnitType string
	Unit     string

	Title string

	StartedAt db_common.NullTime `db:"started_at"`
	EndedAt   db_common.NullTime `db:"ended_at"`

	Success bool
	Failure bool
}

// IDString returns a succinct string that uniquely identifies this build task.
func (t *BuildTask) IDString() string {
	return buildIDString(t.BID) + "-T" + strconv.FormatInt(t.TID, 36)
}

// LogURL is the URL to the logs for this task.
func (t *BuildTask) LogURL() string {
	return task2.LogURLForTag(t.IDString())
}

// BuildConfig configures a repository build.
type BuildConfig struct {
	// Import is whether to import the build data into the database when the
	// build is complete. The data must be imported for Sourcegraph's web app or
	// API to use it, except that unimported build data is available through the
	// BuildData service. (TODO(sqs): BuildData isn't yet implemented.)
	Import bool

	// Queue is whether this build should be enqueued. If enqueued, any worker
	// may begin running this build. If not enqueued, it is up to the client to
	// run the build and update it accordingly.
	Queue bool
}

var ErrBuildNotFound = errors.New("build not found")

type BuildGetOptions struct{}

func (s *buildsService) Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error) {
	url, err := s.client.url(api_router.Build, build.RouteVars(), opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var build_ *Build
	resp, err := s.client.Do(req, &build_)
	if err != nil {
		return nil, resp, err
	}

	return build_, nil, nil
}

type BuildListOptions struct {
	Queued    bool `url:",omitempty"`
	Active    bool `url:",omitempty"`
	Ended     bool `url:",omitempty"`
	Succeeded bool `url:",omitempty"`
	Failed    bool `url:",omitempty"`

	Sort      string `url:",omitempty"`
	Direction string `url:",omitempty"`

	ListOptions
}

func (s *buildsService) List(opt *BuildListOptions) ([]*Build, Response, error) {
	url, err := s.client.url(api_router.Builds, nil, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var builds []*Build
	resp, err := s.client.Do(req, &builds)
	if err != nil {
		return nil, resp, err
	}

	return builds, resp, nil
}

type BuildListByRepositoryOptions struct {
	BuildListOptions
	Rev string `url:",omitempty"`
}

func (s *buildsService) ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error) {
	url, err := s.client.url(api_router.RepositoryBuilds, map[string]string{"RepoURI": repo.URI}, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var builds []*Build
	resp, err := s.client.Do(req, &builds)
	if err != nil {
		return nil, resp, err
	}

	return builds, resp, nil
}

func (s *buildsService) Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error) {
	url, err := s.client.url(api_router.RepositoryBuildsCreate, repo.RouteVars(), nil)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("POST", url.String(), conf)
	if err != nil {
		return nil, nil, err
	}

	var build *Build
	resp, err := s.client.Do(req, &build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, nil
}

type BuildTaskListOptions struct{ ListOptions }

func (s *buildsService) ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error) {
	url, err := s.client.url(api_router.BuildTasks, build.RouteVars(), opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var tasks []*BuildTask
	resp, err := s.client.Do(req, &tasks)
	if err != nil {
		return nil, resp, err
	}

	return tasks, resp, nil
}

type MockBuildsService struct {
	Get_              func(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error)
	List_             func(opt *BuildListOptions) ([]*Build, Response, error)
	ListByRepository_ func(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error)
	Create_           func(repo RepositorySpec, conf BuildConfig) (*Build, Response, error)
	ListBuildTasks_   func(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error)
}

var _ BuildsService = MockBuildsService{}

func (s MockBuildsService) Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error) {
	if s.Get_ == nil {
		return nil, &HTTPResponse{}, nil
	}
	return s.Get_(build, opt)
}

func (s MockBuildsService) List(opt *BuildListOptions) ([]*Build, Response, error) {
	if s.List_ == nil {
		return nil, nil, nil
	}
	return s.List_(opt)
}

func (s MockBuildsService) ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error) {
	if s.ListByRepository_ == nil {
		return nil, &HTTPResponse{}, nil
	}
	return s.ListByRepository_(repo, opt)
}

func (s MockBuildsService) Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error) {
	if s.Create_ == nil {
		return nil, nil, nil
	}
	return s.Create_(repo, conf)
}

func (s MockBuildsService) ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error) {
	if s.ListBuildTasks_ == nil {
		return nil, nil, nil
	}
	return s.ListBuildTasks_(build, opt)
}
<|endoftext|>


<commit_before>package client

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"strconv"
)

type Client struct {
	conn net.Conn
}

// NewClient will connect to the server and create a client.
func NewClient(o *Options) (*Client, error) {
	conn, err := o.getDialer()()
	return &Client{
		conn: conn,
	}, err
}

// Cmd will send command, receive data from server and build reply.
func (c *Client) Cmd(args ...interface{}) (r *Reply) {
	r = &Reply{
		State: ReplyError,
	}

	if c.conn == nil {
		r.State = ReplyFail
		return
	}

	if err := c.send(args); err != nil {
		r.State = ReplyFail
		return
	}
	r = c.recv()

	return
}

// Close socks.
func (c *Client) Close() {
	if c.conn != nil {
		c.conn.Close()
	}
}

func (c *Client) send(args []interface{}) (err error) {
	var buf []byte

	buf, err = c.sendBuf(args)
	if err == nil {
		_, err = c.conn.Write(buf)
	}
	return
}

func (c *Client) recv() (r *Reply) {
	var line []byte
	reader := bufio.NewReader(c.conn)

	// *<number of arguments>CRLF
	line, err = reader.ReadBytes('\n')
	if err != nil {
		return
	}

	if line[0] == '+' || line[0] == '-' {
		return &Reply{
			State: string(line[1 : len(line)-2]),
		}
	}
	if line[0] == ':' {
		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[1 : len(line)-2]},
		}
	}

	if line[0] == '$' {
		var dLen int
		dLen, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		line, _ = reader.ReadBytes('\n')

		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[:dLen]},
		}
	}

	var data [][]byte
	if line[0] == '*' {
		var count int
		count, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		for i := 0; i < count; i++ {
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			dLen, _ := strconv.Atoi(string(line[1 : len(line)-2]))
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			data = append(data, line[:dLen])
		}
	}

	r = &Reply{
		State: ReplyOK,
		Data:  data,
	}

	return
}

func (c *Client) sendBuf(args []interface{}) (b []byte, err error) {
	var buf bytes.Buffer
	var s, size string
	buf.WriteString(fmt.Sprintf("*%d\r\n", len(args)))

	for _, arg := range args {
		switch arg.(type) {
		case string:
			s = arg.(string)

		case int, int8, int16, int32, int64,
			uint, uint8, uint16, uint32, uint64:
			s = fmt.Sprintf("%d", arg)

		case float32:
			s = strconv.FormatFloat(float64(arg.(float32)), 'f', -1, 32)

		case float64:
			s = strconv.FormatFloat(arg.(float64), 'f', -1, 64)

		case bool:
		case nil:
		case []byte:
		case [][]byte:
		case []string:

		}

		size = fmt.Sprintf("$%d\r\n", len(s))
		buf.WriteString(size)
		buf.WriteString(s + "\r\n")

	}

	b = buf.Bytes()

	return
}
<commit_msg>Improve string format efficiency.<commit_after>package client

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"strconv"
)

type Client struct {
	conn net.Conn
}

// NewClient will connect to the server and create a client.
func NewClient(o *Options) (*Client, error) {
	conn, err := o.getDialer()()
	return &Client{
		conn: conn,
	}, err
}

// Cmd will send command, receive data from server and build reply.
func (c *Client) Cmd(args ...interface{}) (r *Reply) {
	r = &Reply{
		State: ReplyError,
	}

	if c.conn == nil {
		r.State = ReplyFail
		return
	}

	if err := c.send(args); err != nil {
		r.State = ReplyFail
		return
	}
	r = c.recv()

	return
}

// Close socks.
func (c *Client) Close() {
	if c.conn != nil {
		c.conn.Close()
	}
}

func (c *Client) send(args []interface{}) (err error) {
	var buf []byte

	buf, err = c.sendBuf(args)
	if err == nil {
		_, err = c.conn.Write(buf)
	}
	return
}

func (c *Client) recv() (r *Reply) {
	var line []byte
	reader := bufio.NewReader(c.conn)

	// *<number of arguments>CRLF
	line, err = reader.ReadBytes('\n')
	if err != nil {
		return
	}

	if line[0] == '+' || line[0] == '-' {
		return &Reply{
			State: string(line[1 : len(line)-2]),
		}
	}
	if line[0] == ':' {
		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[1 : len(line)-2]},
		}
	}

	if line[0] == '$' {
		var dLen int
		dLen, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		line, _ = reader.ReadBytes('\n')

		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[:dLen]},
		}
	}

	var data [][]byte
	if line[0] == '*' {
		var count int
		count, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		for i := 0; i < count; i++ {
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			dLen, _ := strconv.Atoi(string(line[1 : len(line)-2]))
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			data = append(data, line[:dLen])
		}
	}

	r = &Reply{
		State: ReplyOK,
		Data:  data,
	}

	return
}

func (c *Client) sendBuf(args []interface{}) (b []byte, err error) {
	var buf bytes.Buffer
	var s, size string
	buf.WriteString(fmt.Sprintf("*%d\r\n", len(args)))

	for _, arg := range args {
		switch t := arg.(type) {
		case string:
			s = t

		case int:
			s = strconv.FormatInt(int64(t), 10)
		case int8:
			s = strconv.FormatInt(int64(t), 10)
		case int16:
			s = strconv.FormatInt(int64(t), 10)
		case int32:
			s = strconv.FormatInt(int64(t), 10)
		case int64:
			s = strconv.FormatInt(int64(t), 10)

		case uint:
			s = strconv.FormatUint(uint64(t), 10)
		case uint8:
			s = strconv.FormatUint(uint64(t), 10)
		case uint16:
			s = strconv.FormatUint(uint64(t), 10)
		case uint32:
			s = strconv.FormatUint(uint64(t), 10)
		case uint64:
			s = strconv.FormatUint(uint64(t), 10)

		case float32:
			s = strconv.FormatFloat(float64(t), 'f', -1, 32)

		case float64:
			s = strconv.FormatFloat(t, 'f', -1, 64)

		case bool:
		case nil:
		case []byte:
		case [][]byte:
		case []string:

		}

		size = fmt.Sprintf("$%d\r\n", len(s))
		buf.WriteString(size)
		buf.WriteString(s + "\r\n")

	}

	b = buf.Bytes()

	return
}
<|endoftext|>


<commit_before>/* Copyright (C) 2013 CompleteDB LLC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with PubSubSQL.  If not, see <http://www.gnu.org/licenses/>.
 */

package pubsubsql

import (
	"container/list"
	"encoding/json"
	"net"
)

type Client interface {

	// Connect connects to the pubsubsql server.
	// Address has the form host:port.
	Connect(address string) bool

	// Disconnect disconnects from the pubsubsql server.
	Disconnect()

	// Ok determines if last operation succeeded.
	Ok() bool

	// Failed determines if last operation failed.
	Failed() bool

	// Error returns error string when last operation fails.
	Error() string

	// Execute executes a command.
	// Returns true on success.
	Execute(command string) bool

	// JSON() returns JSON response string returned from the last operation.
	JSON() string

	// Action returns action for last operation.
	Action() string

	// I returns unique record identifier generated by the database table.
	// Valid for actions: insert, select (when id is in selected columns),
	// pubsub insert, pubsub add, pubsub delete, pubsub update.
	Id() string

	// PubSubId returns unique pubsub identifier generated by the database.
	// Valid for pubsub related actions: subscribe. insert, add, delete, update.
	// PubSubId is generated by pubsubsql server when client subscribes to a table and
	// used to uniqely identify particual subscription for the connected client.
	PubSubId() string

	// RecordCount returns number of records in the returned result set.
	RecordCount() int

	// NextRecord moves cusrsor to the next data record of the returned result set.
	// Returns false when all records are read.
	// Must be called initially to position cursor to the first record.
	NextRecord() bool

	// Value returns column value by column name.
	// If column does not exist in current result set it returns empty string.
	Value(column string) string

	// Determines if column exist in the current result set.
	HasColumn(column string) bool

	// Columns returns an array of valid column names in the current result set.
	Columns() []string

	// WaitForPubSub waits until publish message is retreived or timeout expired.
	// Returns false on error or timeout.
	// When timeout expires Ok() will return true.
	WaitForPubSub(timeout int64) bool
}

func NewClient() Client {
	var c client
	return &c
}

var CLIENT_DEFAULT_BUFFER_SIZE int = 2048

// respnoseData holds unmarshaled result from pubsubsql JSON response
type responseData struct {
	Status   string
	Msg      string
	Action   string
	Id       string
	PubSubId string
	Rows     int
	Fromrow  int
	Torow    int
	Columns  []string
	Data     []map[string]string
}

func (this *responseData) reset() {
	this.Status = ""
	this.Msg = ""
	this.Action = ""
	this.Id = ""
	this.PubSubId = ""
	this.Rows = 0
	this.Fromrow = 0
	this.Torow = 0
	this.Columns = nil
	this.Data = nil
}

type client struct {
	Client
	address   string
	rw        NetMessageReaderWriter
	requestId uint32
	err       string
	rawjson   []byte
	//
	response responseData
	record   int

	// pubsub back log
	backlog list.List
}

func (this *client) Connect(address string) bool {
	this.address = address
	this.Disconnect()
	conn, err := net.Dial("tcp", this.address)
	if err != nil {
		this.setError(err)
		return false
	}
	this.rw.Set(conn, CLIENT_DEFAULT_BUFFER_SIZE)
	return true
}

func (this *client) Disconnect() {
	this.write("close")
	// write may generate error so we reset after instead
	this.reset()
	this.rw.Close()
}

func (this *client) Ok() bool {
	return this.err == ""
}

func (this *client) Failed() bool {
	return !this.Ok()
}

func (this *client) Error() string {
	return this.err
}

func (this *client) Execute(command string) bool {
	ok := this.write(command)
	var bytes []byte
	var header *NetworkHeader
	for ok {
		this.reset()
		header, bytes, ok = this.read()
		if !ok {
			break
		}
		if header.RequestId == this.requestId {
			// response we are waiting for
			return this.unmarshalJSON(bytes)
		} else if header.RequestId == 0 {
			// pubsub action, save it and skip it for now
			// will be proccesed next time WaitPubSub is called
			this.backlog.PushBack(bytes)
		} else if header.RequestId < this.requestId {
			// we did not read full result set from previous command ignore it or flag and error?
			// for now lets ignore it, continue reading until we hit our request id
			this.reset()
		} else {
			// this should never happen
			this.setErrorString("protocol error invalid requestId")
			ok = false
		}
	}
	return ok
}

func (this *client) JSON() string {
	return string(this.rawjson)
}

func (this *client) Action() string {
	return this.response.Action
}

func (this *client) Id() string {
	return this.response.Id
}

func (this *client) PubSubId() string {
	return this.response.PubSubId
}

func (this *client) RecordCount() int {
	return this.response.Rows
}

func (this *client) NextRecord() bool {
	for this.Ok() {
		// no result set
		if this.response.Rows == 0 {
			return false
		}
		if this.response.Fromrow == 0 || this.response.Torow == 0 {
			return false
		}
		// the current record is valid
		this.record++
		if this.record <= (this.response.Torow - this.response.Fromrow) {
			return true
		}
		// we reached the end of result set
		if this.response.Rows == this.response.Torow {
			// gaurd against over fill
			this.record--
			return false
		}
		// if we are here there is another batch
		this.reset()
		header, bytes, ok := this.read()
		if !ok {
			return false
		}
		// should not happen but check anyway
		// when RequestId is 0 it means we are reading published data
		if header.RequestId > 0 && header.RequestId != this.requestId {
			this.setErrorString("protocol error")
			return false
		}
		// we got another batch unmarshall the data
		this.unmarshalJSON(bytes)
	}
	return false
}

func (this *client) Value(column string) string {
	if this.record > -1 && this.record < len(this.response.Data) {
		rec := this.response.Data[this.record]
		return rec[column]
	}
	return ""
}

func (this *client) HasColumn(column string) bool {
	hasColumn := false
	if len(this.response.Data) > 0 {
		_, hasColumn = this.response.Data[0][column]
	}
	return hasColumn
}

func (this *client) Columns() []string {
	return this.response.Columns
}

func (this *client) WaitForPubSub(timeout int64) bool {
	var bytes []byte
	for {
		this.reset()
		// process backlog first
		if element := this.backlog.Front(); element != nil {
			bytes = element.Value.([]byte)
			this.backlog.Remove(element)
			break
		}
		if len(bytes) == 0 {
			header, temp, success, timedout := this.readTimeout(timeout)
			bytes = temp
			if !success || timedout {
				return false
			}
			if header.RequestId == 0 {
				break
			}
			// this is not pubsub message; are we reading abandoned cursor?
			// ignore and keep trying
		}
	}
	return this.unmarshalJSON(bytes)
}

func (this *client) unmarshalJSON(bytes []byte) bool {
	this.rawjson = bytes
	err := json.Unmarshal(bytes, &this.response)
	if err != nil {
		this.setError(err)
		return false
	}
	if this.response.Status != "ok" {
		this.setErrorString(this.response.Msg)
		return false
	}
	return true
}

func (this *client) reset() {
	this.resetError()
	this.response.reset()
	this.rawjson = nil
	this.record = -1
}

func (this *client) resetError() {
	this.err = ""
}

func (this *client) setErrorString(err string) {
	this.reset()
	this.err = err
}

func (this *client) setError(err error) {
	this.setErrorString(err.Error())
}

func (this *client) write(message string) bool {
	this.requestId++
	if !this.rw.Valid() {
		this.setErrorString("Not connected")
		return false
	}
	err := this.rw.WriteHeaderAndMessage(this.requestId, []byte(message))
	if err != nil {
		this.setError(err)
		return false
	}
	return true
}

func (this *client) readTimeout(timeout int64) (*NetworkHeader, []byte, bool, bool) {
	if !this.rw.Valid() {
		this.setErrorString("Not connected")
		return nil, nil, false, false
	}
	header, bytes, err, timedout := this.rw.ReadMessageTimeout(timeout)
	if timedout {
		return nil, nil, true, true
	}
	// error
	if err != nil {
		this.setError(err)
		return nil, nil, false, false
	}
	// success
	return header, bytes, true, false

}

func (this *client) read() (*NetworkHeader, []byte, bool) {
	var MAX_READ_TIMEOUT_MILLISECONDS int64 = 1000 * 60 * 3
	header, bytes, success, timedout := this.readTimeout(MAX_READ_TIMEOUT_MILLISECONDS)
	if timedout {
		this.setErrorString("Read timed out")
		return nil, nil, false
	}
	return header, bytes, success
}
<commit_msg>client grow comments<commit_after>/* Copyright (C) 2013 CompleteDB LLC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with PubSubSQL.  If not, see <http://www.gnu.org/licenses/>.
 */

package pubsubsql

import (
	"container/list"
	"encoding/json"
	"net"
)

type Client interface {

	// Connect connects to the pubsubsql server.
	// Address has the form host:port.
	Connect(address string) bool

	// Disconnect disconnects from the pubsubsql server.
	Disconnect()

	// Ok determines if last operation succeeded.
	Ok() bool

	// Failed determines if last operation failed.
	Failed() bool

	// Error returns error string when last operation fails.
	Error() string

	// Execute executes a command.
	// Returns true on success.
	Execute(command string) bool

	// JSON() returns JSON response string returned from the last operation.
	JSON() string

	// Action returns action for last operation.
	// Valid actions [status, insert, upate, delete, add, remove, update, subsribe, unsubscribe]
	Action() string

	// Id returns unique table record identifier generated by the database.
	// Valid for actions: insert, pubsub insert, pubsub add,
	// pubsub delete, pubsub remove, pubsub update.
	Id() string

	// PubSubId returns unique pubsub identifier generated by the database.
	// Valid for pubsub related actions: subscribe, insert, add, delete, remove, update.
	// PubSubId is generated by pubsubsql server when client subscribes to a table and
	// used to uniqely identify particual subscription.
	PubSubId() string

	// RecordCount returns number of records in the returned result set
	// or number of records affected by the operation.
	RecordCount() int

	// NextRecord moves cusrsor to the next data record in the returned result set.
	// Returns false when all records are read.
	// Must be called initially to position cursor to the first record.
	NextRecord() bool

	// Value returns column value by column name.
	// If column does not exist in current result set it returns empty string.
	Value(column string) string

	// Determines if column exist in the current result set.
	HasColumn(column string) bool

	// Columns returns an array of valid column names in the current result set.
	Columns() []string

	// WaitForPubSub waits until publish message is retreived or timeout expired.
	// Returns false on error or timeout.
	// When timeout expires Ok() will return true.
	WaitForPubSub(timeout int64) bool
}

func NewClient() Client {
	var c client
	return &c
}

var CLIENT_DEFAULT_BUFFER_SIZE int = 2048

// respnoseData holds unmarshaled result from pubsubsql JSON response
type responseData struct {
	Status   string
	Msg      string
	Action   string
	Id       string
	PubSubId string
	Rows     int
	Fromrow  int
	Torow    int
	Columns  []string
	Data     []map[string]string
}

func (this *responseData) reset() {
	this.Status = ""
	this.Msg = ""
	this.Action = ""
	this.Id = ""
	this.PubSubId = ""
	this.Rows = 0
	this.Fromrow = 0
	this.Torow = 0
	this.Columns = nil
	this.Data = nil
}

type client struct {
	Client
	address   string
	rw        NetMessageReaderWriter
	requestId uint32
	err       string
	rawjson   []byte
	//
	response responseData
	record   int

	// pubsub back log
	backlog list.List
}

func (this *client) Connect(address string) bool {
	this.address = address
	this.Disconnect()
	conn, err := net.Dial("tcp", this.address)
	if err != nil {
		this.setError(err)
		return false
	}
	this.rw.Set(conn, CLIENT_DEFAULT_BUFFER_SIZE)
	return true
}

func (this *client) Disconnect() {
	this.write("close")
	// write may generate error so we reset after instead
	this.reset()
	this.rw.Close()
}

func (this *client) Ok() bool {
	return this.err == ""
}

func (this *client) Failed() bool {
	return !this.Ok()
}

func (this *client) Error() string {
	return this.err
}

func (this *client) Execute(command string) bool {
	ok := this.write(command)
	var bytes []byte
	var header *NetworkHeader
	for ok {
		this.reset()
		header, bytes, ok = this.read()
		if !ok {
			break
		}
		if header.RequestId == this.requestId {
			// response we are waiting for
			return this.unmarshalJSON(bytes)
		} else if header.RequestId == 0 {
			// pubsub action, save it and skip it for now
			// will be proccesed next time WaitPubSub is called
			this.backlog.PushBack(bytes)
		} else if header.RequestId < this.requestId {
			// we did not read full result set from previous command ignore it or flag and error?
			// for now lets ignore it, continue reading until we hit our request id
			this.reset()
		} else {
			// this should never happen
			this.setErrorString("protocol error invalid requestId")
			ok = false
		}
	}
	return ok
}

func (this *client) JSON() string {
	return string(this.rawjson)
}

func (this *client) Action() string {
	return this.response.Action
}

func (this *client) Id() string {
	return this.response.Id
}

func (this *client) PubSubId() string {
	return this.response.PubSubId
}

func (this *client) RecordCount() int {
	return this.response.Rows
}

func (this *client) NextRecord() bool {
	for this.Ok() {
		// no result set
		if this.response.Rows == 0 {
			return false
		}
		if this.response.Fromrow == 0 || this.response.Torow == 0 {
			return false
		}
		// the current record is valid
		this.record++
		if this.record <= (this.response.Torow - this.response.Fromrow) {
			return true
		}
		// we reached the end of result set
		if this.response.Rows == this.response.Torow {
			// gaurd against over fill
			this.record--
			return false
		}
		// if we are here there is another batch
		this.reset()
		header, bytes, ok := this.read()
		if !ok {
			return false
		}
		// should not happen but check anyway
		// when RequestId is 0 it means we are reading published data
		if header.RequestId > 0 && header.RequestId != this.requestId {
			this.setErrorString("protocol error")
			return false
		}
		// we got another batch unmarshall the data
		this.unmarshalJSON(bytes)
	}
	return false
}

func (this *client) Value(column string) string {
	if this.record > -1 && this.record < len(this.response.Data) {
		rec := this.response.Data[this.record]
		return rec[column]
	}
	return ""
}

func (this *client) HasColumn(column string) bool {
	hasColumn := false
	if len(this.response.Data) > 0 {
		_, hasColumn = this.response.Data[0][column]
	}
	return hasColumn
}

func (this *client) Columns() []string {
	return this.response.Columns
}

func (this *client) WaitForPubSub(timeout int64) bool {
	var bytes []byte
	for {
		this.reset()
		// process backlog first
		if element := this.backlog.Front(); element != nil {
			bytes = element.Value.([]byte)
			this.backlog.Remove(element)
			break
		}
		if len(bytes) == 0 {
			header, temp, success, timedout := this.readTimeout(timeout)
			bytes = temp
			if !success || timedout {
				return false
			}
			if header.RequestId == 0 {
				break
			}
			// this is not pubsub message; are we reading abandoned cursor?
			// ignore and keep trying
		}
	}
	return this.unmarshalJSON(bytes)
}

func (this *client) unmarshalJSON(bytes []byte) bool {
	this.rawjson = bytes
	err := json.Unmarshal(bytes, &this.response)
	if err != nil {
		this.setError(err)
		return false
	}
	if this.response.Status != "ok" {
		this.setErrorString(this.response.Msg)
		return false
	}
	return true
}

func (this *client) reset() {
	this.resetError()
	this.response.reset()
	this.rawjson = nil
	this.record = -1
}

func (this *client) resetError() {
	this.err = ""
}

func (this *client) setErrorString(err string) {
	this.reset()
	this.err = err
}

func (this *client) setError(err error) {
	this.setErrorString(err.Error())
}

func (this *client) write(message string) bool {
	this.requestId++
	if !this.rw.Valid() {
		this.setErrorString("Not connected")
		return false
	}
	err := this.rw.WriteHeaderAndMessage(this.requestId, []byte(message))
	if err != nil {
		this.setError(err)
		return false
	}
	return true
}

func (this *client) readTimeout(timeout int64) (*NetworkHeader, []byte, bool, bool) {
	if !this.rw.Valid() {
		this.setErrorString("Not connected")
		return nil, nil, false, false
	}
	header, bytes, err, timedout := this.rw.ReadMessageTimeout(timeout)
	if timedout {
		return nil, nil, true, true
	}
	// error
	if err != nil {
		this.setError(err)
		return nil, nil, false, false
	}
	// success
	return header, bytes, true, false

}

func (this *client) read() (*NetworkHeader, []byte, bool) {
	var MAX_READ_TIMEOUT_MILLISECONDS int64 = 1000 * 60 * 3
	header, bytes, success, timedout := this.readTimeout(MAX_READ_TIMEOUT_MILLISECONDS)
	if timedout {
		this.setErrorString("Read timed out")
		return nil, nil, false
	}
	return header, bytes, success
}
<|endoftext|>


<commit_before>package client

import (
	"bufio"
	"errors"
	"net"
	"crypto/tls"
	"strings"

	imap "github.com/emersion/imap/common"
	"github.com/emersion/imap/commands"
	"github.com/emersion/imap/responses"
)

type Client struct {
	conn net.Conn
	handlers []imap.RespHandler
}

func (c *Client) read() (err error) {
	scanner := bufio.NewScanner(c.conn)

	for scanner.Scan() {
		line := scanner.Text() + "\n"
		r := strings.NewReader(line)

		var res interface{}
		res, err = imap.ReadResp(imap.NewReader(bufio.NewReader(r)))
		if err != nil {
			return
		}

		for _, hdlr := range c.handlers {
			h := &imap.RespHandling{
				Resp: res,
				Accepts: make(chan bool),
			}

			hdlr <- h

			if <-h.Accepts {
				break
			}
		}
	}

	return scanner.Err()
}

func (c *Client) execute(cmdr imap.Commander, r imap.RespHandlerFrom) (err error) {
	cmd := cmdr.Command()

	_, err = cmd.WriteTo(c.conn)
	if err != nil {
		return
	}

	statusHdlr := make(imap.RespHandler)
	defer close(statusHdlr)
	c.handlers = append(c.handlers, statusHdlr)

	var hdlr imap.RespHandler
	done := make(chan bool)
	if r != nil {
		hdlr := make(imap.RespHandler)
		defer close(hdlr)

		go (func() {
			err = r.HandleFrom(hdlr)
			done <- true
		})()
	}

	for {
		select {
		case h := <-statusHdlr:
			if status, ok := h.Resp.(*imap.StatusResp); ok && status.Tag == cmd.Tag {
				h.Accept()
				err = status
				return
			} else if hdlr != nil {
				hdlr <- h
			} else {
				h.Reject()
			}
		case <-done:
			return
		}
	}
	return
}

func (c *Client) Capability() (res *responses.Capability, err error) {
	cmd := &commands.Capability{}
	res = &responses.Capability{}

	err = c.execute(cmd, res)
	return
}

func (c *Client) StartTLS(tlsConfig *tls.Config) (err error) {
	if _, ok := c.conn.(*tls.Conn); ok {
		err = errors.New("TLS is already enabled")
		return
	}

	cmd := &commands.StartTLS{}

	err = c.execute(cmd, nil)
	if err != nil {
		return
	}

	tlsConn := tls.Client(c.conn, tlsConfig)
	err = tlsConn.Handshake()
	if err != nil {
		return
	}

	c.conn = tlsConn
	return
}

func NewClient(conn net.Conn) *Client {
	c := &Client{
		conn: conn,
	}

	go c.read()

	return c
}

func Dial(addr string) (c *Client, err error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return
	}

	c = NewClient(conn)
	return
}

func DialTLS(addr string, tlsConfig *tls.Config) (c *Client, err error) {
	conn, err := tls.Dial("tcp", addr, tlsConfig)
	if err != nil {
		return
	}

	c = NewClient(conn)
	return
}
<commit_msg>Adds a todo<commit_after>package client

import (
	"bufio"
	"errors"
	"net"
	"crypto/tls"
	"strings"

	imap "github.com/emersion/imap/common"
	"github.com/emersion/imap/commands"
	"github.com/emersion/imap/responses"
)

type Client struct {
	conn net.Conn
	handlers []imap.RespHandler
}

func (c *Client) read() (err error) {
	// TODO: optimize readers, do not create new ones for each response
	scanner := bufio.NewScanner(c.conn)

	for scanner.Scan() {
		line := scanner.Text() + "\n"
		r := strings.NewReader(line)

		var res interface{}
		res, err = imap.ReadResp(imap.NewReader(bufio.NewReader(r)))
		if err != nil {
			return
		}

		for _, hdlr := range c.handlers {
			h := &imap.RespHandling{
				Resp: res,
				Accepts: make(chan bool),
			}

			hdlr <- h

			if <-h.Accepts {
				break
			}
		}
	}

	return scanner.Err()
}

func (c *Client) execute(cmdr imap.Commander, r imap.RespHandlerFrom) (err error) {
	cmd := cmdr.Command()

	_, err = cmd.WriteTo(c.conn)
	if err != nil {
		return
	}

	statusHdlr := make(imap.RespHandler)
	defer close(statusHdlr)
	c.handlers = append(c.handlers, statusHdlr)

	var hdlr imap.RespHandler
	done := make(chan bool)
	if r != nil {
		hdlr := make(imap.RespHandler)
		defer close(hdlr)

		go (func() {
			err = r.HandleFrom(hdlr)
			done <- true
		})()
	}

	for {
		select {
		case h := <-statusHdlr:
			if status, ok := h.Resp.(*imap.StatusResp); ok && status.Tag == cmd.Tag {
				h.Accept()
				err = status
				return
			} else if hdlr != nil {
				hdlr <- h
			} else {
				h.Reject()
			}
		case <-done:
			return
		}
	}
	return
}

func (c *Client) Capability() (res *responses.Capability, err error) {
	cmd := &commands.Capability{}
	res = &responses.Capability{}

	err = c.execute(cmd, res)
	return
}

func (c *Client) StartTLS(tlsConfig *tls.Config) (err error) {
	if _, ok := c.conn.(*tls.Conn); ok {
		err = errors.New("TLS is already enabled")
		return
	}

	cmd := &commands.StartTLS{}

	err = c.execute(cmd, nil)
	if err != nil {
		return
	}

	tlsConn := tls.Client(c.conn, tlsConfig)
	err = tlsConn.Handshake()
	if err != nil {
		return
	}

	c.conn = tlsConn
	return
}

func NewClient(conn net.Conn) *Client {
	c := &Client{
		conn: conn,
	}

	go c.read()

	return c
}

func Dial(addr string) (c *Client, err error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return
	}

	c = NewClient(conn)
	return
}

func DialTLS(addr string, tlsConfig *tls.Config) (c *Client, err error) {
	conn, err := tls.Dial("tcp", addr, tlsConfig)
	if err != nil {
		return
	}

	c = NewClient(conn)
	return
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
)

func TestAccAWSPlacementGroup_basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSPlacementGroupDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSPlacementGroupConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSPlacementGroupExists("aws_placement_group.pg"),
				),
			},
		},
	})
}

func testAccCheckAWSPlacementGroupDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).ec2conn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_placement_group" {
			continue
		}
		_, err := conn.DeletePlacementGroup(&ec2.DeletePlacementGroupInput{
			GroupName: aws.String(rs.Primary.ID),
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func testAccCheckAWSPlacementGroupExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Placement Group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).ec2conn
		_, err := conn.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
			GroupNames: []*string{aws.String(rs.Primary.ID)},
		})

		if err != nil {
			return fmt.Errorf("Placement Group error: %v", err)
		}
		return nil
	}
}

func testAccCheckAWSDestroyPlacementGroup(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Placement Group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).ec2conn
		_, err := conn.DeletePlacementGroup(&ec2.DeletePlacementGroupInput{
			GroupName: aws.String(rs.Primary.ID),
		})

		if err != nil {
			return fmt.Errorf("Error destroying Placement Group (%s): %s", rs.Primary.ID, err)
		}
		return nil
	}
}

var testAccAWSPlacementGroupConfig = `
resource "aws_placement_group" "pg" {
	name = "tf-test-pg"
	strategy = "cluster"
}
`
<commit_msg>provider/aws: fix CheckDestroy on placement_group tests<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
)

func TestAccAWSPlacementGroup_basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSPlacementGroupDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSPlacementGroupConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSPlacementGroupExists("aws_placement_group.pg"),
				),
			},
		},
	})
}

func testAccCheckAWSPlacementGroupDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).ec2conn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_placement_group" {
			continue
		}

		_, err := conn.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
			GroupNames: []*string{aws.String(rs.Primary.Attributes["name"])},
		})
		if err != nil {
			// Verify the error is what we want
			if ae, ok := err.(awserr.Error); ok && ae.Code() == "InvalidPlacementGroup.Unknown" {
				continue
			}
			return err
		}

		return fmt.Errorf("still exists")
	}
	return nil
}

func testAccCheckAWSPlacementGroupExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Placement Group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).ec2conn
		_, err := conn.DescribePlacementGroups(&ec2.DescribePlacementGroupsInput{
			GroupNames: []*string{aws.String(rs.Primary.ID)},
		})

		if err != nil {
			return fmt.Errorf("Placement Group error: %v", err)
		}
		return nil
	}
}

func testAccCheckAWSDestroyPlacementGroup(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Placement Group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).ec2conn
		_, err := conn.DeletePlacementGroup(&ec2.DeletePlacementGroupInput{
			GroupName: aws.String(rs.Primary.ID),
		})

		if err != nil {
			return fmt.Errorf("Error destroying Placement Group (%s): %s", rs.Primary.ID, err)
		}
		return nil
	}
}

var testAccAWSPlacementGroupConfig = `
resource "aws_placement_group" "pg" {
	name = "tf-test-pg"
	strategy = "cluster"
}
`
<|endoftext|>


<commit_before>package docker

import (
	"fmt"
	"testing"

	dc "github.com/fsouza/go-dockerclient"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDockerContainer_basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccDockerContainerConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccContainerRunning("docker_container.foo"),
				),
			},
		},
	})
}

func testAccContainerRunning(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		client := testAccProvider.Meta().(*dc.Client)
		containers, err := client.ListContainers(dc.ListContainersOptions{})
		if err != nil {
			return err
		}

		for _, c := range containers {
			if c.ID == rs.Primary.ID {
				return nil
			}
		}

		return fmt.Errorf("Container not found: %s", rs.Primary.ID)
	}
}

const testAccDockerContainerConfig = `
resource "docker_image" "foo" {
	name = "ubuntu:trusty-20150320"
}

resource "docker_container" "foo" {
	name = "tf-test"
	image = "${docker_image.foo.latest}"
}
`
<commit_msg>provider/docker: [tests] change images<commit_after>package docker

import (
	"fmt"
	"testing"

	dc "github.com/fsouza/go-dockerclient"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDockerContainer_basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccDockerContainerConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccContainerRunning("docker_container.foo"),
				),
			},
		},
	})
}

func testAccContainerRunning(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		client := testAccProvider.Meta().(*dc.Client)
		containers, err := client.ListContainers(dc.ListContainersOptions{})
		if err != nil {
			return err
		}

		for _, c := range containers {
			if c.ID == rs.Primary.ID {
				return nil
			}
		}

		return fmt.Errorf("Container not found: %s", rs.Primary.ID)
	}
}

const testAccDockerContainerConfig = `
resource "docker_image" "foo" {
	name = "nginx:latest"
}

resource "docker_container" "foo" {
	name = "tf-test"
	image = "${docker_image.foo.latest}"
}
`
<|endoftext|>