<commit_before>package gene

// Doc generation
// http://beego.me/docs/advantage/docs.md
// https://github.com/yvasiyarov/swagger

// Code generation
// http://clipperhouse.github.io/gen/#List

// Configuration change management

// Host Pooling
// https://github.com/bitly/go-hostpool
<commit_msg>Ideas: added new ideas<commit_after>package gene

// Doc generation
// http://beego.me/docs/advantage/docs.md
// https://github.com/yvasiyarov/swagger

// Code generation
// http://clipperhouse.github.io/gen/#List

// Configuration change management

// Host Pooling
// https://github.com/bitly/go-hostpool
// https://github.com/strava/go.serversets

// Throttling
// https://github.com/PuerkitoBio/throttled
<|endoftext|>


<commit_before>package mdocker

import (
	"archive/tar"
	"bufio"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/mistifyio/mistify-agent/rpc"
)

// ListImages retrieves a list of Docker images
func (md *MDocker) ListImages(h *http.Request, request *rpc.ImageRequest, response *rpc.ImageResponse) error {
	opts := docker.ListImagesOptions{}

	apiImages, err := md.client.ListImages(opts)
	if err != nil {
		return err
	}
	images := make([]*rpc.Image, 0, len(apiImages))
	for _, ai := range apiImages {
		id, _ := docker.ParseRepositoryTag(ai.RepoTags[0])
		images = append(images, &rpc.Image{
			Id:   id,
			Type: "container",
			Size: uint64(ai.Size) / 1024 / 1024,
		})
	}

	response.Images = images
	return nil
}

// GetImage retrieves information about a specific Docker image
func (md *MDocker) GetImage(h *http.Request, request *rpc.ImageRequest, response *rpc.ImageResponse) error {
	image, err := md.client.InspectImage(request.Id)
	if err != nil {
		return err
	}

	response.Images = []*rpc.Image{
		&rpc.Image{
			Id:   request.Id,
			Type: "container",
			Size: uint64(image.Size) / 1024 / 1024,
		},
	}
	return nil
}

// LoadImage downloads a new container image from the image service and
// imports it into Docker
func (md *MDocker) LoadImage(h *http.Request, request *rpc.ImageRequest, response *rpc.ImageResponse) error {
	name := request.Id

	// Check if we already have the image to avoid unnecessary pulling
	image, err := md.client.InspectImage(name)
	if err != nil && err != docker.ErrNoSuchImage {
		return err
	}

	if image == nil {
		// Docker Import lets the image get renamed, but it strips metadata
		// (which includes any CMD that had been set). Docker Load doesn't let
		// the image get renamed and doesn't return the image id or name after
		// loading, but does preserve metadata. Since a rename to use the
		// image-service assigned id and metadata with CMD are both necessary,
		// the only way forward is to update the repositories file inside and
		// then load it.
		source := fmt.Sprintf("http://%s/images/%s/download", md.imageService, request.Id)
		resp, err := http.Get(source)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			return ErrorHTTPCode{
				Expected: http.StatusOK,
				Code:     resp.StatusCode,
				Source:   source,
			}
		}

		// Use a response buffer so the first few bytes can be peeked at for
		// file type detection. Uncompress the image if it is gzipped
		responseBuffer := bufio.NewReader(resp.Body)
		var imageReader io.Reader = responseBuffer
		filetypeBytes, err := responseBuffer.Peek(512)
		if err != nil {
			return err
		}
		if http.DetectContentType(filetypeBytes) == "application/x-gzip" {
			gzipReader, err := gzip.NewReader(responseBuffer)
			if err != nil {
				return err
			}
			defer gzipReader.Close()
			imageReader = gzipReader
		}

		pipeReader, pipeWriter := io.Pipe()

		go fixRepositoriesFile(name, imageReader, pipeWriter)

		opts := docker.LoadImageOptions{
			InputStream: pipeReader,
		}
		if err := md.client.LoadImage(opts); err != nil {
			return err
		}

		image, err = md.client.InspectImage(name)
		if err != nil {
			return err
		}
	}

	response.Images = []*rpc.Image{
		&rpc.Image{
			Id:   request.Id,
			Type: "container",
			Size: uint64(image.Size) / 1024 / 1024,
		},
	}
	return nil
}

func fixRepositoriesFile(newName string, in io.Reader, out io.WriteCloser) {
	defer out.Close()
	tarReader := tar.NewReader(in)
	tarWriter := tar.NewWriter(out)
	defer tarWriter.Close()

	for {
		header, err := tarReader.Next()
		if err != nil {
			if err == io.EOF {
				return
			}
			log.WithField("error", err).Error("failed to get next tar header")
			return
		}

		switch header.Typeflag {
		case tar.TypeReg:
			// Update the image name in the repositories file
			if header.Name == "repositories" {
				// Read the file and parse the JSON
				inBytes := make([]byte, header.Size)
				if _, err := tarReader.Read(inBytes); err != nil {
					log.WithField("error", err).Error("failed to read repositories file")
					return
				}
				origRepo := map[string]interface{}{}
				if err := json.Unmarshal(inBytes, &origRepo); err != nil {
					log.WithField("error", err).Error("failed to parse repositories json")
					return
				}
				// Should only be one key. Replace it with the new repo name
				newRepo := map[string]interface{}{}
				for oldName := range origRepo {
					newRepo[newName] = origRepo[oldName]
					break
				}

				// Update the header
				outBytes, err := json.Marshal(newRepo)
				if err != nil {
					log.WithField("error", err).Error("failed to marshal repositories json")
					return
				}
				header.Size = int64(len(outBytes))
				header.ModTime = time.Now()

				// Write the new header and data
				if err := tarWriter.WriteHeader(header); err != nil {
					log.WithField("error", err).Error("failed to write repositories header")
					return
				}
				if _, err := tarWriter.Write(outBytes); err != nil {
					log.WithField("error", err).Error("failed to write repositories json")
					return
				}
			} else {
				// Direct copy
				if err := tarWriter.WriteHeader(header); err != nil {
					log.WithField("error", err).Error("failed to write tar header")
					return
				}
				if _, err := io.Copy(tarWriter, tarReader); err != nil {
					log.WithField("error", err).Error("failed to copy tar file")
					return
				}
			}
		default:
			// Direct copy
			if err := tarWriter.WriteHeader(header); err != nil {
				log.WithField("error", err).Error("failed to write tar header")
				return
			}
			if _, err := io.Copy(tarWriter, tarReader); err != nil {
				log.WithField("error", err).Error("failed to copy tar body")
				return
			}
		}
	}
}

// DeleteImage deletes a Docker image
func (md *MDocker) DeleteImage(h *http.Request, request *rpc.ImageRequest, response *rpc.ImageResponse) error {
	image, err := md.client.InspectImage(request.Id)
	if err != nil {
		return err
	}

	opts := docker.RemoveImageOptions{}
	if err := md.client.RemoveImageExtended(request.Id, opts); err != nil {
		return err
	}

	response.Images = []*rpc.Image{
		&rpc.Image{
			Id:   request.Id,
			Type: "container",
			Size: uint64(image.Size) / 1024 / 1024,
		},
	}
	return nil
}
<commit_msg>Avoid intermediate read step, use json.Decoder for repositories file<commit_after>package mdocker

import (
	"archive/tar"
	"bufio"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/mistifyio/mistify-agent/rpc"
)

// ListImages retrieves a list of Docker images
func (md *MDocker) ListImages(h *http.Request, request *rpc.ImageRequest, response *rpc.ImageResponse) error {
	opts := docker.ListImagesOptions{}

	apiImages, err := md.client.ListImages(opts)
	if err != nil {
		return err
	}
	images := make([]*rpc.Image, 0, len(apiImages))
	for _, ai := range apiImages {
		id, _ := docker.ParseRepositoryTag(ai.RepoTags[0])
		images = append(images, &rpc.Image{
			Id:   id,
			Type: "container",
			Size: uint64(ai.Size) / 1024 / 1024,
		})
	}

	response.Images = images
	return nil
}

// GetImage retrieves information about a specific Docker image
func (md *MDocker) GetImage(h *http.Request, request *rpc.ImageRequest, response *rpc.ImageResponse) error {
	image, err := md.client.InspectImage(request.Id)
	if err != nil {
		return err
	}

	response.Images = []*rpc.Image{
		&rpc.Image{
			Id:   request.Id,
			Type: "container",
			Size: uint64(image.Size) / 1024 / 1024,
		},
	}
	return nil
}

// LoadImage downloads a new container image from the image service and
// imports it into Docker
func (md *MDocker) LoadImage(h *http.Request, request *rpc.ImageRequest, response *rpc.ImageResponse) error {
	name := request.Id

	// Check if we already have the image to avoid unnecessary pulling
	image, err := md.client.InspectImage(name)
	if err != nil && err != docker.ErrNoSuchImage {
		return err
	}

	if image == nil {
		// Docker Import lets the image get renamed, but it strips metadata
		// (which includes any CMD that had been set). Docker Load doesn't let
		// the image get renamed and doesn't return the image id or name after
		// loading, but does preserve metadata. Since a rename to use the
		// image-service assigned id and metadata with CMD are both necessary,
		// the only way forward is to update the repositories file inside and
		// then load it.
		source := fmt.Sprintf("http://%s/images/%s/download", md.imageService, request.Id)
		resp, err := http.Get(source)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			return ErrorHTTPCode{
				Expected: http.StatusOK,
				Code:     resp.StatusCode,
				Source:   source,
			}
		}

		// Use a response buffer so the first few bytes can be peeked at for
		// file type detection. Uncompress the image if it is gzipped
		responseBuffer := bufio.NewReader(resp.Body)
		var imageReader io.Reader = responseBuffer
		filetypeBytes, err := responseBuffer.Peek(512)
		if err != nil {
			return err
		}
		if http.DetectContentType(filetypeBytes) == "application/x-gzip" {
			gzipReader, err := gzip.NewReader(responseBuffer)
			if err != nil {
				return err
			}
			defer gzipReader.Close()
			imageReader = gzipReader
		}

		pipeReader, pipeWriter := io.Pipe()

		go fixRepositoriesFile(name, imageReader, pipeWriter)

		opts := docker.LoadImageOptions{
			InputStream: pipeReader,
		}
		if err := md.client.LoadImage(opts); err != nil {
			return err
		}

		image, err = md.client.InspectImage(name)
		if err != nil {
			return err
		}
	}

	response.Images = []*rpc.Image{
		&rpc.Image{
			Id:   request.Id,
			Type: "container",
			Size: uint64(image.Size) / 1024 / 1024,
		},
	}
	return nil
}

func fixRepositoriesFile(newName string, in io.Reader, out io.WriteCloser) {
	defer out.Close()
	tarReader := tar.NewReader(in)
	tarWriter := tar.NewWriter(out)
	defer tarWriter.Close()

	for {
		header, err := tarReader.Next()
		if err != nil {
			if err == io.EOF {
				return
			}
			log.WithField("error", err).Error("failed to get next tar header")
			return
		}

		switch header.Typeflag {
		case tar.TypeReg:
			// Update the image name in the repositories file
			if header.Name == "repositories" {
				// Read the file and parse the JSON
				origRepo := map[string]interface{}{}
				jsonDecoder := json.NewDecoder(tarReader)
				if err := jsonDecoder.Decode(&origRepo); err != nil {
					log.WithField("error", err).Error("failed to parse repositories json")
					return
				}
				// Should only be one key. Replace it with the new repo name
				newRepo := map[string]interface{}{}
				for oldName := range origRepo {
					newRepo[newName] = origRepo[oldName]
					break
				}

				// Update the header
				outBytes, err := json.Marshal(newRepo)
				if err != nil {
					log.WithField("error", err).Error("failed to marshal repositories json")
					return
				}
				header.Size = int64(len(outBytes))
				header.ModTime = time.Now()

				// Write the new header and data
				if err := tarWriter.WriteHeader(header); err != nil {
					log.WithField("error", err).Error("failed to write repositories header")
					return
				}
				if _, err := tarWriter.Write(outBytes); err != nil {
					log.WithField("error", err).Error("failed to write repositories json")
					return
				}
			} else {
				// Direct copy
				if err := tarWriter.WriteHeader(header); err != nil {
					log.WithField("error", err).Error("failed to write tar header")
					return
				}
				if _, err := io.Copy(tarWriter, tarReader); err != nil {
					log.WithField("error", err).Error("failed to copy tar file")
					return
				}
			}
		default:
			// Direct copy
			if err := tarWriter.WriteHeader(header); err != nil {
				log.WithField("error", err).Error("failed to write tar header")
				return
			}
			if _, err := io.Copy(tarWriter, tarReader); err != nil {
				log.WithField("error", err).Error("failed to copy tar body")
				return
			}
		}
	}
}

// DeleteImage deletes a Docker image
func (md *MDocker) DeleteImage(h *http.Request, request *rpc.ImageRequest, response *rpc.ImageResponse) error {
	image, err := md.client.InspectImage(request.Id)
	if err != nil {
		return err
	}

	opts := docker.RemoveImageOptions{}
	if err := md.client.RemoveImageExtended(request.Id, opts); err != nil {
		return err
	}

	response.Images = []*rpc.Image{
		&rpc.Image{
			Id:   request.Id,
			Type: "container",
			Size: uint64(image.Size) / 1024 / 1024,
		},
	}
	return nil
}
<|endoftext|>


<commit_before>package images

import (
    "fmt"
    "errors"
    "strings"
    "os"
    "image"
    "image/jpeg"
    "image/png"
    "image/gif"
//    _ "image/draw"
    "golang.org/x/image/tiff"
    "golang.org/x/image/bmp"
)

type Image struct {
    Image *image.NRGBA
    Address string
    Format string
    Quality int
    width int
    height int
    Error error
}

const (
    FormatTIFF string = "tiff"
    FormatJPEG string = "jpeg"
    FormatGIF  string = "gif"
    FormatPNG  string = "png"
    FormatBMP  string = "bmp"
)

var Formats = [5]string{"tiff", "jpeg", "gif", "png", "bmp"}

func Open(address string) (i *Image, err error){
    i = new(Image)
    i.Address = address
    i.Quality = 100

    file, err := os.Open(address)
    if err != nil {
        i.Error = err
        return
    }
    defer file.Close()
    var img image.Image

    img, i.Format, err = image.Decode(file)
    if err != nil {
        i.Error = err
        return
    }
    i.Image = toNRGBA(img)

    i.width = i.Image.Bounds().Max.X
    i.height = i.Image.Bounds().Max.Y
    return
}

func (i *Image) Save(params ...string) (err error) {
    if i.Image == nil {
        if i.Error != nil {
            return i.Error
        }
        return errors.New("no image")
    }

    address := i.Address
    if len(params) > 0 && strings.Trim(params[0], " \n\t\r") != "" {
        address = params[0]
    }

    file, err := os.Create(address)
    defer file.Close()

    switch i.Format {
        case FormatJPEG:
            var rgba *image.RGBA
            if i.Image.Opaque() {
                rgba = &image.RGBA{
                    Pix:    i.Image.Pix,
                    Stride: i.Image.Stride,
                    Rect:   i.Image.Rect,
                }
            }

            if rgba != nil {
                err = jpeg.Encode(file, rgba, &jpeg.Options{Quality: i.Quality})
            } else {
                err = jpeg.Encode(file, i.Image, &jpeg.Options{Quality: i.Quality})
            }
        case FormatPNG:
            encoder := &png.Encoder{CompressionLevel: png.NoCompression}
            err = encoder.Encode(file, i.Image)
        case FormatGIF:
            err = gif.Encode(file, i.Image, &gif.Options{NumColors: 256})
        case FormatTIFF:
            err = tiff.Encode(file, i.Image, &tiff.Options{Compression: tiff.Uncompressed, Predictor: true})
        case FormatBMP:
            err = bmp.Encode(file, i.Image)
        default:
            err = errors.New("unsupported image format "+i.Format)
    }
    return
}

// Картинка больше прямоугольника
func (i *Image) ResizeOut(width, height int, params ...ResampleFilter) (image *Image){
    var w_ratio float64 = float64(width)  / i.Width64()
    var h_ratio float64 = float64(height) / i.Height64()

    if w_ratio == h_ratio {
    } else if w_ratio > h_ratio {
        height = int(i.Height64() * w_ratio)
    } else {
        width  = int(i.Width64() * h_ratio)
    }
    return i.Resize(width, height, params...)
}

// Картинка меньше прямоугольника
func (i *Image) ResizeIn(width, height int, params ...ResampleFilter) *Image{
    var w_ratio float64 = float64(width)  / i.Width64()
    var h_ratio float64 = float64(height) / i.Height64()

    if w_ratio == h_ratio {
    } else if w_ratio < h_ratio {
        height = int(i.Height64() * w_ratio)
    } else {
        width  = int(i.Width64() * h_ratio)
    }
    return i.Resize(width, height, params...)
}

func (i *Image) ResizeCrop(width, height int, params ...ResampleFilter) (img *Image) {
    img = i.ResizeOut(width, height, params...)
    defer func() {
        if r := recover(); r != nil {
            img.Error = errors.New(fmt.Sprintf("%v", r))
        }
    }()

    if img.Width() == width && img.Height() == height {
        return
    }

    Min := image.Point{}
    Min.X = int((img.Width() - width)/2)
    Min.Y = int((img.Height() - height)/2)

    Max := image.Point{}
    Max.X = Min.X + width
    Max.Y = Min.Y + height

    res := image.Rectangle{Min: Min, Max: Max}
    sub := img.Image.SubImage(res)
//    img.Image = Clone(sub)
    img.Image = toNRGBA(sub)

    img.width = width
    img.height= height

    return
}

func (i *Image) Resize(width, height int, params ...ResampleFilter) (image *Image) {
    image = new(Image)
    image.width = i.Width()
    image.height = i.Height()
    image.Quality = 100
    defer func() {
        if r := recover(); r != nil {
            image.Error = errors.New(fmt.Sprintf("%v", r))
        }
    }()

    filter := Lanczos
    if len(params) > 0 {
        filter = params[0]
    }

    if filter.Support <= 0.0 {
        image.Image, image.Error = i.resize(width, height)
    } else {
        if width != i.Width() {
            image.Image, image.Error = i.resizeW(width, filter)
            image.width = width
        }
        if image.Error != nil {
            return
        }
        if height != i.Height() {
            image.Image, image.Error = image.resizeH(height, filter)
            image.height = height
        }
    }
    image.Format = i.Format
    return
}

// In the future be replaced by toYCbCr
func toNRGBA(i image.Image) *image.NRGBA {
    srcBounds := i.Bounds()
    if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 {
        if src0, ok := i.(*image.NRGBA); ok {
            return src0
        }
    }
    return Clone(i)
}<commit_msg>Add Crop and package rename to images<commit_after>package images

import (
    "fmt"
    "errors"
    "strings"
    "os"
    "image"
    "image/jpeg"
    "image/png"
    "image/gif"
//    _ "image/draw"
    "golang.org/x/image/tiff"
    "golang.org/x/image/bmp"
)

type Image struct {
    Image *image.NRGBA
    Address string
    Format string
    Quality int
    width int
    height int
    Error error
}

const (
    FormatTIFF string = "tiff"
    FormatJPEG string = "jpeg"
    FormatGIF  string = "gif"
    FormatPNG  string = "png"
    FormatBMP  string = "bmp"
)

var Formats = [5]string{"tiff", "jpeg", "gif", "png", "bmp"}

func Open(address string) (i *Image, err error){
    i = new(Image)
    i.Address = address
    i.Quality = 100

    file, err := os.Open(address)
    if err != nil {
        i.Error = err
        return
    }
    defer file.Close()
    var img image.Image

    img, i.Format, err = image.Decode(file)
    if err != nil {
        i.Error = err
        return
    }
    i.Image = toNRGBA(img)

    i.width = i.Image.Bounds().Max.X
    i.height = i.Image.Bounds().Max.Y
    return
}

func (i *Image) Save(params ...string) (err error) {
    if i.Image == nil {
        if i.Error != nil {
            return i.Error
        }
        return errors.New("no image")
    }

    address := i.Address
    if len(params) > 0 && strings.Trim(params[0], " \n\t\r") != "" {
        address = params[0]
    }

    file, err := os.Create(address)
    defer file.Close()

    switch i.Format {
        case FormatJPEG:
            var rgba *image.RGBA
            if i.Image.Opaque() {
                rgba = &image.RGBA{
                    Pix:    i.Image.Pix,
                    Stride: i.Image.Stride,
                    Rect:   i.Image.Rect,
                }
            }

            if rgba != nil {
                err = jpeg.Encode(file, rgba, &jpeg.Options{Quality: i.Quality})
            } else {
                err = jpeg.Encode(file, i.Image, &jpeg.Options{Quality: i.Quality})
            }
        case FormatPNG:
            encoder := &png.Encoder{CompressionLevel: png.NoCompression}
            err = encoder.Encode(file, i.Image)
        case FormatGIF:
            err = gif.Encode(file, i.Image, &gif.Options{NumColors: 256})
        case FormatTIFF:
            err = tiff.Encode(file, i.Image, &tiff.Options{Compression: tiff.Uncompressed, Predictor: true})
        case FormatBMP:
            err = bmp.Encode(file, i.Image)
        default:
            err = errors.New("unsupported image format "+i.Format)
    }
    return
}

// Картинка больше прямоугольника
func (i *Image) ResizeOut(width, height int, params ...ResampleFilter) *Image {
    var w_ratio float64 = float64(width)  / i.Width64()
    var h_ratio float64 = float64(height) / i.Height64()

    if w_ratio == h_ratio {
    } else if w_ratio > h_ratio {
        height = int(i.Height64() * w_ratio)
    } else {
        width  = int(i.Width64() * h_ratio)
    }
    return i.Resize(width, height, params...)
}

// Картинка меньше прямоугольника
func (i *Image) ResizeIn(width, height int, params ...ResampleFilter) *Image {
    var w_ratio float64 = float64(width)  / i.Width64()
    var h_ratio float64 = float64(height) / i.Height64()

    if w_ratio == h_ratio {
    } else if w_ratio < h_ratio {
        height = int(i.Height64() * w_ratio)
    } else {
        width  = int(i.Width64() * h_ratio)
    }
    return i.Resize(width, height, params...)
}

func (i *Image) ResizeCrop(width, height int, params ...ResampleFilter) (img *Image) {
    img = i.ResizeOut(width, height, params...)
    defer func() {
        if r := recover(); r != nil {
            img.Error = errors.New(fmt.Sprintf("%v", r))
        }
    }()

    if img.Width() == width && img.Height() == height {
        return
    }

    Min := image.Point{}
    Min.X = int((img.Width() - width)/2)
    Min.Y = int((img.Height() - height)/2)

    Max := image.Point{}
    Max.X = Min.X + width
    Max.Y = Min.Y + height

    res := image.Rectangle{Min: Min, Max: Max}
    sub := img.Image.SubImage(res)
//    img.Image = Clone(sub)
    img.Image = toNRGBA(sub)

    img.width = width
    img.height= height

    return
}

func (i *Image) Resize(width, height int, params ...ResampleFilter) (image *Image) {
    image = new(Image)
    image.width = i.Width()
    image.height = i.Height()
    image.Quality = 100
    defer func() {
        if r := recover(); r != nil {
            image.Error = errors.New(fmt.Sprintf("%v", r))
        }
    }()

    filter := Lanczos
    if len(params) > 0 {
        filter = params[0]
    }

    if filter.Support <= 0.0 {
        image.Image, image.Error = i.resize(width, height)
    } else {
        if width != i.Width() {
            image.Image, image.Error = i.resizeW(width, filter)
            image.width = width
        }
        if image.Error != nil {
            return
        }
        if height != i.Height() {
            image.Image, image.Error = image.resizeH(height, filter)
            image.height = height
        }
    }
    image.Format = i.Format
    return
}

// In the future be replaced by toYCbCr
func toNRGBA(i image.Image) *image.NRGBA {
    srcBounds := i.Bounds()
    if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 {
        if src0, ok := i.(*image.NRGBA); ok {
            return src0
        }
    }
    return Clone(i)
}<|endoftext|>


<commit_before>package main

import (
	"io"
)

func Start(stop chan int) {
	for _, in := range Plugins.Inputs {
		go CopyMulty(in, Plugins.Outputs...)
	}

	select {
	case <-stop:
		return
	}
}

// Copy from 1 reader to multiple writers
func CopyMulty(src io.Reader, writers ...io.Writer) (err error) {
	buf := make([]byte, 32*1024)
	wIndex := 0

	for {
		nr, er := src.Read(buf)
		if nr > 0 && len(buf) > nr{
			Debug("Sending", src, ": ", string(buf[0:nr]))

			if Settings.splitOutput {
				// Simple round robin
				writers[wIndex].Write(buf[0:nr])

				wIndex++

				if wIndex >= len(writers) {
					wIndex = 0
				}
			} else {
				for _, dst := range writers {
					dst.Write(buf[0:nr])
				}
			}

		}
		if er == io.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return err
}
<commit_msg>Fix main goroutine<commit_after>package main

import (
	"io"
	"time"
)

func Start(stop chan int) {
	for _, in := range Plugins.Inputs {
		go CopyMulty(in, Plugins.Outputs...)
	}

	for {
		select {
		case <-stop:
			return
		case <- time.After(1 * time.Second):
		}
	}
}

// Copy from 1 reader to multiple writers
func CopyMulty(src io.Reader, writers ...io.Writer) (err error) {
	buf := make([]byte, 32*1024)
	wIndex := 0

	for {
		nr, er := src.Read(buf)
		if nr > 0 && len(buf) > nr{
			Debug("Sending", src, ": ", string(buf[0:nr]))

			if Settings.splitOutput {
				// Simple round robin
				writers[wIndex].Write(buf[0:nr])

				wIndex++

				if wIndex >= len(writers) {
					wIndex = 0
				}
			} else {
				for _, dst := range writers {
					dst.Write(buf[0:nr])
				}
			}

		}
		if er == io.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return err
}
<|endoftext|>


<commit_before>// Copyright 2012 Apcera Inc. All rights reserved.

package server

import (
	"time"
)

const (
	VERSION = "go 0.2.1.alpha.2"

	DEFAULT_PORT = 4222
	DEFAULT_HOST = "0.0.0.0"

	// 1k should be plenty since payloads sans connect string are separate
	MAX_CONTROL_LINE_SIZE = 1024

	// Should be using something different if > 1MB payload
	MAX_PAYLOAD_SIZE = (1024 * 1024)

	// Maximum outbound size per client
	MAX_PENDING_SIZE = (10 * 1024 * 1024)

	// Maximum connections default
	DEFAULT_MAX_CONNECTIONS = (64 * 1024)

	// TLS/SSL wait time
	SSL_TIMEOUT = 250 * time.Millisecond

	// Authorization wait time
	AUTH_TIMEOUT = 2 * SSL_TIMEOUT

	// Ping intervals
	DEFAULT_PING_INTERVAL = 2 * time.Minute
	DEFAULT_PING_MAX      = 2

	CR_LF = "\r\n"
)
<commit_msg>Added flush deadline<commit_after>// Copyright 2012 Apcera Inc. All rights reserved.

package server

import (
	"time"
)

const (
	VERSION = "go 0.2.1.alpha.2"

	DEFAULT_PORT = 4222
	DEFAULT_HOST = "0.0.0.0"

	// 1k should be plenty since payloads sans connect string are separate
	MAX_CONTROL_LINE_SIZE = 1024

	// Should be using something different if > 1MB payload
	MAX_PAYLOAD_SIZE = (1024 * 1024)

	// Maximum outbound size per client
	MAX_PENDING_SIZE = (10 * 1024 * 1024)

	// Maximum connections default
	DEFAULT_MAX_CONNECTIONS = (64 * 1024)

	// TLS/SSL wait time
	SSL_TIMEOUT = 250 * time.Millisecond

	// Authorization wait time
	AUTH_TIMEOUT = 2 * SSL_TIMEOUT

	// Ping intervals
	DEFAULT_PING_INTERVAL = 2 * time.Minute
	DEFAULT_PING_MAX      = 2

	CR_LF = "\r\n"

	// Write/Flush Deadlines
	DEFAULT_FLUSH_DEADLINE = 500 * time.Millisecond
)
<|endoftext|>


<commit_before>package workers

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io"
	"time"
)

type EnqueueData struct {
	Queue      string      `json:"queue,omitempty"`
	Class      string      `json:"class"`
	Args       interface{} `json:"args"`
	Jid        string      `json:"jid"`
	EnqueuedAt float64     `json:"enqueued_at"`
	EnqueueOptions
}

type EnqueueOptions struct {
	RetryCount int     `json:"retry_count,omitempty"`
	Retry      bool    `json:"retry,omitempty"`
	At         float64 `json:"at,omitempty"`
}

func generateJid() string {
	// Return 12 random bytes as 24 character hex
	b := make([]byte, 12)
	_, err := io.ReadFull(rand.Reader, b)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%x", b)
}

func Enqueue(queue, class string, args interface{}) error {
	return EnqueueWithOptions(queue, class, args, EnqueueOptions{})
}

func EnqueueWithOptions(queue, class string, args interface{}, opts EnqueueOptions) error {
	conn := Config.Pool.Get()
	defer conn.Close()

	data := EnqueueData{
		Queue:          queue,
		Class:          class,
		Args:           args,
		Jid:            generateJid(),
		EnqueuedAt:     float64(time.Now().UnixNano()) / 1000000000,
		EnqueueOptions: opts,
	}
	bytes, err := json.Marshal(data)
	if err != nil {
		return err
	}

	_, err = conn.Do("sadd", Config.Namespace+"queues", queue)
	if err != nil {
		return err
	}
	queue = Config.Namespace + "queue:" + queue
	_, err = conn.Do("rpush", queue, bytes)
	return err
}
<commit_msg>Return JID and error when queueing.<commit_after>package workers

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io"
	"time"
)

type EnqueueData struct {
	Queue      string      `json:"queue,omitempty"`
	Class      string      `json:"class"`
	Args       interface{} `json:"args"`
	Jid        string      `json:"jid"`
	EnqueuedAt float64     `json:"enqueued_at"`
	EnqueueOptions
}

type EnqueueOptions struct {
	RetryCount int     `json:"retry_count,omitempty"`
	Retry      bool    `json:"retry,omitempty"`
	At         float64 `json:"at,omitempty"`
}

func generateJid() string {
	// Return 12 random bytes as 24 character hex
	b := make([]byte, 12)
	_, err := io.ReadFull(rand.Reader, b)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%x", b)
}

func Enqueue(queue, class string, args interface{}) (string, error) {
	return EnqueueWithOptions(queue, class, args, EnqueueOptions{})
}

func EnqueueWithOptions(queue, class string, args interface{}, opts EnqueueOptions) (string, error) {
	conn := Config.Pool.Get()
	defer conn.Close()

	data := EnqueueData{
		Queue:          queue,
		Class:          class,
		Args:           args,
		Jid:            generateJid(),
		EnqueuedAt:     float64(time.Now().UnixNano()) / 1000000000,
		EnqueueOptions: opts,
	}
	bytes, err := json.Marshal(data)
	if err != nil {
		return "", err
	}

	_, err = conn.Do("sadd", Config.Namespace+"queues", queue)
	if err != nil {
		return "", err
	}
	queue = Config.Namespace + "queue:" + queue
	_, err = conn.Do("rpush", queue, bytes)
	if err != nil {
		return "", err
	}

	return data.Jid, nil
	return "", err
}
<|endoftext|>


<commit_before>package middleware

import (
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	"gopkg.in/gin-gonic/gin.v1"
	"gopkg.in/square/go-jose.v1/json"
)

type errorResponse struct {
	Errors []string `json:"errors"`
}

type normalResponse struct {
	Response string `json:"response"`
}

func TestErrorOccurred(t *testing.T) {
	router := gin.New()
	router.Use(ErrorHandler())
	router.GET("/test", func(c *gin.Context) {
		c.AbortWithError(400, errors.New("An error occured."))
	})

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/test", nil)

	router.ServeHTTP(w, req)

	var resp errorResponse
	json.NewDecoder(w.Result().Body).Decode(&resp)

	assert.Equal(t, 400, w.Code)
	assert.Equal(t, errorResponse{[]string{"An error occured."}}, resp)
}

func TestNoErrorOccurred(t *testing.T) {
	router := gin.New()
	router.Use(ErrorHandler())
	router.GET("/test", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"response": "Response",
		})
	})

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/test", nil)

	router.ServeHTTP(w, req)

	var resp normalResponse
	json.NewDecoder(w.Result().Body).Decode(&resp)

	assert.Equal(t, 200, w.Code)
	assert.Equal(t, normalResponse{"Response"}, resp)
}
<commit_msg>fix failing build<commit_after>package middleware

import (
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	"gopkg.in/gin-gonic/gin.v1"
	"gopkg.in/square/go-jose.v1/json"
)

type errorResponse struct {
	Errors []string `json:"errors"`
}

type normalResponse struct {
	Response string `json:"response"`
}

func TestErrorOccurred(t *testing.T) {
	router := gin.New()
	router.Use(ErrorHandler())
	router.GET("/test", func(c *gin.Context) {
		c.AbortWithError(400, errors.New("An error occured."))
	})

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/test", nil)

	router.ServeHTTP(w, req)

	var resp errorResponse
	json.NewDecoder(w.Body).Decode(&resp)

	assert.Equal(t, 400, w.Code)
	assert.Equal(t, errorResponse{[]string{"An error occured."}}, resp)
}

func TestNoErrorOccurred(t *testing.T) {
	router := gin.New()
	router.Use(ErrorHandler())
	router.GET("/test", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"response": "Response",
		})
	})

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/test", nil)

	router.ServeHTTP(w, req)

	var resp normalResponse
	json.NewDecoder(w.Body).Decode(&resp)

	assert.Equal(t, 200, w.Code)
	assert.Equal(t, normalResponse{"Response"}, resp)
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"github.com/lunny/xorm"
	k "github.com/roadrunners/go-url-shortener/app/models/key"
	"github.com/roadrunners/go-url-shortener/app/redis"
	r "github.com/robfig/revel"
	"os"
	"os/signal"
)

const totalBufferedPulls = 1000

var (
	pulls chan *ShortUrl
)

type ShortUrl struct {
	Id   int64  `xorm:"id pk not null autoincr" json:"-"`
	Slug string `xorm:"-" json:"slug"`
	URL  string `xorm:"url unique" json:"url"`
}

func (s ShortUrl) String() string {
	return fmt.Sprintf("(%s, %s)", s.Slug, s.URL)
}

func (s *ShortUrl) pull() {
	key := fmt.Sprintf("shorturl:%d:url", s.Id)
	err := redis.Client.Set(key, []byte(s.URL))
	if err != nil {
		r.ERROR.Fatal("Could not push short url to redis")
	}
}

func ShortUrlById(session *xorm.Session, id int64) (*ShortUrl, error) {
	var s ShortUrl
	has, err := session.Id(id).Get(&s)
	if err != nil || !has {
		return nil, err
	}
	s.Slug = k.GenKey(s.Id)
	return &s, nil
}

func ShortUrlBySlug(session *xorm.Session, slug string) (*ShortUrl, error) {
	id := k.GenId(slug)
	return ShortUrlById(session, id)
}

func CachedShortUrlBySlug(session *xorm.Session, slug string) (*ShortUrl, error) {
	id := k.GenId(slug)
	key := fmt.Sprintf("shorturl:%d:url", id)
	data, err := redis.Client.Get(key)
	if err == nil {
		s := ShortUrl{Id: id, Slug: slug, URL: string(data)}
		return &s, nil
	}
	r.WARN.Printf("Missed cache for slug %v (id %v)", slug, id)
	s, err := ShortUrlById(session, id)
	if s != nil {
		pulls <- s
	}
	return s, err
}

func ShortUrlCreate(session *xorm.Session, url string) (*ShortUrl, error) {
	s := ShortUrl{URL: url}
	_, err := session.Insert(&s)
	if err != nil {
		return nil, err
	}
	s.Slug = k.GenKey(s.Id)
	pulls <- &s
	return &s, nil
}

func pullMonitor() chan *ShortUrl {
	pulls := make(chan *ShortUrl, totalBufferedPulls)
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, os.Kill)
	go func() {
		for {
			select {
			case s := <-pulls:
				s.pull()
			case <-quit:
				r.WARN.Printf("Stopping pull monitor goroutine")
				close(pulls)
				return
			}
		}
	}()
	return pulls
}

func Init() {
	pulls = pullMonitor()
}

func init() {
	r.OnAppStart(Init)
}
<commit_msg>shorturl.go: be VERBOSE when we miss/populate the cache (redis)<commit_after>package models

import (
	"fmt"
	"github.com/lunny/xorm"
	k "github.com/roadrunners/go-url-shortener/app/models/key"
	"github.com/roadrunners/go-url-shortener/app/redis"
	r "github.com/robfig/revel"
	"os"
	"os/signal"
)

const totalBufferedPulls = 1000

var (
	pulls chan *ShortUrl
)

type ShortUrl struct {
	Id   int64  `xorm:"id pk not null autoincr" json:"-"`
	Slug string `xorm:"-" json:"slug"`
	URL  string `xorm:"url unique" json:"url"`
}

func (s ShortUrl) String() string {
	return fmt.Sprintf("(%s, %s)", s.Slug, s.URL)
}

func (s *ShortUrl) pull() {
	key := fmt.Sprintf("shorturl:%d:url", s.Id)
	r.INFO.Printf("Populating cache %v: %v", key, s.URL)
	err := redis.Client.Set(key, []byte(s.URL))
	if err != nil {
		r.ERROR.Fatal("Could not push short url to redis")
	}
}

func ShortUrlById(session *xorm.Session, id int64) (*ShortUrl, error) {
	var s ShortUrl
	has, err := session.Id(id).Get(&s)
	if err != nil || !has {
		return nil, err
	}
	s.Slug = k.GenKey(s.Id)
	return &s, nil
}

func ShortUrlBySlug(session *xorm.Session, slug string) (*ShortUrl, error) {
	id := k.GenId(slug)
	return ShortUrlById(session, id)
}

func CachedShortUrlBySlug(session *xorm.Session, slug string) (*ShortUrl, error) {
	id := k.GenId(slug)
	key := fmt.Sprintf("shorturl:%d:url", id)
	data, err := redis.Client.Get(key)
	if err == nil {
		s := ShortUrl{Id: id, Slug: slug, URL: string(data)}
		return &s, nil
	}
	r.WARN.Printf("Missed cache for slug %v (id %v, key %v)", slug, id, key)
	s, err := ShortUrlById(session, id)
	if s != nil && err != nil {
		pulls <- s
	}
	return s, err
}

func ShortUrlCreate(session *xorm.Session, url string) (*ShortUrl, error) {
	s := ShortUrl{URL: url}
	_, err := session.Insert(&s)
	if err != nil {
		return nil, err
	}
	s.Slug = k.GenKey(s.Id)
	pulls <- &s
	return &s, nil
}

func pullMonitor() chan *ShortUrl {
	pulls := make(chan *ShortUrl, totalBufferedPulls)
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, os.Kill)
	go func() {
		for {
			select {
			case s := <-pulls:
				s.pull()
			case <-quit:
				r.WARN.Print("Stopping pull monitor")
				close(pulls)
				return
			}
		}
	}()
	return pulls
}

func Init() {
	pulls = pullMonitor()
}

func init() {
	r.OnAppStart(Init)
}
<|endoftext|>


<commit_before>package assert

import (
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

/*
	Helper functions
*/

// ObjectsAreEqual determines if two objects are considered equal.
//
// This function does no assertion of any kind.
func ObjectsAreEqual(a, b interface{}) bool {

	if reflect.DeepEqual(a, b) {
		return true
	}

	if reflect.ValueOf(a) == reflect.ValueOf(b) {
		return true
	}

	// Last ditch effort
	if fmt.Sprintf("%#v", a) == fmt.Sprintf("%#v", b) {
		return true
	}

	return false

}

/* CallerInfo is necessary because the assert functions use the testing object
internally, causing it to print the file:line of the assert method, rather than where
the problem actually occured in calling code.*/

// CallerInfo returns a string containing the file and line number of the assert call
// that failed.
func CallerInfo() string {

	file := ""
	line := 0
	ok := false

	for i := 0; ; i++ {
		_, file, line, ok = runtime.Caller(i)
		if !ok {
			return ""
		}
		parts := strings.Split(file, "/")
		dir := parts[len(parts)-2]
		file = parts[len(parts)-1]
		if (dir != "assert" && dir != "mock") || file == "mock_test.go" {
			break
		}
	}

	return fmt.Sprintf("%s:%d", file, line)
}

// getWhitespaceString returns a string that is long enough to overwrite the default
// output from the go testing framework.
func getWhitespaceString() string {

	_, file, line, ok := runtime.Caller(1)
	if !ok {
		return ""
	}
	parts := strings.Split(file, "/")
	file = parts[len(parts)-1]

	return strings.Repeat(" ", len(fmt.Sprintf("%s:%d:      ", file, line)))

}

// Implements asserts that an object is implemented by the specified interface.
//
//    assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject")
func Implements(t *testing.T, interfaceObject interface{}, object interface{}, message ...string) bool {

	interfaceType := reflect.TypeOf(interfaceObject).Elem()

	if !reflect.TypeOf(object).Implements(interfaceType) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tObject must implement: %v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), interfaceType, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tObject must implement: %v\n\r", getWhitespaceString(), CallerInfo(), interfaceType)
		}

		return false
	}

	return true

}

// IsType asserts that the specified objects are of the same type.
func IsType(t *testing.T, expectedType interface{}, object interface{}, message ...string) bool {

	if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tObject expected to be of type %v, but was %v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), reflect.TypeOf(expectedType), reflect.TypeOf(object), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tObject expected to be of type %v, but was %v\n\r", getWhitespaceString(), CallerInfo(), reflect.TypeOf(expectedType), reflect.TypeOf(object))
		}

		return false
	}

	return true
}

// Equal asserts that two objects are equal.
//
//    assert.Equal(t, 123, 123, "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func Equal(t *testing.T, a, b interface{}, message ...string) bool {

	if !ObjectsAreEqual(a, b) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tNot equal: %#v != %#v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), a, b, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tNot equal: %#v != %#v\n\r", getWhitespaceString(), CallerInfo(), a, b)
		}

		return false
	}

	return true

}

// NotNil asserts that the specified object is not nil.
//
//    assert.NotNil(t, err, "err should be something")
//
// Returns whether the assertion was successful (true) or not (false).
func NotNil(t *testing.T, object interface{}, message ...string) bool {

	var success bool = true

	if object == nil {
		success = false
	} else if reflect.ValueOf(object).IsNil() {
		success = false
	}

	if !success {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tExpected not to be nil.\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tExpected not to be nil.\n\r", getWhitespaceString(), CallerInfo())
		}

	}

	return success
}

// Nil asserts that the specified object is nil.
//
//    assert.Nil(t, err, "err should be nothing")
//
// Returns whether the assertion was successful (true) or not (false).
func Nil(t *testing.T, object interface{}, message ...string) bool {

	if object == nil {
		return true
	} else if reflect.ValueOf(object).IsNil() {
		return true
	}

	if len(message) > 0 {
		t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tExpected nil, but got: %#v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), object, message)
	} else {
		t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tExpected nil, but got: %#v\n\r", getWhitespaceString(), CallerInfo(), object)
	}

	return false
}

// True asserts that the specified value is true.
//
//    assert.True(t, myBool, "myBool should be true")
//
// Returns whether the assertion was successful (true) or not (false).
func True(t *testing.T, value bool, message ...string) bool {

	if value != true {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould be true\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould be true\n\r", getWhitespaceString(), CallerInfo())
		}

		return false
	}

	return true

}

// False asserts that the specified value is true.
//
//    assert.False(t, myBool, "myBool should be false")
//
// Returns whether the assertion was successful (true) or not (false).
func False(t *testing.T, value bool, message ...string) bool {

	if value != false {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould be false\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould be false\n\r", getWhitespaceString(), CallerInfo())
		}

		return false
	}

	return true

}

// NotEqual asserts that the specified values are NOT equal.
//
//    assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func NotEqual(t *testing.T, a, b interface{}, message ...string) bool {

	if ObjectsAreEqual(a, b) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould not be equal.\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould not be equal.\n\r", getWhitespaceString(), CallerInfo())
		}

		return false
	}

	return true

}

// Contains asserts that the specified string contains the specified substring.
//
//    assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
//
// Returns whether the assertion was successful (true) or not (false).
func Contains(t *testing.T, s, contains string, message ...string) bool {

	if !strings.Contains(s, contains) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\t\"%s\" does not contain \"%s\"\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), s, contains, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\t\"%s\" does not contain \"%s\"\n\r", getWhitespaceString(), CallerInfo(), s, contains)
		}

		return false
	}

	return true

}

// NotContains asserts that the specified string does NOT contain the specified substring.
//
//    assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func NotContains(t *testing.T, s, contains string, message ...string) bool {

	if strings.Contains(s, contains) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\t\"%s\" should not contain \"%s\"\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), s, contains, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\t\"%s\" should not contain \"%s\"\n\r", getWhitespaceString(), CallerInfo(), s, contains)
		}

		return false
	}

	return true

}

// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics
// methods, and represents a simple func that takes no arguments, and returns nothing.
type PanicTestFunc func()

// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
func didPanic(f PanicTestFunc) (bool, interface{}) {

	var didPanic bool = false
	var message interface{}
	func() {

		defer func() {
			if message = recover(); message != nil {
				didPanic = true
			}
		}()

		// call the target function
		f()

	}()

	return didPanic, message

}

// Panics asserts that the code inside the specified PanicTestFunc panics.
//
//   assert.Panics(t, func(){
//     GoCrazy()
//   }, "Calling GoCrazy() should panic")
//
// Returns whether the assertion was successful (true) or not (false).
func Panics(t *testing.T, f PanicTestFunc, message ...string) bool {

	if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tfunc %#v should panic\n\r\tPanic value:\t%v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), f, panicValue, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tfunc %#v should panic\n\r\tPanic value:\t%v\n\r", getWhitespaceString(), CallerInfo(), f, panicValue)
		}
		return false
	}

	return true
}

// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
//   assert.NotPanics(t, func(){
//     RemainCalm()
//   }, "Calling RemainCalm() should NOT panic")
//
// Returns whether the assertion was successful (true) or not (false).
func NotPanics(t *testing.T, f PanicTestFunc, message ...string) bool {
	if funcDidPanic, panicValue := didPanic(f); funcDidPanic {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tfunc %#v should panic\n\r\tPanic value:\t%v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), f, panicValue, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tfunc %#v should panic\n\r\tPanic value:\t%v\n\r", getWhitespaceString(), CallerInfo(), f, panicValue)
		}

		return false
	}

	return true
}
<commit_msg>Fixes #7<commit_after>package assert

import (
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

/*
	Helper functions
*/

// ObjectsAreEqual determines if two objects are considered equal.
//
// This function does no assertion of any kind.
func ObjectsAreEqual(a, b interface{}) bool {

	if reflect.DeepEqual(a, b) {
		return true
	}

	if reflect.ValueOf(a) == reflect.ValueOf(b) {
		return true
	}

	// Last ditch effort
	if fmt.Sprintf("%#v", a) == fmt.Sprintf("%#v", b) {
		return true
	}

	return false

}

/* CallerInfo is necessary because the assert functions use the testing object
internally, causing it to print the file:line of the assert method, rather than where
the problem actually occured in calling code.*/

// CallerInfo returns a string containing the file and line number of the assert call
// that failed.
func CallerInfo() string {

	file := ""
	line := 0
	ok := false

	for i := 0; ; i++ {
		_, file, line, ok = runtime.Caller(i)
		if !ok {
			return ""
		}
		parts := strings.Split(file, "/")
		dir := parts[len(parts)-2]
		file = parts[len(parts)-1]
		if (dir != "assert" && dir != "mock") || file == "mock_test.go" {
			break
		}
	}

	return fmt.Sprintf("%s:%d", file, line)
}

// getWhitespaceString returns a string that is long enough to overwrite the default
// output from the go testing framework.
func getWhitespaceString() string {

	_, file, line, ok := runtime.Caller(1)
	if !ok {
		return ""
	}
	parts := strings.Split(file, "/")
	file = parts[len(parts)-1]

	return strings.Repeat(" ", len(fmt.Sprintf("%s:%d:      ", file, line)))

}

// Implements asserts that an object is implemented by the specified interface.
//
//    assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject")
func Implements(t *testing.T, interfaceObject interface{}, object interface{}, message ...string) bool {

	interfaceType := reflect.TypeOf(interfaceObject).Elem()

	if !reflect.TypeOf(object).Implements(interfaceType) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tObject must implement: %v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), interfaceType, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tObject must implement: %v\n\r", getWhitespaceString(), CallerInfo(), interfaceType)
		}

		return false
	}

	return true

}

// IsType asserts that the specified objects are of the same type.
func IsType(t *testing.T, expectedType interface{}, object interface{}, message ...string) bool {

	if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tObject expected to be of type %v, but was %v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), reflect.TypeOf(expectedType), reflect.TypeOf(object), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tObject expected to be of type %v, but was %v\n\r", getWhitespaceString(), CallerInfo(), reflect.TypeOf(expectedType), reflect.TypeOf(object))
		}

		return false
	}

	return true
}

// Equal asserts that two objects are equal.
//
//    assert.Equal(t, 123, 123, "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func Equal(t *testing.T, a, b interface{}, message ...string) bool {

	if !ObjectsAreEqual(a, b) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tNot equal: %#v != %#v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), a, b, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tNot equal: %#v != %#v\n\r", getWhitespaceString(), CallerInfo(), a, b)
		}

		return false
	}

	return true

}

// NotNil asserts that the specified object is not nil.
//
//    assert.NotNil(t, err, "err should be something")
//
// Returns whether the assertion was successful (true) or not (false).
func NotNil(t *testing.T, object interface{}, message ...string) bool {

	var success bool = true

	if object == nil {
		success = false
	} else if reflect.ValueOf(object).IsNil() {
		success = false
	}

	if !success {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tExpected not to be nil.\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tExpected not to be nil.\n\r", getWhitespaceString(), CallerInfo())
		}

	}

	return success
}

// Nil asserts that the specified object is nil.
//
//    assert.Nil(t, err, "err should be nothing")
//
// Returns whether the assertion was successful (true) or not (false).
func Nil(t *testing.T, object interface{}, message ...string) bool {

	if object == nil {
		return true
	} else if reflect.ValueOf(object).IsNil() {
		return true
	}

	if len(message) > 0 {
		t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tExpected nil, but got: %#v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), object, message)
	} else {
		t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tExpected nil, but got: %#v\n\r", getWhitespaceString(), CallerInfo(), object)
	}

	return false
}

// True asserts that the specified value is true.
//
//    assert.True(t, myBool, "myBool should be true")
//
// Returns whether the assertion was successful (true) or not (false).
func True(t *testing.T, value bool, message ...string) bool {

	if value != true {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould be true\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould be true\n\r", getWhitespaceString(), CallerInfo())
		}

		return false
	}

	return true

}

// False asserts that the specified value is true.
//
//    assert.False(t, myBool, "myBool should be false")
//
// Returns whether the assertion was successful (true) or not (false).
func False(t *testing.T, value bool, message ...string) bool {

	if value != false {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould be false\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould be false\n\r", getWhitespaceString(), CallerInfo())
		}

		return false
	}

	return true

}

// NotEqual asserts that the specified values are NOT equal.
//
//    assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func NotEqual(t *testing.T, a, b interface{}, message ...string) bool {

	if ObjectsAreEqual(a, b) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould not be equal.\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tShould not be equal.\n\r", getWhitespaceString(), CallerInfo())
		}

		return false
	}

	return true

}

// Contains asserts that the specified string contains the specified substring.
//
//    assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
//
// Returns whether the assertion was successful (true) or not (false).
func Contains(t *testing.T, s, contains string, message ...string) bool {

	if !strings.Contains(s, contains) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\t\"%s\" does not contain \"%s\"\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), s, contains, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\t\"%s\" does not contain \"%s\"\n\r", getWhitespaceString(), CallerInfo(), s, contains)
		}

		return false
	}

	return true

}

// NotContains asserts that the specified string does NOT contain the specified substring.
//
//    assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func NotContains(t *testing.T, s, contains string, message ...string) bool {

	if strings.Contains(s, contains) {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\t\"%s\" should not contain \"%s\"\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), s, contains, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\t\"%s\" should not contain \"%s\"\n\r", getWhitespaceString(), CallerInfo(), s, contains)
		}

		return false
	}

	return true

}

// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics
// methods, and represents a simple func that takes no arguments, and returns nothing.
type PanicTestFunc func()

// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
func didPanic(f PanicTestFunc) (bool, interface{}) {

	var didPanic bool = false
	var message interface{}
	func() {

		defer func() {
			if message = recover(); message != nil {
				didPanic = true
			}
		}()

		// call the target function
		f()

	}()

	return didPanic, message

}

// Panics asserts that the code inside the specified PanicTestFunc panics.
//
//   assert.Panics(t, func(){
//     GoCrazy()
//   }, "Calling GoCrazy() should panic")
//
// Returns whether the assertion was successful (true) or not (false).
func Panics(t *testing.T, f PanicTestFunc, message ...string) bool {

	if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tfunc %#v should panic\n\r\tPanic value:\t%v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), f, panicValue, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tfunc %#v should panic\n\r\tPanic value:\t%v\n\r", getWhitespaceString(), CallerInfo(), f, panicValue)
		}
		return false
	}

	return true
}

// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
//   assert.NotPanics(t, func(){
//     RemainCalm()
//   }, "Calling RemainCalm() should NOT panic")
//
// Returns whether the assertion was successful (true) or not (false).
func NotPanics(t *testing.T, f PanicTestFunc, message ...string) bool {
	if funcDidPanic, panicValue := didPanic(f); funcDidPanic {

		if len(message) > 0 {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tfunc %#v should not panic\n\r\tPanic value:\t%v\n\r\tMessages:\t%s\n\r", getWhitespaceString(), CallerInfo(), f, panicValue, message)
		} else {
			t.Errorf("\r%s\r\tLocation:\t%s\n\r\tError:\t\tfunc %#v should not panic\n\r\tPanic value:\t%v\n\r", getWhitespaceString(), CallerInfo(), f, panicValue)
		}

		return false
	}

	return true
}
<|endoftext|>


<commit_before>package agent

import (
	"fmt"
	"testing"
	"time"

	"github.com/hashicorp/consul/acl"

	"github.com/stretchr/testify/require"

	"github.com/hashicorp/consul/agent/structs"
)

func TestAgent_sidecarServiceFromNodeService(t *testing.T) {
	if testing.Short() {
		t.Skip("too slow for testing.Short")
	}

	tests := []struct {
		name       string
		sd         *structs.ServiceDefinition
		token      string
		wantNS     *structs.NodeService
		wantChecks []*structs.CheckType
		wantToken  string
		wantErr    string
	}{
		{
			name: "no sidecar",
			sd: &structs.ServiceDefinition{
				Name: "web",
				Port: 1111,
			},
			token:      "foo",
			wantNS:     nil,
			wantChecks: nil,
			wantToken:  "",
			wantErr:    "", // Should NOT error
		},
		{
			name: "all the defaults",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{},
				},
			},
			token: "foo",
			wantNS: &structs.NodeService{
				EnterpriseMeta:             *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Kind:                       structs.ServiceKindConnectProxy,
				ID:                         "web1-sidecar-proxy",
				Service:                    "web-sidecar-proxy",
				Port:                       0,
				LocallyRegisteredAsSidecar: true,
				Proxy: structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.0.1",
					LocalServicePort:       1111,
				},
			},
			wantChecks: nil,
			wantToken:  "foo",
		},
		{
			name: "all the allowed overrides",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Tags: []string{"baz"},
				Meta: map[string]string{"foo": "baz"},
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{
						Name:    "motorbike1",
						Port:    3333,
						Tags:    []string{"foo", "bar"},
						Address: "127.127.127.127",
						Meta:    map[string]string{"foo": "bar"},
						Check: structs.CheckType{
							ScriptArgs: []string{"sleep", "1"},
							Interval:   999 * time.Second,
						},
						Token:             "custom-token",
						EnableTagOverride: true,
						Proxy: &structs.ConnectProxyConfig{
							DestinationServiceName: "web",
							DestinationServiceID:   "web1",
							LocalServiceAddress:    "127.0.127.0",
							LocalServicePort:       9999,
							Config:                 map[string]interface{}{"baz": "qux"},
							Upstreams:              structs.TestUpstreams(t),
						},
					},
				},
			},
			token: "foo",
			wantNS: &structs.NodeService{
				EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Kind:           structs.ServiceKindConnectProxy,
				ID:             "web1-sidecar-proxy",
				Service:        "motorbike1",
				Port:           3333,
				Tags:           []string{"foo", "bar"},
				Address:        "127.127.127.127",
				Meta: map[string]string{
					"foo": "bar",
				},
				LocallyRegisteredAsSidecar: true,
				EnableTagOverride:          true,
				Proxy: structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.127.0",
					LocalServicePort:       9999,
					Config:                 map[string]interface{}{"baz": "qux"},
					Upstreams: structs.TestAddDefaultsToUpstreams(t, structs.TestUpstreams(t),
						*structs.DefaultEnterpriseMetaInDefaultPartition()),
				},
			},
			wantChecks: []*structs.CheckType{
				{
					ScriptArgs: []string{"sleep", "1"},
					Interval:   999 * time.Second,
				},
			},
			wantToken: "custom-token",
		},
		{
			name: "inherit tags and meta",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Tags: []string{"foo"},
				Meta: map[string]string{"foo": "bar"},
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{},
				},
			},
			wantNS: &structs.NodeService{
				EnterpriseMeta:             *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Kind:                       structs.ServiceKindConnectProxy,
				ID:                         "web1-sidecar-proxy",
				Service:                    "web-sidecar-proxy",
				Port:                       0,
				Tags:                       []string{"foo"},
				Meta:                       map[string]string{"foo": "bar"},
				LocallyRegisteredAsSidecar: true,
				Proxy: structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.0.1",
					LocalServicePort:       1111,
				},
			},
			wantChecks: nil,
		},
		{
			name: "invalid check type",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{
						Check: structs.CheckType{
							TCP: "foo",
							// Invalid since no interval specified
						},
					},
				},
			},
			token:   "foo",
			wantErr: "Interval must be > 0",
		},
		{
			name: "invalid meta",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{
						Meta: map[string]string{
							"consul-reserved-key-should-be-rejected": "true",
						},
					},
				},
			},
			token:   "foo",
			wantErr: "reserved for internal use",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ns := tt.sd.NodeService()
			err := ns.Validate()
			require.NoError(t, err, "Invalid test case - NodeService must validate")

			gotNS, gotChecks, gotToken, err := sidecarServiceFromNodeService(ns, tt.token)
			if tt.wantErr != "" {
				require.Error(t, err)
				require.Contains(t, err.Error(), tt.wantErr)
				return
			}

			require.NoError(t, err)
			require.Equal(t, tt.wantNS, gotNS)
			require.Equal(t, tt.wantChecks, gotChecks)
			require.Equal(t, tt.wantToken, gotToken)
		})
	}
}

func TestAgent_SidecarPortFromServiceID(t *testing.T) {
	if testing.Short() {
		t.Skip("too slow for testing.Short")
	}

	tests := []struct {
		name              string
		autoPortsDisabled bool
		enterpriseMeta    acl.EnterpriseMeta
		maxPort           int
		port              int
		preRegister       *structs.ServiceDefinition
		serviceID         string
		wantPort          int
		wantErr           string
	}{
		{
			name:      "use auto ports",
			serviceID: "web1",
			wantPort:  2222,
		},
		{
			name: "re-registering same sidecar with no port should pick same one",
			// Allow multiple ports to be sure we get the right one
			maxPort: 2500,
			// Pre register the sidecar we want
			preRegister: &structs.ServiceDefinition{
				Kind: structs.ServiceKindConnectProxy,
				ID:   "web1-sidecar-proxy",
				Name: "web-sidecar-proxy",
				Port: 2222,
				Proxy: &structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.0.1",
					LocalServicePort:       1111,
				},
			},
			// Register same again
			serviceID: "web1-sidecar-proxy",
			wantPort:  2222, // Should claim the same port as before
		},
		{
			name: "all auto ports already taken",
			// register another sidecar consuming our 1 and only allocated auto port.
			preRegister: &structs.ServiceDefinition{
				Kind: structs.ServiceKindConnectProxy,
				Name: "api-proxy-sidecar",
				Port: 2222, // Consume the one available auto-port
				Proxy: &structs.ConnectProxyConfig{
					DestinationServiceName: "api",
				},
			},
			wantErr: "none left in the configured range [2222, 2222]",
		},
		{
			name:              "auto ports disabled",
			autoPortsDisabled: true,
			wantErr:           "auto-assignment disabled in config",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Set port range to be tiny (one available) to test consuming all of it.
			// This allows a single assigned port at 2222 thanks to being inclusive at
			// both ends.
			if tt.maxPort == 0 {
				tt.maxPort = 2222
			}
			hcl := fmt.Sprintf(`
			ports {
				sidecar_min_port = 2222
				sidecar_max_port = %d
			}
			`, tt.maxPort)
			if tt.autoPortsDisabled {
				hcl = `
				ports {
					sidecar_min_port = 0
					sidecar_max_port = 0
				}
				`
			}
			a := NewTestAgent(t, hcl)
			defer a.Shutdown()

			if tt.preRegister != nil {
				err := a.addServiceFromSource(tt.preRegister.NodeService(), nil, false, "", ConfigSourceLocal)
				require.NoError(t, err)
			}

			gotPort, err := a.sidecarPortFromServiceIDLocked(structs.ServiceID{ID: tt.serviceID, EnterpriseMeta: tt.enterpriseMeta})

			if tt.wantErr != "" {
				require.Error(t, err)
				require.Contains(t, err.Error(), tt.wantErr)
				return
			}

			require.NoError(t, err)
			require.Equal(t, tt.wantPort, gotPort)
		})
	}
}

func TestAgent_SidecarDefaultChecks(t *testing.T) {
	tests := []struct {
		name                 string
		serviceID            string
		svcAddress           string
		proxyLocalSvcAddress string
		port                 int
		wantChecks           []*structs.CheckType
	}{{
		name:                 "uses proxy address for check",
		serviceID:            "web1-1-sidecar-proxy",
		svcAddress:           "123.123.123.123",
		proxyLocalSvcAddress: "255.255.255.255",
		port:                 2222,
		wantChecks: []*structs.CheckType{
			{
				Name:     "Connect Sidecar Listening",
				TCP:      "123.123.123.123:2222",
				Interval: 10 * time.Second,
			},
			{
				Name:         "Connect Sidecar Aliasing web1-1",
				AliasService: "web1-1",
			},
		},
	},
		{
			name:                 "uses proxy.local_service_address for check if proxy address is empty",
			serviceID:            "web1-1-sidecar-proxy",
			proxyLocalSvcAddress: "1.2.3.4",
			port:                 2222,
			wantChecks: []*structs.CheckType{
				{
					Name:     "Connect Sidecar Listening",
					TCP:      "1.2.3.4:2222",
					Interval: 10 * time.Second,
				},
				{
					Name:         "Connect Sidecar Aliasing web1-1",
					AliasService: "web1-1",
				},
			},
		},
		{
			name:                 "redundant name",
			serviceID:            "1-sidecar-proxy-web1-sidecar-proxy",
			svcAddress:           "123.123.123.123",
			proxyLocalSvcAddress: "255.255.255.255",
			port:                 2222,
			wantChecks: []*structs.CheckType{
				{
					Name:     "Connect Sidecar Listening",
					TCP:      "123.123.123.123:2222",
					Interval: 10 * time.Second,
				},
				{
					Name:         "Connect Sidecar Aliasing 1-sidecar-proxy-web1",
					AliasService: "1-sidecar-proxy-web1",
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotChecks := sidecarDefaultChecks(tt.serviceID, tt.svcAddress, tt.proxyLocalSvcAddress, tt.port)
			require.Equal(t, tt.wantChecks, gotChecks)
		})
	}
}
<commit_msg>Backport test from ENT: "Fix missing test fields" (#15258)<commit_after>package agent

import (
	"fmt"
	"testing"
	"time"

	"github.com/hashicorp/consul/acl"

	"github.com/stretchr/testify/require"

	"github.com/hashicorp/consul/agent/structs"
)

func TestAgent_sidecarServiceFromNodeService(t *testing.T) {
	if testing.Short() {
		t.Skip("too slow for testing.Short")
	}

	tests := []struct {
		name       string
		sd         *structs.ServiceDefinition
		token      string
		wantNS     *structs.NodeService
		wantChecks []*structs.CheckType
		wantToken  string
		wantErr    string
	}{
		{
			name: "no sidecar",
			sd: &structs.ServiceDefinition{
				Name: "web",
				Port: 1111,
			},
			token:      "foo",
			wantNS:     nil,
			wantChecks: nil,
			wantToken:  "",
			wantErr:    "", // Should NOT error
		},
		{
			name: "all the defaults",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{},
				},
			},
			token: "foo",
			wantNS: &structs.NodeService{
				EnterpriseMeta:             *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Kind:                       structs.ServiceKindConnectProxy,
				ID:                         "web1-sidecar-proxy",
				Service:                    "web-sidecar-proxy",
				Port:                       0,
				LocallyRegisteredAsSidecar: true,
				Proxy: structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.0.1",
					LocalServicePort:       1111,
				},
			},
			wantChecks: nil,
			wantToken:  "foo",
		},
		{
			name: "all the allowed overrides",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Tags: []string{"baz"},
				Meta: map[string]string{"foo": "baz"},
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{
						Name:    "motorbike1",
						Port:    3333,
						Tags:    []string{"foo", "bar"},
						Address: "127.127.127.127",
						Meta:    map[string]string{"foo": "bar"},
						Check: structs.CheckType{
							ScriptArgs: []string{"sleep", "1"},
							Interval:   999 * time.Second,
						},
						Token:             "custom-token",
						EnableTagOverride: true,
						Proxy: &structs.ConnectProxyConfig{
							DestinationServiceName: "web",
							DestinationServiceID:   "web1",
							LocalServiceAddress:    "127.0.127.0",
							LocalServicePort:       9999,
							Config:                 map[string]interface{}{"baz": "qux"},
							Upstreams:              structs.TestUpstreams(t),
						},
					},
				},
			},
			token: "foo",
			wantNS: &structs.NodeService{
				EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Kind:           structs.ServiceKindConnectProxy,
				ID:             "web1-sidecar-proxy",
				Service:        "motorbike1",
				Port:           3333,
				Tags:           []string{"foo", "bar"},
				Address:        "127.127.127.127",
				Meta: map[string]string{
					"foo": "bar",
				},
				LocallyRegisteredAsSidecar: true,
				EnableTagOverride:          true,
				Proxy: structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.127.0",
					LocalServicePort:       9999,
					Config:                 map[string]interface{}{"baz": "qux"},
					Upstreams: structs.TestAddDefaultsToUpstreams(t, structs.TestUpstreams(t),
						*structs.DefaultEnterpriseMetaInDefaultPartition()),
				},
			},
			wantChecks: []*structs.CheckType{
				{
					ScriptArgs: []string{"sleep", "1"},
					Interval:   999 * time.Second,
				},
			},
			wantToken: "custom-token",
		},
		{
			name: "inherit tags and meta",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Tags: []string{"foo"},
				Meta: map[string]string{"foo": "bar"},
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{},
				},
			},
			wantNS: &structs.NodeService{
				EnterpriseMeta:             *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Kind:                       structs.ServiceKindConnectProxy,
				ID:                         "web1-sidecar-proxy",
				Service:                    "web-sidecar-proxy",
				Port:                       0,
				Tags:                       []string{"foo"},
				Meta:                       map[string]string{"foo": "bar"},
				LocallyRegisteredAsSidecar: true,
				Proxy: structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.0.1",
					LocalServicePort:       1111,
				},
			},
			wantChecks: nil,
		},
		{
			name: "invalid check type",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{
						Check: structs.CheckType{
							TCP: "foo",
							// Invalid since no interval specified
						},
					},
				},
			},
			token:   "foo",
			wantErr: "Interval must be > 0",
		},
		{
			name: "invalid meta",
			sd: &structs.ServiceDefinition{
				ID:   "web1",
				Name: "web",
				Port: 1111,
				Connect: &structs.ServiceConnect{
					SidecarService: &structs.ServiceDefinition{
						Meta: map[string]string{
							"consul-reserved-key-should-be-rejected": "true",
						},
					},
				},
			},
			token:   "foo",
			wantErr: "reserved for internal use",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ns := tt.sd.NodeService()
			err := ns.Validate()
			require.NoError(t, err, "Invalid test case - NodeService must validate")

			gotNS, gotChecks, gotToken, err := sidecarServiceFromNodeService(ns, tt.token)
			if tt.wantErr != "" {
				require.Error(t, err)
				require.Contains(t, err.Error(), tt.wantErr)
				return
			}

			require.NoError(t, err)
			require.Equal(t, tt.wantNS, gotNS)
			require.Equal(t, tt.wantChecks, gotChecks)
			require.Equal(t, tt.wantToken, gotToken)
		})
	}
}

func TestAgent_SidecarPortFromServiceID(t *testing.T) {
	if testing.Short() {
		t.Skip("too slow for testing.Short")
	}

	tests := []struct {
		name              string
		autoPortsDisabled bool
		enterpriseMeta    acl.EnterpriseMeta
		maxPort           int
		port              int
		preRegister       []*structs.ServiceDefinition
		serviceID         string
		wantPort          int
		wantErr           string
	}{
		{
			name:      "use auto ports",
			serviceID: "web1",
			wantPort:  2222,
		},
		{
			name: "re-registering same sidecar with no port should pick same one",
			// Allow multiple ports to be sure we get the right one
			maxPort: 2500,
			// Pre register the main service and sidecar we want
			preRegister: []*structs.ServiceDefinition{{
				Kind:           structs.ServiceKindConnectProxy,
				ID:             "web1",
				EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Name:           "web",
				Port:           2221,
				Proxy: &structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.0.1",
					LocalServicePort:       1110,
				},
			}, {
				Kind:           structs.ServiceKindConnectProxy,
				ID:             "web1-sidecar-proxy",
				EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Name:           "web-sidecar-proxy",
				Port:           2222,
				Proxy: &structs.ConnectProxyConfig{
					DestinationServiceName: "web",
					DestinationServiceID:   "web1",
					LocalServiceAddress:    "127.0.0.1",
					LocalServicePort:       1111,
				},
			}},
			// Register same sidecar again
			serviceID:      "web1-sidecar-proxy",
			enterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
			wantPort:       2222, // Should claim the same port as before
		},
		{
			name: "all auto ports already taken",
			// register another service with sidecar consuming our 1 and only allocated auto port.
			preRegister: []*structs.ServiceDefinition{{
				Kind:           structs.ServiceKindConnectProxy,
				ID:             "api1",
				EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
				Name:           "api",
				Port:           2221,
				Proxy: &structs.ConnectProxyConfig{
					DestinationServiceName: "api",
					DestinationServiceID:   "api1",
					LocalServiceAddress:    "127.0.0.1",
					LocalServicePort:       1110,
				},
			}, {
				Kind: structs.ServiceKindConnectProxy,
				Name: "api-proxy-sidecar",
				Port: 2222, // Consume the one available auto-port
				Proxy: &structs.ConnectProxyConfig{
					DestinationServiceID:   "api1",
					DestinationServiceName: "api",
				},
			}},
			wantErr: "none left in the configured range [2222, 2222]",
		},
		{
			name:              "auto ports disabled",
			autoPortsDisabled: true,
			wantErr:           "auto-assignment disabled in config",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Set port range to be tiny (one available) to test consuming all of it.
			// This allows a single assigned port at 2222 thanks to being inclusive at
			// both ends.
			if tt.maxPort == 0 {
				tt.maxPort = 2222
			}
			hcl := fmt.Sprintf(`
			ports {
				sidecar_min_port = 2222
				sidecar_max_port = %d
			}
			`, tt.maxPort)
			if tt.autoPortsDisabled {
				hcl = `
				ports {
					sidecar_min_port = 0
					sidecar_max_port = 0
				}
				`
			}
			a := NewTestAgent(t, hcl)
			defer a.Shutdown()

			if len(tt.preRegister) > 0 {
				for _, s := range tt.preRegister {
					err := a.addServiceFromSource(s.NodeService(), nil, false, "", ConfigSourceLocal)
					require.NoError(t, err)
				}
			}

			gotPort, err := a.sidecarPortFromServiceIDLocked(structs.ServiceID{ID: tt.serviceID, EnterpriseMeta: tt.enterpriseMeta})

			if tt.wantErr != "" {
				require.Error(t, err)
				require.Contains(t, err.Error(), tt.wantErr)
				return
			}

			require.NoError(t, err)
			require.Equal(t, tt.wantPort, gotPort)
		})
	}
}

func TestAgent_SidecarDefaultChecks(t *testing.T) {
	tests := []struct {
		name                 string
		serviceID            string
		svcAddress           string
		proxyLocalSvcAddress string
		port                 int
		wantChecks           []*structs.CheckType
	}{{
		name:                 "uses proxy address for check",
		serviceID:            "web1-1-sidecar-proxy",
		svcAddress:           "123.123.123.123",
		proxyLocalSvcAddress: "255.255.255.255",
		port:                 2222,
		wantChecks: []*structs.CheckType{
			{
				Name:     "Connect Sidecar Listening",
				TCP:      "123.123.123.123:2222",
				Interval: 10 * time.Second,
			},
			{
				Name:         "Connect Sidecar Aliasing web1-1",
				AliasService: "web1-1",
			},
		},
	},
		{
			name:                 "uses proxy.local_service_address for check if proxy address is empty",
			serviceID:            "web1-1-sidecar-proxy",
			proxyLocalSvcAddress: "1.2.3.4",
			port:                 2222,
			wantChecks: []*structs.CheckType{
				{
					Name:     "Connect Sidecar Listening",
					TCP:      "1.2.3.4:2222",
					Interval: 10 * time.Second,
				},
				{
					Name:         "Connect Sidecar Aliasing web1-1",
					AliasService: "web1-1",
				},
			},
		},
		{
			name:                 "redundant name",
			serviceID:            "1-sidecar-proxy-web1-sidecar-proxy",
			svcAddress:           "123.123.123.123",
			proxyLocalSvcAddress: "255.255.255.255",
			port:                 2222,
			wantChecks: []*structs.CheckType{
				{
					Name:     "Connect Sidecar Listening",
					TCP:      "123.123.123.123:2222",
					Interval: 10 * time.Second,
				},
				{
					Name:         "Connect Sidecar Aliasing 1-sidecar-proxy-web1",
					AliasService: "1-sidecar-proxy-web1",
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotChecks := sidecarDefaultChecks(tt.serviceID, tt.svcAddress, tt.proxyLocalSvcAddress, tt.port)
			require.Equal(t, tt.wantChecks, gotChecks)
		})
	}
}
<|endoftext|>


<commit_before>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
package rpc

import (
	"encoding/json"
	"fmt"
	"math/big"
	"reflect"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/state"
	"github.com/ethereum/go-ethereum/xeth"
)

var rpclogger = logger.NewLogger("RPC")

// Unmarshal state is a helper method which has the ability to decode messsages
// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter)
// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction
// message and the second one refers to the block height (or state) to which to apply this `call`.
func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error) {
	var data []json.RawMessage
	if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 {
		return NewDecodeParamError(err.Error())
	}

	// Hrm... Occurs when no params
	if len(data) == 0 {
		return NewDecodeParamError("No data")
	}

	// Number index determines the index in the array for a possible block number
	numberIndex := 0

	value := reflect.ValueOf(iface)
	rvalue := reflect.Indirect(value)

	switch rvalue.Kind() {
	case reflect.Slice:
		// This is a bit of a cheat, but `data` is expected to be larger than 2 if iface is a slice
		if number != nil {
			numberIndex = len(data) - 1
		} else {
			numberIndex = len(data)
		}

		slice := reflect.MakeSlice(rvalue.Type(), numberIndex, numberIndex)
		for i, raw := range data[0:numberIndex] {
			v := slice.Index(i).Interface()
			if err = json.Unmarshal(raw, &v); err != nil {
				fmt.Println(err, v)
				return err
			}
			slice.Index(i).Set(reflect.ValueOf(v))
		}
		reflect.Indirect(rvalue).Set(slice) //value.Set(slice)
	case reflect.Struct:
		fallthrough
	default:
		if err = json.Unmarshal(data[0], iface); err != nil {
			return NewDecodeParamError(err.Error())
		}
		numberIndex = 1
	}

	// <0 index means out of bound for block number
	if numberIndex >= 0 && len(data) > numberIndex {
		if err = blockNumber(data[numberIndex], number); err != nil {
			return NewDecodeParamError(err.Error())
		}
	}

	return nil
}

func i2hex(n int) string {
	return common.ToHex(big.NewInt(int64(n)).Bytes())
}

type RpcServer interface {
	Start()
	Stop()
}

type Log struct {
	Address string   `json:"address"`
	Topic   []string `json:"topic"`
	Data    string   `json:"data"`
	Number  uint64   `json:"number"`
}

func toLogs(logs state.Logs) (ls []Log) {
	ls = make([]Log, len(logs))

	for i, log := range logs {
		var l Log
		l.Topic = make([]string, len(log.Topics()))
		l.Address = common.ToHex(log.Address())
		l.Data = common.ToHex(log.Data())
		l.Number = log.Number()
		for j, topic := range log.Topics() {
			l.Topic[j] = common.ToHex(topic)
		}
		ls[i] = l
	}

	return
}

type whisperFilter struct {
	messages []xeth.WhisperMessage
	timeout  time.Time
	id       int
}

func (w *whisperFilter) add(msgs ...xeth.WhisperMessage) {
	w.messages = append(w.messages, msgs...)
}
func (w *whisperFilter) get() []xeth.WhisperMessage {
	w.timeout = time.Now()
	tmp := w.messages
	w.messages = nil
	return tmp
}

type logFilter struct {
	logs    state.Logs
	timeout time.Time
	id      int
}

func (l *logFilter) add(logs ...state.Log) {
	l.logs = append(l.logs, logs...)
}

func (l *logFilter) get() state.Logs {
	l.timeout = time.Now()
	tmp := l.logs
	l.logs = nil
	return tmp
}
<commit_msg>Remove RpcServer<commit_after>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
package rpc

import (
	"encoding/json"
	"fmt"
	"math/big"
	"reflect"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/state"
	"github.com/ethereum/go-ethereum/xeth"
)

var rpclogger = logger.NewLogger("RPC")

// Unmarshal state is a helper method which has the ability to decode messsages
// that use the `defaultBlock` (https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter)
// For example a `call`: [{to: "0x....", data:"0x..."}, "latest"]. The first argument is the transaction
// message and the second one refers to the block height (or state) to which to apply this `call`.
func UnmarshalRawMessages(b []byte, iface interface{}, number *int64) (err error) {
	var data []json.RawMessage
	if err = json.Unmarshal(b, &data); err != nil && len(data) == 0 {
		return NewDecodeParamError(err.Error())
	}

	// Hrm... Occurs when no params
	if len(data) == 0 {
		return NewDecodeParamError("No data")
	}

	// Number index determines the index in the array for a possible block number
	numberIndex := 0

	value := reflect.ValueOf(iface)
	rvalue := reflect.Indirect(value)

	switch rvalue.Kind() {
	case reflect.Slice:
		// This is a bit of a cheat, but `data` is expected to be larger than 2 if iface is a slice
		if number != nil {
			numberIndex = len(data) - 1
		} else {
			numberIndex = len(data)
		}

		slice := reflect.MakeSlice(rvalue.Type(), numberIndex, numberIndex)
		for i, raw := range data[0:numberIndex] {
			v := slice.Index(i).Interface()
			if err = json.Unmarshal(raw, &v); err != nil {
				fmt.Println(err, v)
				return err
			}
			slice.Index(i).Set(reflect.ValueOf(v))
		}
		reflect.Indirect(rvalue).Set(slice) //value.Set(slice)
	case reflect.Struct:
		fallthrough
	default:
		if err = json.Unmarshal(data[0], iface); err != nil {
			return NewDecodeParamError(err.Error())
		}
		numberIndex = 1
	}

	// <0 index means out of bound for block number
	if numberIndex >= 0 && len(data) > numberIndex {
		if err = blockNumber(data[numberIndex], number); err != nil {
			return NewDecodeParamError(err.Error())
		}
	}

	return nil
}

func i2hex(n int) string {
	return common.ToHex(big.NewInt(int64(n)).Bytes())
}

type Log struct {
	Address string   `json:"address"`
	Topic   []string `json:"topic"`
	Data    string   `json:"data"`
	Number  uint64   `json:"number"`
}

func toLogs(logs state.Logs) (ls []Log) {
	ls = make([]Log, len(logs))

	for i, log := range logs {
		var l Log
		l.Topic = make([]string, len(log.Topics()))
		l.Address = common.ToHex(log.Address())
		l.Data = common.ToHex(log.Data())
		l.Number = log.Number()
		for j, topic := range log.Topics() {
			l.Topic[j] = common.ToHex(topic)
		}
		ls[i] = l
	}

	return
}

type whisperFilter struct {
	messages []xeth.WhisperMessage
	timeout  time.Time
	id       int
}

func (w *whisperFilter) add(msgs ...xeth.WhisperMessage) {
	w.messages = append(w.messages, msgs...)
}
func (w *whisperFilter) get() []xeth.WhisperMessage {
	w.timeout = time.Now()
	tmp := w.messages
	w.messages = nil
	return tmp
}

type logFilter struct {
	logs    state.Logs
	timeout time.Time
	id      int
}

func (l *logFilter) add(logs ...state.Log) {
	l.logs = append(l.logs, logs...)
}

func (l *logFilter) get() state.Logs {
	l.timeout = time.Now()
	tmp := l.logs
	l.logs = nil
	return tmp
}
<|endoftext|>


<commit_before>package logger

import (
	"fmt"
	"os"
)

import "github.com/jarod/log4go"

type LoggerConfig struct {
	Level  string
	Format string
	File   string
}

type Logger struct {
	log4go log4go.Logger
}

var loggerInstance *Logger = nil

func init() {
	config := LoggerConfig{Format: "stdout", Level: "debug"}
	NewLogger(&config)
}

func GetLogger() *Logger {
	return loggerInstance
}

func NewLogger(config *LoggerConfig) {
	logger := new(Logger)

	level := log4go.WARNING
	if config.Level == "debug" {
		level = log4go.DEBUG
	} else if config.Level == "info" {
		level = log4go.INFO
	}

	logger.log4go = log4go.NewDefaultLogger(level)

	if config.Format == "log" {
		logger.log4go.AddFilter("log", level, log4go.NewFileLogWriter(config.File, true))
	} else {
		logger.log4go.AddFilter("stdout", level, log4go.NewConsoleLogWriter())
	}

	loggerInstance = logger
}

/*
func (self *Logger) PrintWriterStats(elapsed int, writer Writer) {
	created, failed, transferred := writer.GetCounters()
	writer.ResetCounters()

	logFormat := "Created %d document(s), Failed %d times(s), %g"

	rate := float64(transferred) / 1000 / float64(elapsed)
	self.log4go.Info(fmt.Sprintf(logFormat, created, failed, rate))
}*/

func Info(log interface{}, args ...interface{}) {
	fmt.Println(log)
	loggerInstance.log4go.Info(log, args...)
}

func Debug(log interface{}, args ...interface{}) {
	fmt.Println(log)
	loggerInstance.log4go.Debug(log, args...)
}

func Warning(log interface{}, args ...interface{}) {
	fmt.Println(log)
	loggerInstance.log4go.Warn(log, args...)
}

func Error(log interface{}, args ...interface{}) {
	fmt.Println(log)
	loggerInstance.log4go.Error(log, args...)
}

func Critical(log interface{}, args ...interface{}) {
	fmt.Println(log)
	loggerInstance.log4go.Critical(log, args...)
	os.Exit(1)
}
<commit_msg>logger based on default log: wip<commit_after>package logger

import (
	"log"
	"os"
)

type LoggerConfig struct {
	Level  string
	Format string
	File   string
}

func init() {
	config := LoggerConfig{Format: "stdout", Level: "debug"}
	NewLogger(&config)
}

func NewLogger(config *LoggerConfig) {
	if config.Level == "debug" {
		log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
	} else {
		log.SetFlags(log.Ldate | log.Ltime)
	}

	if config.Format == "log" {
		f, err := os.OpenFile(config.File, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			Critical("error opening file: %v", err)
		}

		log.SetOutput(f)
		defer f.Close()
	}
}

/*
func (self *Logger) PrintWriterStats(elapsed int, writer Writer) {
	created, failed, transferred := writer.GetCounters()
	writer.ResetCounters()

	logFormat := "Created %d document(s), Failed %d times(s), %g"

	rate := float64(transferred) / 1000 / float64(elapsed)
	self.log4go.Info(fmt.Sprintf(logFormat, created, failed, rate))
}*/

func Debug(line string, args ...interface{}) {
	log.Printf(formatLogLine("DEBG", line), args...)
}

func Info(line string, args ...interface{}) {
	log.Printf(formatLogLine("INFO", line), args...)
}

func Warning(line string, args ...interface{}) {
	log.Printf(formatLogLine("WARN", line), args...)
}

func Error(line string, args ...interface{}) {
	log.Printf(formatLogLine("ERRO", line), args...)
}

func Critical(line string, args ...interface{}) {
	log.Fatalf(formatLogLine("CRIT", line), args...)
}

func formatLogLine(level string, line string) string {
	return level + ": " + line
}
<|endoftext|>