<commit_before>package send_message_batch

import (
	"encoding/xml"
	"firempq/server/sqsproto/send_message"
	"firempq/server/sqsproto/sqs_response"
	"firempq/server/sqsproto/sqsencoding"
	"firempq/server/sqsproto/sqserr"
	"firempq/server/sqsproto/urlutils"
	"firempq/server/sqsproto/validation"
	"firempq/services/pqueue"
	"net/http"
)

type SendMessageBatchResponse struct {
	XMLName     xml.Name      `xml:"http://queue.amazonaws.com/doc/2012-11-05/ SendMessageBatchResponse"`
	ErrorEntry  []interface{} `xml:"SendMessageBatchResult>BatchResultErrorEntry,omitempty"`
	ResultEntry []interface{} `xml:"SendMessageBatchResult>SendMessageBatchResultEntry,omitempty"`
	RequestId   string        `xml:"ResponseMetadata>RequestId"`
}

func (self *SendMessageBatchResponse) HttpCode() int                        { return http.StatusOK }
func (self *SendMessageBatchResponse) XmlDocument() string                  { return sqsencoding.EncodeXmlDocument(self) }
func (self *SendMessageBatchResponse) BatchResult(docId string) interface{} { return nil }

type MessageBatchParams struct {
	Id         string
	ParamsList []string
}

func NewReqQueueAttr() urlutils.ISubContainer { return &MessageBatchParams{} }

func (self *MessageBatchParams) Parse(paramName, value string) *sqserr.SQSError {
	switch paramName {
	case "Id":
		self.Id = value
	default:
		self.ParamsList = append(self.ParamsList, paramName, value)
	}
	return nil
}

func SendMessageBatch(pq *pqueue.PQueue, sqsQuery *urlutils.SQSQuery) sqs_response.SQSResponse {
	attrs, _ := urlutils.ParseNNotationAttr(
		"SendMessageBatchRequestEntry.", sqsQuery.ParamsList, nil, NewReqQueueAttr)

	attrsLen := len(attrs)
	if attrsLen == 0 {
		return sqserr.EmptyBatchRequestError()
	}

	if attrsLen > 10 {
		return sqserr.TooManyEntriesInBatchRequestError()
	}

	// map used to detect duplicated ids.
	idsMap := make(map[string]struct{}, attrsLen)
	attrList := make([]*MessageBatchParams, 0, attrsLen)

	for i := 1; i <= attrsLen; i++ {
		a, ok := attrs[i]
		if !ok {
			return sqserr.MissingParameterError("The request is missing sequence %d", i)
		}
		p, _ := a.(*MessageBatchParams)
		attrList = append(attrList, p)
		if err := validation.ValidateBatchRequestId(p.Id); err != nil {
			return err
		}
		if _, ok := idsMap[p.Id]; ok {
			return sqserr.NotDistinctIdsError(p.Id)
		}
		idsMap[p.Id] = struct{}{}
	}

	batchResponse := &SendMessageBatchResponse{
		RequestId: "rreer",
	}

	for _, a := range attrList {
		resp := send_message.PushAMessage(pq, sqsQuery.SenderId, a.ParamsList)
		if resp.HttpCode() == 200 {
			batchResponse.ResultEntry = append(batchResponse.ResultEntry, resp.BatchResult(a.Id))
		} else {
			batchResponse.ErrorEntry = append(batchResponse.ErrorEntry, resp.BatchResult(a.Id))
		}
	}

	return batchResponse
}
<commit_msg>Reused code for batched operations.<commit_after>package send_message_batch

import (
	"encoding/xml"
	"firempq/server/sqsproto/send_message"
	"firempq/server/sqsproto/sqs_response"
	"firempq/server/sqsproto/sqsencoding"
	"firempq/server/sqsproto/sqserr"
	"firempq/server/sqsproto/urlutils"
	"firempq/services/pqueue"
	"net/http"
	"firempq/server/sqsproto/validation"
)

type SendMessageBatchResponse struct {
	XMLName     xml.Name      `xml:"http://queue.amazonaws.com/doc/2012-11-05/ SendMessageBatchResponse"`
	ErrorEntry  []interface{} `xml:"SendMessageBatchResult>BatchResultErrorEntry,omitempty"`
	ResultEntry []interface{} `xml:"SendMessageBatchResult>SendMessageBatchResultEntry,omitempty"`
	RequestId   string        `xml:"ResponseMetadata>RequestId"`
}

func (self *SendMessageBatchResponse) HttpCode() int                        { return http.StatusOK }
func (self *SendMessageBatchResponse) XmlDocument() string                  { return sqsencoding.EncodeXmlDocument(self) }
func (self *SendMessageBatchResponse) BatchResult(docId string) interface{} { return nil }

type MessageBatchParams struct {
	Id         string
	ParamsList []string
}

func NewReqQueueAttr() urlutils.ISubContainer { return &MessageBatchParams{} }

func (self *MessageBatchParams) Parse(paramName, value string) *sqserr.SQSError {
	switch paramName {
	case "Id":
		self.Id = value
	default:
		self.ParamsList = append(self.ParamsList, paramName, value)
	}
	return nil
}

func SendMessageBatch(pq *pqueue.PQueue, sqsQuery *urlutils.SQSQuery) sqs_response.SQSResponse {
	attrs, _ := urlutils.ParseNNotationAttr(
		"SendMessageBatchRequestEntry.", sqsQuery.ParamsList, nil, NewReqQueueAttr)

	attrsLen := len(attrs)

	checker, err := validation.NewBatchIdValidation(attrsLen)
	if err != nil {
		return err
	}

	// map used to detect duplicated ids.
	attrList := make([]*MessageBatchParams, 0, attrsLen)

	for i := 1; i <= attrsLen; i++ {
		a, ok := attrs[i]
		if !ok {
			return sqserr.MissingParameterError("The request is missing sequence %d", i)
		}
		p, _ := a.(*MessageBatchParams)
		if err = checker.Validate(p.Id); err != nil {
			return err
		}
		if err := validation.ValidateBatchRequestId(p.Id); err != nil {
			return err
		}
		attrList = append(attrList, p)
	}

	batchResponse := &SendMessageBatchResponse{
		RequestId: "rreer",
	}

	for _, a := range attrList {
		resp := send_message.PushAMessage(pq, sqsQuery.SenderId, a.ParamsList)
		if resp.HttpCode() == 200 {
			batchResponse.ResultEntry = append(batchResponse.ResultEntry, resp.BatchResult(a.Id))
		} else {
			batchResponse.ErrorEntry = append(batchResponse.ErrorEntry, resp.BatchResult(a.Id))
		}
	}

	return batchResponse
}
<|endoftext|>


<commit_before>package rest

import (
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	//"github.com/emicklei/go-restful"
	"github.com/gocraft/health"

	"github.com/gi4nks/quant"

	repos "github.com/streamtune/gadget/repos"
	utils "github.com/streamtune/gadget/utils"
)

//should be global Var
var stream = health.NewStream()

type Rest struct {
	parrot *quant.Parrot

	configuration *utils.Configuration
	repository    *repos.Repository
	commands      *repos.Commands
	utils         *utils.Utilities
}

func NewRest(p quant.Parrot, c utils.Configuration, r repos.Repository, cm repos.Commands, u utils.Utilities) *Rest {
	return &Rest{parrot: &p, configuration: &c, repository: &r, commands: &cm, utils: &u}
}

func (rs *Rest) Serve() {
	// Log to stdout!
	stream.AddSink(&health.WriterSink{os.Stdout})
	// Make sink and add it to stream
	sink := health.NewJsonPollingSink(time.Minute*5, time.Minute*20)
	stream.AddSink(sink)
	// Start the HTTP server! This will expose metrics via a JSON API.
	adr := ":5001"
	sink.StartServer(adr)

	gin.SetMode(rs.configuration.RestServerMode)

	r := gin.Default()
	r.Use(rs.cors())

	admin := r.Group("/api/v1/admin")
	{
		admin.POST("/debug", rs.postDebug)
		admin.POST("/revive", rs.postRevive)
		admin.POST("/update", rs.postUpdate)
	}

	images := r.Group("/api/v1/images")
	{
		images.GET("/", rs.getImages)
		images.GET("/:repo/:tag", rs.getImageByRepoTag)
		images.GET("/:repo", rs.getImageByTag)
		images.POST("/limit", rs.postImagesByLimit)
	}

	labels := r.Group("/api/v1/labels")
	{
		labels.GET("/", rs.getImagesWithLabels)
		labels.POST("", rs.getImagesByLabel)
	}

	volumes := r.Group("/api/v1/volumes")
	{
		volumes.GET("/", rs.getImagesWithVolumes)
		volumes.POST("", rs.getImagesByVolume)
	}

	rs.parrot.Println("Gadget Rest APIs running on port", rs.configuration.RestPort)

	r.Run(":" + strconv.Itoa(rs.configuration.RestPort))

}

func (rs *Rest) cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Add("Access-Control-Allow-Origin", "*")
		c.Next()
	}
}

func (rs *Rest) postRevive(c *gin.Context) {
	err := rs.commands.Revive()

	if err != nil {
		c.Status(http.StatusOK)
	} else {
		c.Status(http.StatusNotAcceptable)
	}
}

func (rs *Rest) postUpdate(c *gin.Context) {
	// curl -i -X POST -H "Content-Type: application/json" http://localhost:9080/api/v1/admin/update

	err := rs.commands.Update()

	if err != nil {
		c.Status(http.StatusOK)
	} else {
		c.Status(http.StatusNotAcceptable)
	}
}

func (rs *Rest) postDebug(c *gin.Context) {
	dbg, err := strconv.ParseBool(c.PostForm("dbg"))

	if err != nil {
		dbg = false
	}

	rs.commands.Debug(dbg)

	c.Status(http.StatusOK)
}

func (rs *Rest) getImages(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/images
	images, err := rs.repository.GetAll()

	rs.parrot.Println("-->", err)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if rs.configuration.DebugMode {
		for _, img := range images {
			rs.parrot.Debug("[" + img.ID + "] - [" + strconv.Itoa(len(img.Tags)) + "] [" + strconv.Itoa(len(img.Labels)) + "]")
		}
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}
}

func (rs *Rest) postImagesByLimit(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/images/limit/:count
	count, err := strconv.Atoi(c.PostForm("count"))

	if err != nil {
		c.Status(http.StatusBadRequest)
		return
	}

	images, err := rs.repository.GetLimit(count)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if rs.configuration.DebugMode {
		for _, img := range images {
			rs.parrot.Debug("[" + img.ID + "] - [" + strconv.Itoa(len(img.Tags)) + "] [" + strconv.Itoa(len(img.Labels)) + "]")
		}
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}
}

func (rs *Rest) getImageByRepoTag(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/images/

	tag := c.Param("tag")
	repo := c.Param("repo")

	images, err := rs.repository.FindByTag(repo + "/" + tag)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}

}

func (rs *Rest) getImageByTag(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/images/
	repo := c.Param("repo")

	images, err := rs.repository.FindByTag(repo)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}
}

func (rs *Rest) getImagesWithLabels(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/labels
	images, err := rs.repository.GetImagesWithLabels()

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}

	c.Status(http.StatusNoContent)
}

func (rs *Rest) getImagesWithVolumes(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/volumes
	images, err := rs.repository.GetImagesWithVolumes()

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}

	c.Status(http.StatusNoContent)
}

func (rs *Rest) getImagesByLabel(c *gin.Context) {
	// curl --data "lbl=vendor" -H "Content-Type: application/json" http://localhost:9080/api/v1/labels
	lbl := c.PostForm("lbl")

	images, err := rs.repository.FindByLabel(lbl)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}
}

func (rs *Rest) getImagesByVolume(c *gin.Context) {
	// curl --data "vlm=vendor" -H "Content-Type: application/json" http://localhost:9080/api/v1/labels
	vlm := c.PostForm("vlm")

	images, err := rs.repository.FindByVolume(vlm)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}

	c.Status(http.StatusNoContent)
}
<commit_msg>cobra migration completed<commit_after>package rest

import (
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	//"github.com/emicklei/go-restful"
	"github.com/gocraft/health"

	"github.com/gi4nks/quant"

	repos "github.com/streamtune/gadget/repos"
	utils "github.com/streamtune/gadget/utils"
)

//should be global Var
var stream = health.NewStream()

type Rest struct {
	parrot *quant.Parrot

	configuration *utils.Configuration
	repository    *repos.Repository
	commands      *repos.Commands
	utils         *utils.Utilities
}

func NewRest(p quant.Parrot, c utils.Configuration, r repos.Repository, cm repos.Commands, u utils.Utilities) *Rest {
	return &Rest{parrot: &p, configuration: &c, repository: &r, commands: &cm, utils: &u}
}

func (rs *Rest) Serve() {
	// Log to stdout!
	stream.AddSink(&health.WriterSink{os.Stdout})
	// Make sink and add it to stream
	sink := health.NewJsonPollingSink(time.Minute*5, time.Minute*20)
	stream.AddSink(sink)
	// Start the HTTP server! This will expose metrics via a JSON API.
	adr := ":5001"
	sink.StartServer(adr)

	gin.SetMode(rs.configuration.RestServerMode)

	r := gin.Default()
	r.Use(rs.cors())

	admin := r.Group("/api/v1/admin")
	{
		admin.POST("/debug", rs.postDebug)
		admin.POST("/revive", rs.postRevive)
		admin.POST("/update", rs.postUpdate)
	}

	images := r.Group("/api/v1/images")
	{
		images.GET("/", rs.getImages)
		images.GET("/:repo/:tag", rs.getImageByRepoTag)
		images.GET("/:repo", rs.getImageByTag)
		images.POST("/limit", rs.postImagesByLimit)
	}

	labels := r.Group("/api/v1/labels")
	{
		labels.GET("/", rs.getImagesWithLabels)
		labels.POST("", rs.getImagesByLabel)
	}

	volumes := r.Group("/api/v1/volumes")
	{
		volumes.GET("/", rs.getImagesWithVolumes)
		volumes.POST("", rs.getImagesByVolume)
	}

	rs.parrot.Println("Gadget Rest APIs running on port", rs.configuration.RestPort)

	r.Run(":" + strconv.Itoa(rs.configuration.RestPort))

}

func (rs *Rest) cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Add("Access-Control-Allow-Origin", "*")
		c.Next()
	}
}

func (rs *Rest) postRevive(c *gin.Context) {
	err := rs.commands.Revive()

	if err != nil {
		c.Status(http.StatusOK)
	} else {
		c.Status(http.StatusNotAcceptable)
	}
}

func (rs *Rest) postUpdate(c *gin.Context) {
	// curl -i -X POST -H "Content-Type: application/json" http://localhost:9080/api/v1/admin/update

	err := rs.commands.Update()

	if err != nil {
		c.Status(http.StatusOK)
	} else {
		c.Status(http.StatusNotAcceptable)
	}
}

func (rs *Rest) postDebug(c *gin.Context) {
	dbg, err := strconv.ParseBool(c.PostForm("dbg"))

	if err != nil {
		dbg = false
	}

	rs.commands.Debug(dbg)

	c.Status(http.StatusOK)
}

func (rs *Rest) getImages(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/images
	images, err := rs.repository.GetAll()

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if rs.configuration.DebugMode {
		for _, img := range images {
			rs.parrot.Debug("[" + img.ID + "] - [" + strconv.Itoa(len(img.Tags)) + "] [" + strconv.Itoa(len(img.Labels)) + "]")
		}
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}
}

func (rs *Rest) postImagesByLimit(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/images/limit/:count
	count, err := strconv.Atoi(c.PostForm("count"))

	if err != nil {
		c.Status(http.StatusBadRequest)
		return
	}

	images, err := rs.repository.GetLimit(count)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if rs.configuration.DebugMode {
		for _, img := range images {
			rs.parrot.Debug("[" + img.ID + "] - [" + strconv.Itoa(len(img.Tags)) + "] [" + strconv.Itoa(len(img.Labels)) + "]")
		}
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}
}

func (rs *Rest) getImageByRepoTag(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/images/

	tag := c.Param("tag")
	repo := c.Param("repo")

	images, err := rs.repository.FindByTag(repo + "/" + tag)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}

}

func (rs *Rest) getImageByTag(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/images/
	repo := c.Param("repo")

	images, err := rs.repository.FindByTag(repo)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}
}

func (rs *Rest) getImagesWithLabels(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/labels
	images, err := rs.repository.GetImagesWithLabels()

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}

	c.Status(http.StatusNoContent)
}

func (rs *Rest) getImagesWithVolumes(c *gin.Context) {
	// curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/api/v1/volumes
	images, err := rs.repository.GetImagesWithVolumes()

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}

	c.Status(http.StatusNoContent)
}

func (rs *Rest) getImagesByLabel(c *gin.Context) {
	// curl --data "lbl=vendor" -H "Content-Type: application/json" http://localhost:9080/api/v1/labels
	lbl := c.PostForm("lbl")

	images, err := rs.repository.FindByLabel(lbl)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}
}

func (rs *Rest) getImagesByVolume(c *gin.Context) {
	// curl --data "vlm=vendor" -H "Content-Type: application/json" http://localhost:9080/api/v1/labels
	vlm := c.PostForm("vlm")

	images, err := rs.repository.FindByVolume(vlm)

	if err != nil {
		c.Status(http.StatusNotAcceptable)
		return
	}

	if len(images) == 0 {
		c.Status(http.StatusNoContent)
	} else {
		c.JSON(http.StatusOK, images)
	}

	c.Status(http.StatusNoContent)
}
<|endoftext|>


<commit_before>// Copyright (C) 2013 Max Riveiro
// Copyright (C) 2015 Grant A. Brady
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Package reuseport provides a function that returns a net.Listener powered by a net.FileListener with a SO_REUSEPORT option set to the socket.
package reuseport

import (
	"errors"
	"net"
	"os"
	"strconv"
	"syscall"
)

const (
	unsupportedProtoError = "Only tcp4 and tcp6 are supported"
	filePrefix            = "port."
)

// getSockaddr parses protocol and address and returns implementor syscall.Sockaddr: syscall.SockaddrInet4 or syscall.SockaddrInet6.
func getSockaddr(proto, addr string) (sa syscall.Sockaddr, soType int, err error) {
	var (
		addr4   [4]byte
		addr6   [16]byte
		tcpAddr *net.TCPAddr
		udpAddr *net.UDPAddr
	)

	if proto == "tcp" || proto == "tcp4" || proto == "tcp6" {
		tcpAddr, err = net.ResolveTCPAddr(proto, addr)
		if err != nil {
			return nil, -1, err
		}
	} else {
		udpAddr, err = net.ResolveUDPAddr(proto, addr)
		if err != nil {
			return nil, -1, err
		}
	}

	switch proto {
	default:
		return nil, -1, errors.New(unsupportedProtoError)
	case "tcp4":
		if tcpAddr.IP != nil {
			copy(addr4[:], tcpAddr.IP[12:16]) // copy last 4 bytes of slice to array
		}
		return &syscall.SockaddrInet4{Port: tcpAddr.Port, Addr: addr4}, syscall.AF_INET, nil
	case "tcp", "tcp6":
		if tcpAddr.IP != nil {
			copy(addr6[:], tcpAddr.IP) // copy all bytes of slice to array
		}
		return &syscall.SockaddrInet6{Port: tcpAddr.Port, Addr: addr6}, syscall.AF_INET6, nil
	case "udp4":
		if udpAddr.IP != nil {
			copy(addr4[:], udpAddr.IP[12:16]) // copy last 4 bytes of slice to array
		}
		return &syscall.SockaddrInet4{Port: udpAddr.Port, Addr: addr4}, syscall.AF_INET, nil
	case "udp", "udp6":
		if udpAddr.IP != nil {
			copy(addr6[:], udpAddr.IP) // copy all bytes of slice to array
		}
		return &syscall.SockaddrInet6{Port: udpAddr.Port, Addr: addr6}, syscall.AF_INET6, nil
	}
	return nil, -1, errors.New(unsupportedProtoError)
}

// NewReusablePortListener returns net.FileListener that created from a file discriptor for a socket with SO_REUSEPORT option.
func newSocket(proto, addr string) (fd *os.File, err error) {
	var (
		soType, fd int
		file       *os.File
		sockaddr   syscall.Sockaddr
	)

	if sockaddr, soType, err = getSockaddr(proto, addr); err != nil {
		return nil, err
	}

	if proto == "tcp" || proto == "tcp4" || proto == "tcp6" {
		if fd, err = syscall.Socket(soType, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil {
			return nil, err
		}
	} else {
		if fd, err = syscall.Socket(soType, syscall.SOCK_STREAM, syscall.IPPROTO_UDP); err != nil {
			return nil, err
		}
	}

	if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, reusePort, 1); err != nil {
		return nil, err
	}

	if err = syscall.Bind(fd, sockaddr); err != nil {
		return nil, err
	}

	// Set backlog size to the maximum
	if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
		return nil, err
	}

	// File Name get be nil
	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	return file, err
}

func NewReusableTCPListener(proto, addr string) (l *net.TCPListener, err error) {
	var file *os.File
	if file, err = newSocket(proto, addr); err != nil {
		return nil, err
	}

	if l, err = net.FileListener(file); err != nil {
		return nil, err
	}

	if err = file.Close(); err != nil {
		return nil, err
	}

	return l, err
}

func NewReusableUDPConn(proto, addr string) (c *net.UDPConn, err error) {
	var file *os.File
	if file, err = newSocket(proto, addr); err != nil {
		return nil, err
	}

	if c, err = net.FileConn(file); err != nil {
		return nil, err
	}

	if err = file.Close(); err != nil {
		return nil, err
	}

	return c, err
}
<commit_msg>Fix type issues<commit_after>// Copyright (C) 2013 Max Riveiro
// Copyright (C) 2015 Grant A. Brady
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Package reuseport provides a function that returns a net.Listener powered by a net.FileListener with a SO_REUSEPORT option set to the socket.
package reuseport

import (
	"errors"
	"net"
	"os"
	"strconv"
	"syscall"
)

const (
	unsupportedProtoError = "Only tcp4 and tcp6 are supported"
	filePrefix            = "port."
)

// getSockaddr parses protocol and address and returns implementor syscall.Sockaddr: syscall.SockaddrInet4 or syscall.SockaddrInet6.
func getSockaddr(proto, addr string) (sa syscall.Sockaddr, soType int, err error) {
	var (
		addr4   [4]byte
		addr6   [16]byte
		tcpAddr *net.TCPAddr
		udpAddr *net.UDPAddr
	)

	if proto == "tcp" || proto == "tcp4" || proto == "tcp6" {
		tcpAddr, err = net.ResolveTCPAddr(proto, addr)
		if err != nil {
			return nil, -1, err
		}
	} else {
		udpAddr, err = net.ResolveUDPAddr(proto, addr)
		if err != nil {
			return nil, -1, err
		}
	}

	switch proto {
	default:
		return nil, -1, errors.New(unsupportedProtoError)
	case "tcp4":
		if tcpAddr.IP != nil {
			copy(addr4[:], tcpAddr.IP[12:16]) // copy last 4 bytes of slice to array
		}
		return &syscall.SockaddrInet4{Port: tcpAddr.Port, Addr: addr4}, syscall.AF_INET, nil
	case "tcp", "tcp6":
		if tcpAddr.IP != nil {
			copy(addr6[:], tcpAddr.IP) // copy all bytes of slice to array
		}
		return &syscall.SockaddrInet6{Port: tcpAddr.Port, Addr: addr6}, syscall.AF_INET6, nil
	case "udp4":
		if udpAddr.IP != nil {
			copy(addr4[:], udpAddr.IP[12:16]) // copy last 4 bytes of slice to array
		}
		return &syscall.SockaddrInet4{Port: udpAddr.Port, Addr: addr4}, syscall.AF_INET, nil
	case "udp", "udp6":
		if udpAddr.IP != nil {
			copy(addr6[:], udpAddr.IP) // copy all bytes of slice to array
		}
		return &syscall.SockaddrInet6{Port: udpAddr.Port, Addr: addr6}, syscall.AF_INET6, nil
	}
	return nil, -1, errors.New(unsupportedProtoError)
}

// NewReusablePortListener returns net.FileListener that created from a file discriptor for a socket with SO_REUSEPORT option.
func newSocket(proto, addr string) (file *os.File, err error) {
	var (
		soType, fd int
		sockaddr   syscall.Sockaddr
	)

	if sockaddr, soType, err = getSockaddr(proto, addr); err != nil {
		return nil, err
	}

	if proto == "tcp" || proto == "tcp4" || proto == "tcp6" {
		if fd, err = syscall.Socket(soType, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil {
			return nil, err
		}
	} else {
		if fd, err = syscall.Socket(soType, syscall.SOCK_STREAM, syscall.IPPROTO_UDP); err != nil {
			return nil, err
		}
	}

	if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, reusePort, 1); err != nil {
		return nil, err
	}

	if err = syscall.Bind(fd, sockaddr); err != nil {
		return nil, err
	}

	// Set backlog size to the maximum
	if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
		return nil, err
	}

	// File Name get be nil
	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	return file, err
}

func NewReusableTCPListener(proto, addr string) (l net.TCPListener, err error) {
	var file *os.File
	if file, err = newSocket(proto, addr); err != nil {
		return nil, err
	}

	if l, err = net.FileListener(file); err != nil {
		return nil, err
	}

	if err = file.Close(); err != nil {
		return nil, err
	}

	return l, err
}

func NewReusableUDPConn(proto, addr string) (c net.UDPConn, err error) {
	var file *os.File
	if file, err = newSocket(proto, addr); err != nil {
		return nil, err
	}

	if c, err = net.FileConn(file); err != nil {
		return nil, err
	}

	if err = file.Close(); err != nil {
		return nil, err
	}

	return c, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
)

func findBoxes(pkg *build.Package) map[string]bool {
	// create one list of files for this package
	filenames := make([]string, 0, len(pkg.GoFiles)+len(pkg.CgoFiles))
	filenames = append(filenames, pkg.GoFiles...)
	filenames = append(filenames, pkg.CgoFiles...)

	// create map of boxes to embed
	var boxMap = make(map[string]bool)

	// loop over files, search for rice.FindBox(..) calls
	for _, filename := range filenames {
		// find full filepath
		fullpath := filepath.Join(pkg.Dir, filename)
		verbosef("scanning file %s\n", fullpath)

		fset := token.NewFileSet()
		f, err := parser.ParseFile(fset, fullpath, nil, 0)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		var riceIsImported bool
		for _, imp := range f.Imports {
			if imp.Path.Value == "\"github.com/GeertJohan/go.rice\"" {
				riceIsImported = true
				break
			}
		}
		if !riceIsImported {
			// Rice wasn't imported, so we won't find a box.
			continue
		}

		// Inspect AST, looking for calls to (Must)?FindBox.
		// First parameter of the func must be a basic literal.
		// Identifiers won't be resolved.
		var nextBasicLitParamIsBoxName bool
		ast.Inspect(f, func(node ast.Node) bool {
			if node == nil {
				return false
			}
			switch x := node.(type) {
			case *ast.Ident:
				if nextBasicLitParamIsBoxName {
					nextBasicLitParamIsBoxName = false
				}
				if x.Name == "FindBox" || x.Name == "MustFindBox" {
					nextBasicLitParamIsBoxName = true
				}
			case *ast.BasicLit:
				if nextBasicLitParamIsBoxName && x.Kind == token.STRING {
					nextBasicLitParamIsBoxName = false
					// trim "" or ``
					name := x.Value[1 : len(x.Value)-1]
					boxMap[name] = true
					verbosef("\tfound box %q\n", name)
				}

			default:
				if nextBasicLitParamIsBoxName {
					nextBasicLitParamIsBoxName = false
				}
			}
			return true
		})
	}

	return boxMap
}
<commit_msg>findBoxes: do not analyze *.rice-box.go files<commit_after>package main

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"strings"
)

func findBoxes(pkg *build.Package) map[string]bool {
	// create one list of files for this package
	filenames := make([]string, 0, len(pkg.GoFiles)+len(pkg.CgoFiles))
	filenames = append(filenames, pkg.GoFiles...)
	filenames = append(filenames, pkg.CgoFiles...)

	// create map of boxes to embed
	var boxMap = make(map[string]bool)

	// loop over files, search for rice.FindBox(..) calls
	for _, filename := range filenames {
		// find full filepath
		fullpath := filepath.Join(pkg.Dir, filename)
		if strings.HasSuffix(filename, "rice-box.go") {
			// Ignore *.rice-box.go files
			verbosef("skipping file %q\n", fullpath)
			continue
		}
		verbosef("scanning file %q\n", fullpath)

		fset := token.NewFileSet()
		f, err := parser.ParseFile(fset, fullpath, nil, 0)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		var riceIsImported bool
		for _, imp := range f.Imports {
			if imp.Path.Value == "\"github.com/GeertJohan/go.rice\"" {
				riceIsImported = true
				break
			}
		}
		if !riceIsImported {
			// Rice wasn't imported, so we won't find a box.
			continue
		}

		// Inspect AST, looking for calls to (Must)?FindBox.
		// First parameter of the func must be a basic literal.
		// Identifiers won't be resolved.
		var nextBasicLitParamIsBoxName bool
		ast.Inspect(f, func(node ast.Node) bool {
			if node == nil {
				return false
			}
			switch x := node.(type) {
			case *ast.Ident:
				if nextBasicLitParamIsBoxName {
					nextBasicLitParamIsBoxName = false
				}
				if x.Name == "FindBox" || x.Name == "MustFindBox" {
					nextBasicLitParamIsBoxName = true
				}
			case *ast.BasicLit:
				if nextBasicLitParamIsBoxName && x.Kind == token.STRING {
					nextBasicLitParamIsBoxName = false
					// trim "" or ``
					name := x.Value[1 : len(x.Value)-1]
					boxMap[name] = true
					verbosef("\tfound box %q\n", name)
				}

			default:
				if nextBasicLitParamIsBoxName {
					nextBasicLitParamIsBoxName = false
				}
			}
			return true
		})
	}

	return boxMap
}
<|endoftext|>


<commit_before>// Copyright 2016 The Prometheus Authors
// 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 ring

// Based on https://raw.githubusercontent.com/stathat/consistent/master/consistent.go

import (
	"errors"
	"math"
	"sort"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

type uint32s []uint32

func (x uint32s) Len() int           { return len(x) }
func (x uint32s) Less(i, j int) bool { return x[i] < x[j] }
func (x uint32s) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

// ErrEmptyRing is the error returned when trying to get an element when nothing has been added to hash.
var ErrEmptyRing = errors.New("empty circle")

var (
	ingestorOwnershipDesc = prometheus.NewDesc(
		"prometheus_distributor_ingester_ownership_percent",
		"The percent ownership of the ring by ingestor",
		[]string{"ingester"}, nil,
	)
	ingesterTotalDesc = prometheus.NewDesc(
		"prometheus_distributor_ingesters_total",
		"Number of ingesters in the ring",
		nil, nil,
	)
	tokensTotalDesc = prometheus.NewDesc(
		"prometheus_distributor_tokens_total",
		"Number of token in the ring",
		nil, nil,
	)
)

// CoordinationStateClient is an interface to getting changes to the coordination
// state.  Should allow us to swap out Consul for something else (mesh?) later.
type CoordinationStateClient interface {
	WatchKey(key string, factory InstanceFactory, done <-chan struct{}, f func(interface{}) bool)
}

// Ring holds the information about the members of the consistent hash circle.
type Ring struct {
	client     CoordinationStateClient
	quit, done chan struct{}

	mtx      sync.RWMutex
	ringDesc Desc
}

// New creates a new Ring
func New(client CoordinationStateClient) *Ring {
	r := &Ring{
		client: client,
		quit:   make(chan struct{}),
		done:   make(chan struct{}),
	}
	go r.loop()
	return r
}

// Stop the distributor.
func (r *Ring) Stop() {
	close(r.quit)
	<-r.done
}

func (r *Ring) loop() {
	defer close(r.done)
	r.client.WatchKey(consulKey, descFactory, r.quit, func(value interface{}) bool {
		if value == nil {
			log.Infof("Ring doesn't exist in consul yet.")
			return true
		}

		ringDesc := value.(*Desc)
		r.mtx.Lock()
		defer r.mtx.Unlock()
		r.ringDesc = *ringDesc
		return true
	})
}

// Get returns a collector close to the hash in the circle.
func (r *Ring) Get(key uint32) (IngesterDesc, error) {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	if len(r.ringDesc.Tokens) == 0 {
		return IngesterDesc{}, ErrEmptyRing
	}
	i := r.search(key)
	return r.ringDesc.Ingesters[r.ringDesc.Tokens[i].Ingester], nil
}

// GetAll returns all ingesters in the circle.
func (r *Ring) GetAll() []IngesterDesc {
	r.mtx.RLock()
	defer r.mtx.RUnlock()

	ingesters := make([]IngesterDesc, 0, len(r.ringDesc.Ingesters))
	for _, ingester := range r.ringDesc.Ingesters {
		ingesters = append(ingesters, ingester)
	}
	return ingesters
}

func (r *Ring) search(key uint32) int {
	i := sort.Search(len(r.ringDesc.Tokens), func(x int) bool {
		return r.ringDesc.Tokens[x].Token > key
	})
	if i >= len(r.ringDesc.Tokens) {
		i = 0
	}
	return i
}

// Describe implements prometheus.Collector.
func (r *Ring) Describe(ch chan<- *prometheus.Desc) {
	ch <- ingestorOwnershipDesc
}

// Collect implements prometheus.Collector.
func (r *Ring) Collect(ch chan<- prometheus.Metric) {
	r.mtx.RLock()
	defer r.mtx.RUnlock()

	owned := map[string]uint32{}
	for i, token := range r.ringDesc.Tokens {
		var diff uint32
		if i+1 == len(r.ringDesc.Tokens) {
			diff = (math.MaxUint32 - token.Token) + r.ringDesc.Tokens[0].Token
		} else {
			diff = r.ringDesc.Tokens[i+1].Token - token.Token
		}
		owned[token.Ingester] = owned[token.Ingester] + diff
	}

	for id, totalOwned := range owned {
		ch <- prometheus.MustNewConstMetric(
			ingestorOwnershipDesc,
			prometheus.GaugeValue,
			float64(totalOwned)/float64(math.MaxUint32),
			id,
		)
	}

	ch <- prometheus.MustNewConstMetric(
		ingesterTotalDesc,
		prometheus.GaugeValue,
		float64(len(r.ringDesc.Ingesters)),
	)
	ch <- prometheus.MustNewConstMetric(
		tokensTotalDesc,
		prometheus.GaugeValue,
		float64(len(r.ringDesc.Tokens)),
	)
}
<commit_msg>ingestor -> ingester<commit_after>// Copyright 2016 The Prometheus Authors
// 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 ring

// Based on https://raw.githubusercontent.com/stathat/consistent/master/consistent.go

import (
	"errors"
	"math"
	"sort"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

type uint32s []uint32

func (x uint32s) Len() int           { return len(x) }
func (x uint32s) Less(i, j int) bool { return x[i] < x[j] }
func (x uint32s) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

// ErrEmptyRing is the error returned when trying to get an element when nothing has been added to hash.
var ErrEmptyRing = errors.New("empty circle")

var (
	ingesterOwnershipDesc = prometheus.NewDesc(
		"prometheus_distributor_ingester_ownership_percent",
		"The percent ownership of the ring by ingester",
		[]string{"ingester"}, nil,
	)
	ingesterTotalDesc = prometheus.NewDesc(
		"prometheus_distributor_ingesters_total",
		"Number of ingesters in the ring",
		nil, nil,
	)
	tokensTotalDesc = prometheus.NewDesc(
		"prometheus_distributor_tokens_total",
		"Number of token in the ring",
		nil, nil,
	)
)

// CoordinationStateClient is an interface to getting changes to the coordination
// state.  Should allow us to swap out Consul for something else (mesh?) later.
type CoordinationStateClient interface {
	WatchKey(key string, factory InstanceFactory, done <-chan struct{}, f func(interface{}) bool)
}

// Ring holds the information about the members of the consistent hash circle.
type Ring struct {
	client     CoordinationStateClient
	quit, done chan struct{}

	mtx      sync.RWMutex
	ringDesc Desc
}

// New creates a new Ring
func New(client CoordinationStateClient) *Ring {
	r := &Ring{
		client: client,
		quit:   make(chan struct{}),
		done:   make(chan struct{}),
	}
	go r.loop()
	return r
}

// Stop the distributor.
func (r *Ring) Stop() {
	close(r.quit)
	<-r.done
}

func (r *Ring) loop() {
	defer close(r.done)
	r.client.WatchKey(consulKey, descFactory, r.quit, func(value interface{}) bool {
		if value == nil {
			log.Infof("Ring doesn't exist in consul yet.")
			return true
		}

		ringDesc := value.(*Desc)
		r.mtx.Lock()
		defer r.mtx.Unlock()
		r.ringDesc = *ringDesc
		return true
	})
}

// Get returns a collector close to the hash in the circle.
func (r *Ring) Get(key uint32) (IngesterDesc, error) {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	if len(r.ringDesc.Tokens) == 0 {
		return IngesterDesc{}, ErrEmptyRing
	}
	i := r.search(key)
	return r.ringDesc.Ingesters[r.ringDesc.Tokens[i].Ingester], nil
}

// GetAll returns all ingesters in the circle.
func (r *Ring) GetAll() []IngesterDesc {
	r.mtx.RLock()
	defer r.mtx.RUnlock()

	ingesters := make([]IngesterDesc, 0, len(r.ringDesc.Ingesters))
	for _, ingester := range r.ringDesc.Ingesters {
		ingesters = append(ingesters, ingester)
	}
	return ingesters
}

func (r *Ring) search(key uint32) int {
	i := sort.Search(len(r.ringDesc.Tokens), func(x int) bool {
		return r.ringDesc.Tokens[x].Token > key
	})
	if i >= len(r.ringDesc.Tokens) {
		i = 0
	}
	return i
}

// Describe implements prometheus.Collector.
func (r *Ring) Describe(ch chan<- *prometheus.Desc) {
	ch <- ingesterOwnershipDesc
}

// Collect implements prometheus.Collector.
func (r *Ring) Collect(ch chan<- prometheus.Metric) {
	r.mtx.RLock()
	defer r.mtx.RUnlock()

	owned := map[string]uint32{}
	for i, token := range r.ringDesc.Tokens {
		var diff uint32
		if i+1 == len(r.ringDesc.Tokens) {
			diff = (math.MaxUint32 - token.Token) + r.ringDesc.Tokens[0].Token
		} else {
			diff = r.ringDesc.Tokens[i+1].Token - token.Token
		}
		owned[token.Ingester] = owned[token.Ingester] + diff
	}

	for id, totalOwned := range owned {
		ch <- prometheus.MustNewConstMetric(
			ingesterOwnershipDesc,
			prometheus.GaugeValue,
			float64(totalOwned)/float64(math.MaxUint32),
			id,
		)
	}

	ch <- prometheus.MustNewConstMetric(
		ingesterTotalDesc,
		prometheus.GaugeValue,
		float64(len(r.ringDesc.Ingesters)),
	)
	ch <- prometheus.MustNewConstMetric(
		tokensTotalDesc,
		prometheus.GaugeValue,
		float64(len(r.ringDesc.Tokens)),
	)
}
<|endoftext|>


<commit_before>package shared

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
	"gopkg.in/robfig/cron.v2"

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

type InstanceAction string

const (
	Stop     InstanceAction = "stop"
	Start    InstanceAction = "start"
	Restart  InstanceAction = "restart"
	Freeze   InstanceAction = "freeze"
	Unfreeze InstanceAction = "unfreeze"
)

func IsInt64(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	return nil
}

func IsUint8(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseUint(value, 10, 8)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s. Must be between 0 and 255", value)
	}

	return nil
}

func IsUint32(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseUint(value, 10, 32)
	if err != nil {
		return fmt.Errorf("Invalid value for uint32: %s: %v", value, err)
	}

	return nil
}

func IsPriority(value string) error {
	if value == "" {
		return nil
	}

	valueInt, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	if valueInt < 0 || valueInt > 10 {
		return fmt.Errorf("Invalid value for a limit '%s'. Must be between 0 and 10", value)
	}

	return nil
}

func IsBool(value string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
		return fmt.Errorf("Invalid value for a boolean: %s", value)
	}

	return nil
}

func IsOneOf(value string, valid []string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(value, valid) {
		return fmt.Errorf("Invalid value: %s (not one of %s)", value, valid)
	}

	return nil
}

func IsAny(value string) error {
	return nil
}

func IsNotEmpty(value string) error {
	if value == "" {
		return fmt.Errorf("Required value")
	}

	return nil
}

// IsSize checks if string is valid size according to units.ParseByteSizeString.
func IsSize(value string) error {
	if value == "" {
		return nil
	}

	_, err := units.ParseByteSizeString(value)
	if err != nil {
		return err
	}

	return nil
}

// IsDeviceID validates string is four lowercase hex characters suitable as Vendor or Device ID.
func IsDeviceID(value string) error {
	if value == "" {
		return nil
	}

	regexHexLc, err := regexp.Compile("^[0-9a-f]+$")
	if err != nil {
		return err
	}

	if len(value) != 4 || !regexHexLc.MatchString(value) {
		return fmt.Errorf("Invalid value, must be four lower case hex characters")
	}

	return nil
}

// IsRootDiskDevice returns true if the given device representation is configured as root disk for
// a container. It typically get passed a specific entry of api.Instance.Devices.
func IsRootDiskDevice(device map[string]string) bool {
	// Root disk devices also need a non-empty "pool" property, but we can't check that here
	// because this function is used with clients talking to older servers where there was no
	// concept of a storage pool, and also it is used for migrating from old to new servers.
	// The validation of the non-empty "pool" property is done inside the disk device itself.
	if device["type"] == "disk" && device["path"] == "/" && device["source"] == "" {
		return true
	}

	return false
}

// GetRootDiskDevice returns the container device that is configured as root disk
func GetRootDiskDevice(devices map[string]map[string]string) (string, map[string]string, error) {
	var devName string
	var dev map[string]string

	for n, d := range devices {
		if IsRootDiskDevice(d) {
			if devName != "" {
				return "", nil, fmt.Errorf("More than one root device found")
			}

			devName = n
			dev = d
		}
	}

	if devName != "" {
		return devName, dev, nil
	}

	return "", nil, fmt.Errorf("No root device could be found")
}

// KnownInstanceConfigKeys maps all fully defined, well-known config keys
// to an appropriate checker function, which validates whether or not a
// given value is syntactically legal.
var KnownInstanceConfigKeys = map[string]func(value string) error{
	"boot.autostart":             IsBool,
	"boot.autostart.delay":       IsInt64,
	"boot.autostart.priority":    IsInt64,
	"boot.stop.priority":         IsInt64,
	"boot.host_shutdown_timeout": IsInt64,

	"limits.cpu": func(value string) error {
		if value == "" {
			return nil
		}

		// Validate the character set
		match, _ := regexp.MatchString("^[-,0-9]*$", value)
		if !match {
			return fmt.Errorf("Invalid CPU limit syntax")
		}

		// Validate first character
		if strings.HasPrefix(value, "-") || strings.HasPrefix(value, ",") {
			return fmt.Errorf("CPU limit can't start with a separator")
		}

		// Validate last character
		if strings.HasSuffix(value, "-") || strings.HasSuffix(value, ",") {
			return fmt.Errorf("CPU limit can't end with a separator")
		}

		return nil
	},
	"limits.cpu.allowance": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			// Percentage based allocation
			_, err := strconv.Atoi(strings.TrimSuffix(value, "%"))
			if err != nil {
				return err
			}

			return nil
		}

		// Time based allocation
		fields := strings.SplitN(value, "/", 2)
		if len(fields) != 2 {
			return fmt.Errorf("Invalid allowance: %s", value)
		}

		_, err := strconv.Atoi(strings.TrimSuffix(fields[0], "ms"))
		if err != nil {
			return err
		}

		_, err = strconv.Atoi(strings.TrimSuffix(fields[1], "ms"))
		if err != nil {
			return err
		}

		return nil
	},
	"limits.cpu.priority": IsPriority,

	"limits.disk.priority": IsPriority,

	"limits.memory": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			_, err := strconv.ParseInt(strings.TrimSuffix(value, "%"), 10, 64)
			if err != nil {
				return err
			}

			return nil
		}

		_, err := units.ParseByteSizeString(value)
		if err != nil {
			return err
		}

		return nil
	},
	"limits.memory.enforce": func(value string) error {
		return IsOneOf(value, []string{"soft", "hard"})
	},
	"limits.memory.swap":          IsBool,
	"limits.memory.swap.priority": IsPriority,
	"limits.memory.hugepages":     IsBool,

	"limits.network.priority": IsPriority,

	"limits.processes": IsInt64,

	"linux.kernel_modules": IsAny,

	"migration.incremental.memory":            IsBool,
	"migration.incremental.memory.iterations": IsUint32,
	"migration.incremental.memory.goal":       IsUint32,

	"nvidia.runtime":             IsBool,
	"nvidia.driver.capabilities": IsAny,
	"nvidia.require.cuda":        IsAny,
	"nvidia.require.driver":      IsAny,

	"security.nesting":       IsBool,
	"security.privileged":    IsBool,
	"security.devlxd":        IsBool,
	"security.devlxd.images": IsBool,

	"security.protection.delete": IsBool,
	"security.protection.shift":  IsBool,

	"security.idmap.base":     IsUint32,
	"security.idmap.isolated": IsBool,
	"security.idmap.size":     IsUint32,

	"security.secureboot": IsBool,

	"security.syscalls.blacklist_default":       IsBool,
	"security.syscalls.blacklist_compat":        IsBool,
	"security.syscalls.blacklist":               IsAny,
	"security.syscalls.intercept.mknod":         IsBool,
	"security.syscalls.intercept.mount":         IsBool,
	"security.syscalls.intercept.mount.allowed": IsAny,
	"security.syscalls.intercept.mount.fuse":    IsAny,
	"security.syscalls.intercept.mount.shift":   IsBool,
	"security.syscalls.intercept.setxattr":      IsBool,
	"security.syscalls.whitelist":               IsAny,

	"snapshots.schedule": func(value string) error {
		if value == "" {
			return nil
		}

		if len(strings.Split(value, " ")) != 5 {
			return fmt.Errorf("Schedule must be of the form: <minute> <hour> <day-of-month> <month> <day-of-week>")
		}

		_, err := cron.Parse(fmt.Sprintf("* %s", value))
		if err != nil {
			return errors.Wrap(err, "Error parsing schedule")
		}

		return nil
	},
	"snapshots.schedule.stopped": IsBool,
	"snapshots.pattern":          IsAny,
	"snapshots.expiry": func(value string) error {
		// Validate expression
		_, err := GetSnapshotExpiry(time.Time{}, value)
		return err
	},

	// Caller is responsible for full validation of any raw.* value
	"raw.apparmor": IsAny,
	"raw.idmap":    IsAny,
	"raw.lxc":      IsAny,
	"raw.qemu":     IsAny,
	"raw.seccomp":  IsAny,

	"volatile.apply_template":   IsAny,
	"volatile.base_image":       IsAny,
	"volatile.last_state.idmap": IsAny,
	"volatile.last_state.power": IsAny,
	"volatile.idmap.base":       IsAny,
	"volatile.idmap.current":    IsAny,
	"volatile.idmap.next":       IsAny,
	"volatile.apply_quota":      IsAny,
}

// ConfigKeyChecker returns a function that will check whether or not
// a provide value is valid for the associate config key.  Returns an
// error if the key is not known.  The checker function only performs
// syntactic checking of the value, semantic and usage checking must
// be done by the caller.  User defined keys are always considered to
// be valid, e.g. user.* and environment.* keys.
func ConfigKeyChecker(key string) (func(value string) error, error) {
	if f, ok := KnownInstanceConfigKeys[key]; ok {
		return f, nil
	}

	if strings.HasPrefix(key, "volatile.") {
		if strings.HasSuffix(key, ".hwaddr") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".name") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".host_name") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".mtu") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".created") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".id") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".vlan") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".spoofcheck") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".apply_quota") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, "vm.uuid") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".ceph_rbd") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".driver") {
			return IsAny, nil
		}
	}

	if strings.HasPrefix(key, "environment.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "user.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "image.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "limits.kernel.") &&
		(len(key) > len("limits.kernel.")) {
		return IsAny, nil
	}

	return nil, fmt.Errorf("Unknown configuration key: %s", key)
}

// InstanceGetParentAndSnapshotName returns the parent container name, snapshot
// name, and whether it actually was a snapshot name.
func InstanceGetParentAndSnapshotName(name string) (string, string, bool) {
	fields := strings.SplitN(name, SnapshotDelimiter, 2)
	if len(fields) == 1 {
		return name, "", false
	}

	return fields[0], fields[1], true
}
<commit_msg>shared/instance: InstanceGetParentAndSnapshotName comments<commit_after>package shared

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
	"gopkg.in/robfig/cron.v2"

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

type InstanceAction string

const (
	Stop     InstanceAction = "stop"
	Start    InstanceAction = "start"
	Restart  InstanceAction = "restart"
	Freeze   InstanceAction = "freeze"
	Unfreeze InstanceAction = "unfreeze"
)

func IsInt64(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	return nil
}

func IsUint8(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseUint(value, 10, 8)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s. Must be between 0 and 255", value)
	}

	return nil
}

func IsUint32(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseUint(value, 10, 32)
	if err != nil {
		return fmt.Errorf("Invalid value for uint32: %s: %v", value, err)
	}

	return nil
}

func IsPriority(value string) error {
	if value == "" {
		return nil
	}

	valueInt, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	if valueInt < 0 || valueInt > 10 {
		return fmt.Errorf("Invalid value for a limit '%s'. Must be between 0 and 10", value)
	}

	return nil
}

func IsBool(value string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
		return fmt.Errorf("Invalid value for a boolean: %s", value)
	}

	return nil
}

func IsOneOf(value string, valid []string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(value, valid) {
		return fmt.Errorf("Invalid value: %s (not one of %s)", value, valid)
	}

	return nil
}

func IsAny(value string) error {
	return nil
}

func IsNotEmpty(value string) error {
	if value == "" {
		return fmt.Errorf("Required value")
	}

	return nil
}

// IsSize checks if string is valid size according to units.ParseByteSizeString.
func IsSize(value string) error {
	if value == "" {
		return nil
	}

	_, err := units.ParseByteSizeString(value)
	if err != nil {
		return err
	}

	return nil
}

// IsDeviceID validates string is four lowercase hex characters suitable as Vendor or Device ID.
func IsDeviceID(value string) error {
	if value == "" {
		return nil
	}

	regexHexLc, err := regexp.Compile("^[0-9a-f]+$")
	if err != nil {
		return err
	}

	if len(value) != 4 || !regexHexLc.MatchString(value) {
		return fmt.Errorf("Invalid value, must be four lower case hex characters")
	}

	return nil
}

// IsRootDiskDevice returns true if the given device representation is configured as root disk for
// a container. It typically get passed a specific entry of api.Instance.Devices.
func IsRootDiskDevice(device map[string]string) bool {
	// Root disk devices also need a non-empty "pool" property, but we can't check that here
	// because this function is used with clients talking to older servers where there was no
	// concept of a storage pool, and also it is used for migrating from old to new servers.
	// The validation of the non-empty "pool" property is done inside the disk device itself.
	if device["type"] == "disk" && device["path"] == "/" && device["source"] == "" {
		return true
	}

	return false
}

// GetRootDiskDevice returns the container device that is configured as root disk
func GetRootDiskDevice(devices map[string]map[string]string) (string, map[string]string, error) {
	var devName string
	var dev map[string]string

	for n, d := range devices {
		if IsRootDiskDevice(d) {
			if devName != "" {
				return "", nil, fmt.Errorf("More than one root device found")
			}

			devName = n
			dev = d
		}
	}

	if devName != "" {
		return devName, dev, nil
	}

	return "", nil, fmt.Errorf("No root device could be found")
}

// KnownInstanceConfigKeys maps all fully defined, well-known config keys
// to an appropriate checker function, which validates whether or not a
// given value is syntactically legal.
var KnownInstanceConfigKeys = map[string]func(value string) error{
	"boot.autostart":             IsBool,
	"boot.autostart.delay":       IsInt64,
	"boot.autostart.priority":    IsInt64,
	"boot.stop.priority":         IsInt64,
	"boot.host_shutdown_timeout": IsInt64,

	"limits.cpu": func(value string) error {
		if value == "" {
			return nil
		}

		// Validate the character set
		match, _ := regexp.MatchString("^[-,0-9]*$", value)
		if !match {
			return fmt.Errorf("Invalid CPU limit syntax")
		}

		// Validate first character
		if strings.HasPrefix(value, "-") || strings.HasPrefix(value, ",") {
			return fmt.Errorf("CPU limit can't start with a separator")
		}

		// Validate last character
		if strings.HasSuffix(value, "-") || strings.HasSuffix(value, ",") {
			return fmt.Errorf("CPU limit can't end with a separator")
		}

		return nil
	},
	"limits.cpu.allowance": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			// Percentage based allocation
			_, err := strconv.Atoi(strings.TrimSuffix(value, "%"))
			if err != nil {
				return err
			}

			return nil
		}

		// Time based allocation
		fields := strings.SplitN(value, "/", 2)
		if len(fields) != 2 {
			return fmt.Errorf("Invalid allowance: %s", value)
		}

		_, err := strconv.Atoi(strings.TrimSuffix(fields[0], "ms"))
		if err != nil {
			return err
		}

		_, err = strconv.Atoi(strings.TrimSuffix(fields[1], "ms"))
		if err != nil {
			return err
		}

		return nil
	},
	"limits.cpu.priority": IsPriority,

	"limits.disk.priority": IsPriority,

	"limits.memory": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			_, err := strconv.ParseInt(strings.TrimSuffix(value, "%"), 10, 64)
			if err != nil {
				return err
			}

			return nil
		}

		_, err := units.ParseByteSizeString(value)
		if err != nil {
			return err
		}

		return nil
	},
	"limits.memory.enforce": func(value string) error {
		return IsOneOf(value, []string{"soft", "hard"})
	},
	"limits.memory.swap":          IsBool,
	"limits.memory.swap.priority": IsPriority,
	"limits.memory.hugepages":     IsBool,

	"limits.network.priority": IsPriority,

	"limits.processes": IsInt64,

	"linux.kernel_modules": IsAny,

	"migration.incremental.memory":            IsBool,
	"migration.incremental.memory.iterations": IsUint32,
	"migration.incremental.memory.goal":       IsUint32,

	"nvidia.runtime":             IsBool,
	"nvidia.driver.capabilities": IsAny,
	"nvidia.require.cuda":        IsAny,
	"nvidia.require.driver":      IsAny,

	"security.nesting":       IsBool,
	"security.privileged":    IsBool,
	"security.devlxd":        IsBool,
	"security.devlxd.images": IsBool,

	"security.protection.delete": IsBool,
	"security.protection.shift":  IsBool,

	"security.idmap.base":     IsUint32,
	"security.idmap.isolated": IsBool,
	"security.idmap.size":     IsUint32,

	"security.secureboot": IsBool,

	"security.syscalls.blacklist_default":       IsBool,
	"security.syscalls.blacklist_compat":        IsBool,
	"security.syscalls.blacklist":               IsAny,
	"security.syscalls.intercept.mknod":         IsBool,
	"security.syscalls.intercept.mount":         IsBool,
	"security.syscalls.intercept.mount.allowed": IsAny,
	"security.syscalls.intercept.mount.fuse":    IsAny,
	"security.syscalls.intercept.mount.shift":   IsBool,
	"security.syscalls.intercept.setxattr":      IsBool,
	"security.syscalls.whitelist":               IsAny,

	"snapshots.schedule": func(value string) error {
		if value == "" {
			return nil
		}

		if len(strings.Split(value, " ")) != 5 {
			return fmt.Errorf("Schedule must be of the form: <minute> <hour> <day-of-month> <month> <day-of-week>")
		}

		_, err := cron.Parse(fmt.Sprintf("* %s", value))
		if err != nil {
			return errors.Wrap(err, "Error parsing schedule")
		}

		return nil
	},
	"snapshots.schedule.stopped": IsBool,
	"snapshots.pattern":          IsAny,
	"snapshots.expiry": func(value string) error {
		// Validate expression
		_, err := GetSnapshotExpiry(time.Time{}, value)
		return err
	},

	// Caller is responsible for full validation of any raw.* value
	"raw.apparmor": IsAny,
	"raw.idmap":    IsAny,
	"raw.lxc":      IsAny,
	"raw.qemu":     IsAny,
	"raw.seccomp":  IsAny,

	"volatile.apply_template":   IsAny,
	"volatile.base_image":       IsAny,
	"volatile.last_state.idmap": IsAny,
	"volatile.last_state.power": IsAny,
	"volatile.idmap.base":       IsAny,
	"volatile.idmap.current":    IsAny,
	"volatile.idmap.next":       IsAny,
	"volatile.apply_quota":      IsAny,
}

// ConfigKeyChecker returns a function that will check whether or not
// a provide value is valid for the associate config key.  Returns an
// error if the key is not known.  The checker function only performs
// syntactic checking of the value, semantic and usage checking must
// be done by the caller.  User defined keys are always considered to
// be valid, e.g. user.* and environment.* keys.
func ConfigKeyChecker(key string) (func(value string) error, error) {
	if f, ok := KnownInstanceConfigKeys[key]; ok {
		return f, nil
	}

	if strings.HasPrefix(key, "volatile.") {
		if strings.HasSuffix(key, ".hwaddr") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".name") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".host_name") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".mtu") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".created") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".id") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".vlan") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".spoofcheck") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".apply_quota") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, "vm.uuid") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".ceph_rbd") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".driver") {
			return IsAny, nil
		}
	}

	if strings.HasPrefix(key, "environment.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "user.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "image.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "limits.kernel.") &&
		(len(key) > len("limits.kernel.")) {
		return IsAny, nil
	}

	return nil, fmt.Errorf("Unknown configuration key: %s", key)
}

// InstanceGetParentAndSnapshotName returns the parent instance name, snapshot name,
// and whether it actually was a snapshot name.
func InstanceGetParentAndSnapshotName(name string) (string, string, bool) {
	fields := strings.SplitN(name, SnapshotDelimiter, 2)
	if len(fields) == 1 {
		return name, "", false
	}

	return fields[0], fields[1], true
}
<|endoftext|>


<commit_before>package reverseconnection

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/Symantec/Dominator/lib/log"
	"github.com/Symantec/Dominator/lib/log/prefixlogger"
	"github.com/Symantec/Dominator/lib/log/testlogger"
	libnet "github.com/Symantec/Dominator/lib/net"
)

const (
	testConnectString = "200 Connected to TestReverseDialer"
	testUrlPath       = "/_ReverseDialer_TEST_/connect"
)

var dialer *Dialer

type testAcceptEvent struct {
	conn  net.Conn
	error error
}

type serverType struct {
	logger log.DebugLogger
}

func createTestListener(logger log.DebugLogger) (*Listener, uint) {
	portNumber := 30000 + uint(rand.Intn(10000))
	listener, err := Listen("tcp", portNumber, logger)
	if err != nil {
		logger.Fatal(err)
	}
	return listener, portNumber
}

func createTestRealListener(logger log.DebugLogger) (net.Listener, uint) {
	portNumber := 30000 + uint(rand.Intn(10000))
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", portNumber))
	if err != nil {
		logger.Fatal(err)
	}
	return listener, portNumber
}

func createAcceptChannel(listener *Listener) <-chan testAcceptEvent {
	acceptChannel := make(chan testAcceptEvent, 1)
	go func() {
		conn, err := listener.Accept()
		acceptChannel <- testAcceptEvent{conn, err}
	}()
	return acceptChannel
}

func makeAndTestHttpConnection(dialer libnet.Dialer, portNumber uint,
	logger log.DebugLogger) error {
	conn, err := dialer.Dial("tcp", fmt.Sprintf("localhost:%d", portNumber))
	if err != nil {
		return errors.New("error dialing: " + err.Error())
	}
	return testHttpConnection(conn, logger)
}

func testEcho(conn net.Conn) error {
	testString := "reflect"
	writeBuffer := []byte(testString)
	if _, err := conn.Write(writeBuffer); err != nil {
		return errors.New("error writing test string: " + err.Error())
	}
	readBuffer := make([]byte, 256)
	if nRead, err := conn.Read(readBuffer); err != nil {
		return errors.New("error reading test string response: " + err.Error())
	} else if nRead != len(writeBuffer) {
		return fmt.Errorf("Wrote: %d bytes, read: %d bytes",
			len(writeBuffer), nRead)
	} else if rString := string(readBuffer[0:nRead]); rString != testString {
		return fmt.Errorf("Wrote: \"%s\", read: \"%s\"", testString, rString)
	}
	return conn.Close()
}

func testHttpConnection(conn net.Conn, logger log.DebugLogger) error {
	io.WriteString(conn, "CONNECT "+testUrlPath+" HTTP/1.0\n\n")
	resp, err := http.ReadResponse(bufio.NewReader(conn),
		&http.Request{Method: "CONNECT"})
	if err != nil {
		return errors.New("error connecting: " + err.Error())
	}
	if resp.StatusCode != http.StatusOK || resp.Status != testConnectString {
		return errors.New("unexpected HTTP response: " + resp.Status)
	}
	return testEcho(conn)
}

func (s *serverType) connectHandler(w http.ResponseWriter, req *http.Request) {
	if req.Method != "CONNECT" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	hijacker, ok := w.(http.Hijacker)
	if !ok {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		s.logger.Println("not a hijacker ", req.RemoteAddr)
		return
	}
	conn, _, err := hijacker.Hijack()
	if err != nil {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		s.logger.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
		return
	}
	_, err = io.WriteString(conn, "HTTP/1.0 "+testConnectString+"\n\n")
	if err != nil {
		s.logger.Println("error writing connect message: ", err.Error())
		return
	}
	if _, err := io.Copy(conn, conn); err != nil {
		s.logger.Println(err)
	}
}

func TestInjectAccept(t *testing.T) {
	logger := testlogger.New(t)
	acceptChannel := make(chan acceptEvent, 1)
	fakeListener := &Listener{
		logger:        logger,
		acceptChannel: acceptChannel,
	}
	server := &serverType{logger}
	http.HandleFunc(testUrlPath, server.connectHandler)
	go http.Serve(fakeListener, nil)
	realListener, portNumber := createTestRealListener(logger)
	sc, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", portNumber))
	if err != nil {
		t.Fatal(err)
	}
	slaveConn := sc.(libnet.TCPConn)
	masterConn, err := realListener.Accept()
	if err != nil {
		t.Fatal(err)
	}
	fakeListener.acceptChannel <- acceptEvent{
		&listenerConn{TCPConn: slaveConn}, nil}
	if err := testHttpConnection(masterConn, logger); err != nil {
		t.Fatal(err)
	}
}

func TestListen(t *testing.T) {
	logger := testlogger.New(t)
	listener, portNumber := createTestListener(logger)
	acceptChannel := createAcceptChannel(listener)
	dialConn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", portNumber))
	if err != nil {
		t.Fatal(err)
	}
	response := <-acceptChannel
	if response.error != nil {
		t.Fatal(response.error)
	}
	testString := "hello"
	writeBuffer := []byte(testString)
	if _, err := dialConn.Write(writeBuffer); err != nil {
		t.Fatal(err)
	}
	readBuffer := make([]byte, 256)
	if nRead, err := response.conn.Read(readBuffer); err != nil {
		t.Fatal(err)
	} else if nRead != len(writeBuffer) {
		t.Fatalf("Wrote: %d bytes, read: %d bytes", len(writeBuffer), nRead)
	} else if rString := string(readBuffer[0:nRead]); rString != testString {
		t.Fatalf("Wrote: \"%s\", read: \"%s\"", testString, rString)
	}
	if err := dialConn.Close(); err != nil {
		t.Fatal(err)
	}
	if err := response.conn.Close(); err != nil {
		t.Fatal(err)
	}
	if err := listener.Close(); err != nil {
		t.Fatal(err)
	}
}

func TestListenAndHttpServe(t *testing.T) {
	logger := testlogger.New(t)
	listener, portNumber := createTestListener(logger)
	serverMux := http.NewServeMux()
	server := &serverType{logger}
	serverMux.HandleFunc(testUrlPath, server.connectHandler)
	go http.Serve(listener, serverMux)
	err := makeAndTestHttpConnection(&net.Dialer{}, portNumber, logger)
	if err != nil {
		t.Fatal(err)
	}
}

func TestReverseListenTcp(t *testing.T) {
	tLogger := testlogger.New(t)
	// Set up slave.
	slaveListener, slavePortNumber := createTestListener(
		prefixlogger.New("slave: ", tLogger))
	slaveAddress := fmt.Sprintf("127.0.0.1:%d", slavePortNumber)
	t.Logf("slaveAddress: %s", slaveAddress)
	// Set up master
	masterLogger := prefixlogger.New("master: ", tLogger)
	masterListener, masterPortNumber := createTestRealListener(masterLogger)
	masterMux := http.NewServeMux()
	go http.Serve(masterListener, masterMux)
	dialer := NewDialer(nil, masterMux, 0, 0, masterLogger)
	// Make slave connect back to master.
	loopbackIP := [4]byte{127, 0, 0, 1}
	if slaveListener.connectionMap[loopbackIP] > 0 {
		t.Fatalf("slave listener already has %d connections",
			slaveListener.connectionMap[loopbackIP])
	}
	if dialer.connectionMap[slaveAddress] != nil {
		t.Fatal("master dialer already has a connection")
	}
	t.Log("making slave connect to master")
	go slaveListener.connectLoop(ReverseListenerConfig{
		Network:         "tcp",
		ServerAddress:   fmt.Sprintf("127.0.0.1:%d", masterPortNumber),
		MinimumInterval: time.Millisecond,
	},
		"127.0.0.1")
	time.Sleep(time.Millisecond * 5)
	if slaveListener.connectionMap[loopbackIP] > 0 {
		t.Fatalf(
			"slave listener has %d loopback connections, expected none",
			slaveListener.connectionMap[loopbackIP])
	}
	if dialer.connectionMap[slaveAddress] == nil {
		t.Fatalf("master dialer does not have a connection yet")
	}
	time.Sleep(time.Millisecond * 5)
	t.Log("making and testing connection")
	masterConn, err := dialer.Dial("tcp", slaveAddress)
	if err != nil {
		t.Fatal(err)
	}
	if dialer.connectionMap[slaveAddress] != nil {
		t.Fatal("master dialer still has a connection")
	}
	slaveConn, err := slaveListener.Accept()
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := slaveConn.(*listenerConn); !ok {
		t.Fatalf("not a *listenerConn connection: %T", slaveConn)
	}
	if slaveListener.connectionMap[loopbackIP] != 0 {
		t.Fatalf("slave listener has %d connections, expected 0",
			slaveListener.connectionMap[loopbackIP])
	}
	if dialer.connectionMap[slaveAddress] != nil {
		t.Fatal("master dialer still has a connection")
	}
	go func() {
		if _, err := io.Copy(slaveConn, slaveConn); err != nil {
			t.Log(err)
		}
	}()
	if err := testEcho(masterConn); err != nil {
		t.Fatal(err)
	}
}

func TestReverseListenHttp(t *testing.T) {
	tLogger := testlogger.New(t)
	// Set up slave.
	slaveLogger := prefixlogger.New("slave: ", tLogger)
	slaveListener, slavePortNumber := createTestListener(slaveLogger)
	slaveMux := http.NewServeMux()
	server := &serverType{slaveLogger}
	slaveMux.HandleFunc(testUrlPath, server.connectHandler)
	go http.Serve(slaveListener, slaveMux)
	// Set up master
	masterLogger := prefixlogger.New("master: ", tLogger)
	masterListener, masterPortNumber := createTestRealListener(masterLogger)
	masterMux := http.NewServeMux()
	go http.Serve(masterListener, masterMux)
	dialer := NewDialer(nil, masterMux, 0, 0, masterLogger)
	// Make slave connect back to master.
	slaveLogger.Print("making slave connect to master")
	go slaveListener.connectLoop(ReverseListenerConfig{
		Network:         "tcp",
		ServerAddress:   fmt.Sprintf("127.0.0.1:%d", masterPortNumber),
		MinimumInterval: time.Millisecond,
	},
		"127.0.0.1")
	time.Sleep(time.Millisecond * 5)
	masterLogger.Print("making and testing connection")
	err := makeAndTestHttpConnection(dialer, slavePortNumber, masterLogger)
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>Increase delays in lib/net/reverseconnection/listen_test.go for TravisCI.<commit_after>package reverseconnection

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/Symantec/Dominator/lib/log"
	"github.com/Symantec/Dominator/lib/log/prefixlogger"
	"github.com/Symantec/Dominator/lib/log/testlogger"
	libnet "github.com/Symantec/Dominator/lib/net"
)

const (
	testConnectString = "200 Connected to TestReverseDialer"
	testUrlPath       = "/_ReverseDialer_TEST_/connect"
)

var dialer *Dialer

type testAcceptEvent struct {
	conn  net.Conn
	error error
}

type serverType struct {
	logger log.DebugLogger
}

func createTestListener(logger log.DebugLogger) (*Listener, uint) {
	portNumber := 30000 + uint(rand.Intn(10000))
	listener, err := Listen("tcp", portNumber, logger)
	if err != nil {
		logger.Fatal(err)
	}
	return listener, portNumber
}

func createTestRealListener(logger log.DebugLogger) (net.Listener, uint) {
	portNumber := 30000 + uint(rand.Intn(10000))
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", portNumber))
	if err != nil {
		logger.Fatal(err)
	}
	return listener, portNumber
}

func createAcceptChannel(listener *Listener) <-chan testAcceptEvent {
	acceptChannel := make(chan testAcceptEvent, 1)
	go func() {
		conn, err := listener.Accept()
		acceptChannel <- testAcceptEvent{conn, err}
	}()
	return acceptChannel
}

func makeAndTestHttpConnection(dialer libnet.Dialer, portNumber uint,
	logger log.DebugLogger) error {
	conn, err := dialer.Dial("tcp", fmt.Sprintf("localhost:%d", portNumber))
	if err != nil {
		return errors.New("error dialing: " + err.Error())
	}
	return testHttpConnection(conn, logger)
}

func testEcho(conn net.Conn) error {
	testString := "reflect"
	writeBuffer := []byte(testString)
	if _, err := conn.Write(writeBuffer); err != nil {
		return errors.New("error writing test string: " + err.Error())
	}
	readBuffer := make([]byte, 256)
	if nRead, err := conn.Read(readBuffer); err != nil {
		return errors.New("error reading test string response: " + err.Error())
	} else if nRead != len(writeBuffer) {
		return fmt.Errorf("Wrote: %d bytes, read: %d bytes",
			len(writeBuffer), nRead)
	} else if rString := string(readBuffer[0:nRead]); rString != testString {
		return fmt.Errorf("Wrote: \"%s\", read: \"%s\"", testString, rString)
	}
	return conn.Close()
}

func testHttpConnection(conn net.Conn, logger log.DebugLogger) error {
	io.WriteString(conn, "CONNECT "+testUrlPath+" HTTP/1.0\n\n")
	resp, err := http.ReadResponse(bufio.NewReader(conn),
		&http.Request{Method: "CONNECT"})
	if err != nil {
		return errors.New("error connecting: " + err.Error())
	}
	if resp.StatusCode != http.StatusOK || resp.Status != testConnectString {
		return errors.New("unexpected HTTP response: " + resp.Status)
	}
	return testEcho(conn)
}

func (s *serverType) connectHandler(w http.ResponseWriter, req *http.Request) {
	if req.Method != "CONNECT" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	hijacker, ok := w.(http.Hijacker)
	if !ok {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		s.logger.Println("not a hijacker ", req.RemoteAddr)
		return
	}
	conn, _, err := hijacker.Hijack()
	if err != nil {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusInternalServerError)
		s.logger.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
		return
	}
	_, err = io.WriteString(conn, "HTTP/1.0 "+testConnectString+"\n\n")
	if err != nil {
		s.logger.Println("error writing connect message: ", err.Error())
		return
	}
	if _, err := io.Copy(conn, conn); err != nil {
		s.logger.Println(err)
	}
}

func TestInjectAccept(t *testing.T) {
	logger := testlogger.New(t)
	acceptChannel := make(chan acceptEvent, 1)
	fakeListener := &Listener{
		logger:        logger,
		acceptChannel: acceptChannel,
	}
	server := &serverType{logger}
	http.HandleFunc(testUrlPath, server.connectHandler)
	go http.Serve(fakeListener, nil)
	realListener, portNumber := createTestRealListener(logger)
	sc, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", portNumber))
	if err != nil {
		t.Fatal(err)
	}
	slaveConn := sc.(libnet.TCPConn)
	masterConn, err := realListener.Accept()
	if err != nil {
		t.Fatal(err)
	}
	fakeListener.acceptChannel <- acceptEvent{
		&listenerConn{TCPConn: slaveConn}, nil}
	if err := testHttpConnection(masterConn, logger); err != nil {
		t.Fatal(err)
	}
}

func TestListen(t *testing.T) {
	logger := testlogger.New(t)
	listener, portNumber := createTestListener(logger)
	acceptChannel := createAcceptChannel(listener)
	dialConn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", portNumber))
	if err != nil {
		t.Fatal(err)
	}
	response := <-acceptChannel
	if response.error != nil {
		t.Fatal(response.error)
	}
	testString := "hello"
	writeBuffer := []byte(testString)
	if _, err := dialConn.Write(writeBuffer); err != nil {
		t.Fatal(err)
	}
	readBuffer := make([]byte, 256)
	if nRead, err := response.conn.Read(readBuffer); err != nil {
		t.Fatal(err)
	} else if nRead != len(writeBuffer) {
		t.Fatalf("Wrote: %d bytes, read: %d bytes", len(writeBuffer), nRead)
	} else if rString := string(readBuffer[0:nRead]); rString != testString {
		t.Fatalf("Wrote: \"%s\", read: \"%s\"", testString, rString)
	}
	if err := dialConn.Close(); err != nil {
		t.Fatal(err)
	}
	if err := response.conn.Close(); err != nil {
		t.Fatal(err)
	}
	if err := listener.Close(); err != nil {
		t.Fatal(err)
	}
}

func TestListenAndHttpServe(t *testing.T) {
	logger := testlogger.New(t)
	listener, portNumber := createTestListener(logger)
	serverMux := http.NewServeMux()
	server := &serverType{logger}
	serverMux.HandleFunc(testUrlPath, server.connectHandler)
	go http.Serve(listener, serverMux)
	err := makeAndTestHttpConnection(&net.Dialer{}, portNumber, logger)
	if err != nil {
		t.Fatal(err)
	}
}

func TestReverseListenTcp(t *testing.T) {
	tLogger := testlogger.New(t)
	// Set up slave.
	slaveListener, slavePortNumber := createTestListener(
		prefixlogger.New("slave: ", tLogger))
	slaveAddress := fmt.Sprintf("127.0.0.1:%d", slavePortNumber)
	t.Logf("slaveAddress: %s", slaveAddress)
	// Set up master
	masterLogger := prefixlogger.New("master: ", tLogger)
	masterListener, masterPortNumber := createTestRealListener(masterLogger)
	masterMux := http.NewServeMux()
	go http.Serve(masterListener, masterMux)
	dialer := NewDialer(nil, masterMux, 0, 0, masterLogger)
	// Make slave connect back to master.
	loopbackIP := [4]byte{127, 0, 0, 1}
	if slaveListener.connectionMap[loopbackIP] > 0 {
		t.Fatalf("slave listener already has %d connections",
			slaveListener.connectionMap[loopbackIP])
	}
	if dialer.connectionMap[slaveAddress] != nil {
		t.Fatal("master dialer already has a connection")
	}
	t.Log("making slave connect to master")
	go slaveListener.connectLoop(ReverseListenerConfig{
		Network:         "tcp",
		ServerAddress:   fmt.Sprintf("127.0.0.1:%d", masterPortNumber),
		MinimumInterval: time.Millisecond,
	},
		"127.0.0.1")
	time.Sleep(time.Millisecond * 50)
	if slaveListener.connectionMap[loopbackIP] > 0 {
		t.Fatalf(
			"slave listener has %d loopback connections, expected none",
			slaveListener.connectionMap[loopbackIP])
	}
	if dialer.connectionMap[slaveAddress] == nil {
		t.Fatalf("master dialer does not have a connection yet")
	}
	time.Sleep(time.Millisecond * 50)
	t.Log("making and testing connection")
	masterConn, err := dialer.Dial("tcp", slaveAddress)
	if err != nil {
		t.Fatal(err)
	}
	if dialer.connectionMap[slaveAddress] != nil {
		t.Fatal("master dialer still has a connection")
	}
	slaveConn, err := slaveListener.Accept()
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := slaveConn.(*listenerConn); !ok {
		t.Fatalf("not a *listenerConn connection: %T", slaveConn)
	}
	if slaveListener.connectionMap[loopbackIP] != 0 {
		t.Fatalf("slave listener has %d connections, expected 0",
			slaveListener.connectionMap[loopbackIP])
	}
	if dialer.connectionMap[slaveAddress] != nil {
		t.Fatal("master dialer still has a connection")
	}
	go func() {
		if _, err := io.Copy(slaveConn, slaveConn); err != nil {
			t.Log(err)
		}
	}()
	if err := testEcho(masterConn); err != nil {
		t.Fatal(err)
	}
}

func TestReverseListenHttp(t *testing.T) {
	tLogger := testlogger.New(t)
	// Set up slave.
	slaveLogger := prefixlogger.New("slave: ", tLogger)
	slaveListener, slavePortNumber := createTestListener(slaveLogger)
	slaveMux := http.NewServeMux()
	server := &serverType{slaveLogger}
	slaveMux.HandleFunc(testUrlPath, server.connectHandler)
	go http.Serve(slaveListener, slaveMux)
	// Set up master
	masterLogger := prefixlogger.New("master: ", tLogger)
	masterListener, masterPortNumber := createTestRealListener(masterLogger)
	masterMux := http.NewServeMux()
	go http.Serve(masterListener, masterMux)
	dialer := NewDialer(nil, masterMux, 0, 0, masterLogger)
	// Make slave connect back to master.
	slaveLogger.Print("making slave connect to master")
	go slaveListener.connectLoop(ReverseListenerConfig{
		Network:         "tcp",
		ServerAddress:   fmt.Sprintf("127.0.0.1:%d", masterPortNumber),
		MinimumInterval: time.Millisecond,
	},
		"127.0.0.1")
	time.Sleep(time.Millisecond * 50)
	masterLogger.Print("making and testing connection")
	err := makeAndTestHttpConnection(dialer, slavePortNumber, masterLogger)
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Package errcheck is the library used to implement the errcheck command-line tool.
//
// Note: The API of this package has not been finalized and may change at any point.
package errcheck

import (
	"bytes"
	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"regexp"
)

var (
	// allImports is a map of already-imported import paths to packages
	allImports map[string]*types.Package

	// ErrNoGoFiles is returned when CheckPackage is run on a package with no Go source files
	ErrNoGoFiles = errors.New("package contains no go source files")
)

// UncheckedErrors is returned from the CheckPackage function if the package contains
// any unchecked errors.
type UncheckedErrors struct {
	// Errors is a list of all the unchecked errors in the package.
	// Printing an error reports its position within the file and the contents of the line.
	Errors []error
}

func (e UncheckedErrors) Error() string {
	return fmt.Sprint(len(e.Errors), "unchecked errors")
}

func CheckPackage(pkgPath string, ignore map[string]*regexp.Regexp, blank bool) error {
	pkg, err := findPackage(pkgPath)
	if err != nil {
		return err
	}
	files := getFiles(pkg)

	if len(files) == 0 {
		return ErrNoGoFiles
	}
	return checkFiles(files, ignore, blank)
}

type file struct {
	fset  *token.FileSet
	name  string
	ast   *ast.File
	lines [][]byte
}

func parseFile(fset *token.FileSet, fileName string) (f file, err error) {
	rd, err := os.Open(fileName)
	if err != nil {
		return f, err
	}
	defer rd.Close()

	data, err := ioutil.ReadAll(rd)
	if err != nil {
		return f, err
	}

	astFile, err := parser.ParseFile(fset, fileName, bytes.NewReader(data), parser.ParseComments)
	if err != nil {
		return f, fmt.Errorf("could not parse: %s", err)
	}

	lines := bytes.Split(data, []byte("\n"))
	f = file{fset: fset, name: fileName, ast: astFile, lines: lines}
	return f, nil
}

func typeCheck(fset *token.FileSet, astFiles []*ast.File) (map[*ast.CallExpr]types.Type, map[*ast.Ident]types.Object, error) {
	callTypes := make(map[*ast.CallExpr]types.Type)
	identObjs := make(map[*ast.Ident]types.Object)

	exprFn := func(x ast.Expr, typ types.Type, val exact.Value) {
		call, ok := x.(*ast.CallExpr)
		if !ok {
			return
		}
		callTypes[call] = typ
	}
	identFn := func(id *ast.Ident, obj types.Object) {
		identObjs[id] = obj
	}
	context := types.Context{
		Expr:   exprFn,
		Ident:  identFn,
		Import: importer,
	}
	_, err := context.Check(astFiles[0].Name.Name, fset, astFiles...)
	return callTypes, identObjs, err
}

type checker struct {
	fset      *token.FileSet
	files     map[string]file
	callTypes map[*ast.CallExpr]types.Type
	identObjs map[*ast.Ident]types.Object
	ignore    map[string]*regexp.Regexp
	blank     bool

	errors []error
}

type uncheckedError struct {
	pos  token.Position
	line []byte
}

func (e uncheckedError) Error() string {
	return fmt.Sprintf("%s\t%s", e.pos, e.line)
}

func (c *checker) ignoreCall(call *ast.CallExpr) bool {
	// Try to get an identifier.
	// Currently only supports simple expressions:
	//     1. f()
	//     2. x.y.f()
	var id *ast.Ident
	switch exp := call.Fun.(type) {
	case (*ast.Ident):
		id = exp
	case (*ast.SelectorExpr):
		id = exp.Sel
	default:
		// eg: *ast.SliceExpr, *ast.IndexExpr
	}

	if id == nil {
		return false
	}

	// If we got an identifier for the function, see if it is ignored

	if re, ok := c.ignore[""]; ok && re.MatchString(id.Name) {
		return true
	}

	if obj := c.identObjs[id]; obj != nil {
		if pkg := obj.Pkg(); pkg != nil {
			if re, ok := c.ignore[pkg.Path()]; ok {
				return re.MatchString(id.Name)
			}
		}
	}

	return false
}

// errorsByArg returns a slice s such that
// len(s) == number of return types of call
// s[i] == true iff return type at position i from left is an error type
func (c *checker) errorsByArg(call *ast.CallExpr) []bool {
	switch t := c.callTypes[call].(type) {
	case *types.Named:
		// Single return
		return []bool{isErrorType(t.Obj())}
	case *types.Tuple:
		// Multiple returns
		s := make([]bool, t.Len())
		for i := 0; i < t.Len(); i++ {
			nt, ok := t.At(i).Type().(*types.Named)
			s[i] = ok && isErrorType(nt.Obj())
		}
		return s
	}
	return nil
}

func (c *checker) callReturnsError(call *ast.CallExpr) bool {
	for _, isError := range c.errorsByArg(call) {
		if isError {
			return true
		}
	}
	return false
}

func (c *checker) addErrorAtPosition(position token.Pos) {
	pos := c.fset.Position(position)
	line := bytes.TrimSpace(c.files[pos.Filename].lines[pos.Line-1])
	c.errors = append(c.errors, uncheckedError{pos, line})
}

func (c *checker) Visit(node ast.Node) ast.Visitor {
	switch stmt := node.(type) {
	case *ast.ExprStmt:
		if call, ok := stmt.X.(*ast.CallExpr); ok {
			if !c.ignoreCall(call) && c.callReturnsError(call) {
				c.addErrorAtPosition(call.Lparen)
			}
		}
	case *ast.AssignStmt:
		if !c.blank {
			break
		}
		if len(stmt.Rhs) == 1 {
			// single value on rhs; check against lhs identifiers
			if call, ok := stmt.Rhs[0].(*ast.CallExpr); ok {
				if c.ignoreCall(call) {
					break
				}
				isError := c.errorsByArg(call)
				for i := 0; i < len(stmt.Lhs); i++ {
					if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
						if id.Name == "_" && isError[i] {
							c.addErrorAtPosition(id.NamePos)
						}
					}
				}
			}
		} else {
			// multiple value on rhs; in this case a call can't return
			// multiple values. Assume len(stmt.Lhs) == len(stmt.Rhs)
			for i := 0; i < len(stmt.Lhs); i++ {
				if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
					if call, ok := stmt.Rhs[i].(*ast.CallExpr); ok {
						if c.ignoreCall(call) {
							continue
						}
						if id.Name == "_" && c.callReturnsError(call) {
							c.addErrorAtPosition(id.NamePos)
						}
					}
				}
			}
		}
	default:
	}
	return c
}

func checkFiles(fileNames []string, ignore map[string]*regexp.Regexp, blank bool) error {
	fset := token.NewFileSet()
	astFiles := make([]*ast.File, len(fileNames))
	files := make(map[string]file, len(fileNames))

	for i, fileName := range fileNames {
		f, err := parseFile(fset, fileName)
		if err != nil {
			return fmt.Errorf("could not parse %s: %s", fileName, err)
		}
		files[fileName] = f
		astFiles[i] = f.ast
	}

	callTypes, identObjs, err := typeCheck(fset, astFiles)
	if err != nil {
		return fmt.Errorf("could not type check: %s", err)
	}

	visitor := &checker{fset, files, callTypes, identObjs, ignore, blank, []error{}}
	for _, astFile := range astFiles {
		ast.Walk(visitor, astFile)
	}

	if len(visitor.errors) > 0 {
		return UncheckedErrors{visitor.errors}
	}
	return nil
}

type obj interface {
	Pkg() *types.Package
	Name() string
}

func isErrorType(v obj) bool {
	return v.Pkg() == nil && v.Name() == "error"
}

func importer(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
	// types.Importer does not seem to be designed for recursive
	// parsing like we're doing here. Specifically, each nested import
	// will maintain its own imports map. This will lead to duplicate
	// imports and in turn packages, which will lead to funny errors
	// such as "cannot pass argument ip (variable of type net.IP) to
	// variable of type net.IP"
	//
	// To work around this, we keep a global imports map, allImports,
	// to which we add all nested imports, and which we use as the
	// cache, instead of imports.
	//
	// Since all nested imports will also use this importer, there
	// should be no way to end up with duplicate imports.

	// We first try to use GcImport directly. This has the downside of
	// using possibly out-of-date packages, but it has the upside of
	// not having to parse most of the Go standard library.

	buildPkg, buildErr := build.Import(path, ".", 0)

	// If we found no build dir, assume we're dealing with installed
	// but no source. If we found a build dir, only use GcImport if
	// it's in GOROOT. This way we always use up-to-date code for
	// normal packages but avoid parsing the standard library.
	if (buildErr == nil && buildPkg.Goroot) || buildErr != nil {
		pkg, err = types.GcImport(allImports, path)
		if err == nil {
			// err should be != nil if the package is not complete, but this is an
			// additional check just in case
			if !pkg.Complete() {
				return pkg, fmt.Errorf("types.GcImport returned incomplete package: %s", path)
			}
			// We don't use imports, but per API we have to add the package.
			imports[pkg.Path()] = pkg
			allImports[pkg.Path()] = pkg
			return pkg, nil
		}
	}

	// See if we already imported this package
	if pkg = allImports[path]; pkg != nil {
		return pkg, nil
	}

	// allImports failed, try to use go/build
	if buildErr != nil {
		return nil, fmt.Errorf("build.Import failed: %s", buildErr)
	}

	fileSet := token.NewFileSet()

	isGoFile := func(d os.FileInfo) bool {
		allFiles := make([]string, 0, len(buildPkg.GoFiles)+len(buildPkg.CgoFiles))
		allFiles = append(allFiles, buildPkg.GoFiles...)
		allFiles = append(allFiles, buildPkg.CgoFiles...)

		for _, file := range allFiles {
			if file == d.Name() {
				return true
			}
		}
		return false
	}
	pkgs, err := parser.ParseDir(fileSet, buildPkg.Dir, isGoFile, 0)
	if err != nil {
		return nil, err
	}

	delete(pkgs, "documentation")
	var astPkg *ast.Package
	var name string
	for name, astPkg = range pkgs {
		// Use the first non-main package, or the only package we
		// found.
		//
		// NOTE(dh) I can't think of a reason why there should be
		// multiple packages in a single directory, but ParseDir
		// accommodates for that possibility.
		if len(pkgs) == 1 || name != "main" {
			break
		}
	}

	if astPkg == nil {
		return nil, fmt.Errorf("can't find import: %s", name)
	}

	var ff []*ast.File
	for _, f := range astPkg.Files {
		ff = append(ff, f)
	}

	context := types.Context{
		Import: importer,
	}

	pkg, err = context.Check(name, fileSet, ff...)
	if err != nil {
		return pkg, fmt.Errorf("types.Context.Check failed: %s", err)
	}

	// Succesfuly imported, mark the package as complete
	pkg = types.NewPackage(pkg.Pos(), pkg.Path(), pkg.Name(), pkg.Scope(), pkg.Imports(), true)

	// We don't use imports, but per API we have to add the package.
	imports[path] = pkg
	allImports[path] = pkg
	return pkg, nil
}

func init() {
	allImports = make(map[string]*types.Package)
}
<commit_msg>move importer to own package<commit_after>// Package errcheck is the library used to implement the errcheck command-line tool.
//
// Note: The API of this package has not been finalized and may change at any point.
package errcheck

import (
	"bytes"
	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
	"errors"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"honnef.co/go/importer"
	"io/ioutil"
	"os"
	"regexp"
)

var (
	// ErrNoGoFiles is returned when CheckPackage is run on a package with no Go source files
	ErrNoGoFiles = errors.New("package contains no go source files")
)

// UncheckedErrors is returned from the CheckPackage function if the package contains
// any unchecked errors.
type UncheckedErrors struct {
	// Errors is a list of all the unchecked errors in the package.
	// Printing an error reports its position within the file and the contents of the line.
	Errors []error
}

func (e UncheckedErrors) Error() string {
	return fmt.Sprint(len(e.Errors), "unchecked errors")
}

func CheckPackage(pkgPath string, ignore map[string]*regexp.Regexp, blank bool) error {
	pkg, err := findPackage(pkgPath)
	if err != nil {
		return err
	}
	files := getFiles(pkg)

	if len(files) == 0 {
		return ErrNoGoFiles
	}
	return checkFiles(files, ignore, blank)
}

type file struct {
	fset  *token.FileSet
	name  string
	ast   *ast.File
	lines [][]byte
}

func parseFile(fset *token.FileSet, fileName string) (f file, err error) {
	rd, err := os.Open(fileName)
	if err != nil {
		return f, err
	}
	defer rd.Close()

	data, err := ioutil.ReadAll(rd)
	if err != nil {
		return f, err
	}

	astFile, err := parser.ParseFile(fset, fileName, bytes.NewReader(data), parser.ParseComments)
	if err != nil {
		return f, fmt.Errorf("could not parse: %s", err)
	}

	lines := bytes.Split(data, []byte("\n"))
	f = file{fset: fset, name: fileName, ast: astFile, lines: lines}
	return f, nil
}

func typeCheck(fset *token.FileSet, astFiles []*ast.File) (map[*ast.CallExpr]types.Type, map[*ast.Ident]types.Object, error) {
	callTypes := make(map[*ast.CallExpr]types.Type)
	identObjs := make(map[*ast.Ident]types.Object)

	exprFn := func(x ast.Expr, typ types.Type, val exact.Value) {
		call, ok := x.(*ast.CallExpr)
		if !ok {
			return
		}
		callTypes[call] = typ
	}
	identFn := func(id *ast.Ident, obj types.Object) {
		identObjs[id] = obj
	}
	context := types.Context{
		Expr:   exprFn,
		Ident:  identFn,
		Import: importer.NewImporter().Import,
	}

	_, err := context.Check(astFiles[0].Name.Name, fset, astFiles...)
	return callTypes, identObjs, err
}

type checker struct {
	fset      *token.FileSet
	files     map[string]file
	callTypes map[*ast.CallExpr]types.Type
	identObjs map[*ast.Ident]types.Object
	ignore    map[string]*regexp.Regexp
	blank     bool

	errors []error
}

type uncheckedError struct {
	pos  token.Position
	line []byte
}

func (e uncheckedError) Error() string {
	return fmt.Sprintf("%s\t%s", e.pos, e.line)
}

func (c *checker) ignoreCall(call *ast.CallExpr) bool {
	// Try to get an identifier.
	// Currently only supports simple expressions:
	//     1. f()
	//     2. x.y.f()
	var id *ast.Ident
	switch exp := call.Fun.(type) {
	case (*ast.Ident):
		id = exp
	case (*ast.SelectorExpr):
		id = exp.Sel
	default:
		// eg: *ast.SliceExpr, *ast.IndexExpr
	}

	if id == nil {
		return false
	}

	// If we got an identifier for the function, see if it is ignored

	if re, ok := c.ignore[""]; ok && re.MatchString(id.Name) {
		return true
	}

	if obj := c.identObjs[id]; obj != nil {
		if pkg := obj.Pkg(); pkg != nil {
			if re, ok := c.ignore[pkg.Path()]; ok {
				return re.MatchString(id.Name)
			}
		}
	}

	return false
}

// errorsByArg returns a slice s such that
// len(s) == number of return types of call
// s[i] == true iff return type at position i from left is an error type
func (c *checker) errorsByArg(call *ast.CallExpr) []bool {
	switch t := c.callTypes[call].(type) {
	case *types.Named:
		// Single return
		return []bool{isErrorType(t.Obj())}
	case *types.Tuple:
		// Multiple returns
		s := make([]bool, t.Len())
		for i := 0; i < t.Len(); i++ {
			nt, ok := t.At(i).Type().(*types.Named)
			s[i] = ok && isErrorType(nt.Obj())
		}
		return s
	}
	return nil
}

func (c *checker) callReturnsError(call *ast.CallExpr) bool {
	for _, isError := range c.errorsByArg(call) {
		if isError {
			return true
		}
	}
	return false
}

func (c *checker) addErrorAtPosition(position token.Pos) {
	pos := c.fset.Position(position)
	line := bytes.TrimSpace(c.files[pos.Filename].lines[pos.Line-1])
	c.errors = append(c.errors, uncheckedError{pos, line})
}

func (c *checker) Visit(node ast.Node) ast.Visitor {
	switch stmt := node.(type) {
	case *ast.ExprStmt:
		if call, ok := stmt.X.(*ast.CallExpr); ok {
			if !c.ignoreCall(call) && c.callReturnsError(call) {
				c.addErrorAtPosition(call.Lparen)
			}
		}
	case *ast.AssignStmt:
		if !c.blank {
			break
		}
		if len(stmt.Rhs) == 1 {
			// single value on rhs; check against lhs identifiers
			if call, ok := stmt.Rhs[0].(*ast.CallExpr); ok {
				if c.ignoreCall(call) {
					break
				}
				isError := c.errorsByArg(call)
				for i := 0; i < len(stmt.Lhs); i++ {
					if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
						if id.Name == "_" && isError[i] {
							c.addErrorAtPosition(id.NamePos)
						}
					}
				}
			}
		} else {
			// multiple value on rhs; in this case a call can't return
			// multiple values. Assume len(stmt.Lhs) == len(stmt.Rhs)
			for i := 0; i < len(stmt.Lhs); i++ {
				if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
					if call, ok := stmt.Rhs[i].(*ast.CallExpr); ok {
						if c.ignoreCall(call) {
							continue
						}
						if id.Name == "_" && c.callReturnsError(call) {
							c.addErrorAtPosition(id.NamePos)
						}
					}
				}
			}
		}
	default:
	}
	return c
}

func checkFiles(fileNames []string, ignore map[string]*regexp.Regexp, blank bool) error {
	fset := token.NewFileSet()
	astFiles := make([]*ast.File, len(fileNames))
	files := make(map[string]file, len(fileNames))

	for i, fileName := range fileNames {
		f, err := parseFile(fset, fileName)
		if err != nil {
			return fmt.Errorf("could not parse %s: %s", fileName, err)
		}
		files[fileName] = f
		astFiles[i] = f.ast
	}

	callTypes, identObjs, err := typeCheck(fset, astFiles)
	if err != nil {
		return fmt.Errorf("could not type check: %s", err)
	}

	visitor := &checker{fset, files, callTypes, identObjs, ignore, blank, []error{}}
	for _, astFile := range astFiles {
		ast.Walk(visitor, astFile)
	}

	if len(visitor.errors) > 0 {
		return UncheckedErrors{visitor.errors}
	}
	return nil
}

type obj interface {
	Pkg() *types.Package
	Name() string
}

func isErrorType(v obj) bool {
	return v.Pkg() == nil && v.Name() == "error"
}
<|endoftext|>