<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/fsouza/go-dockerclient"
	"github.com/pwaller/barrier"
)

type Container struct {
	Name      string
	Args, Env []string

	client    *docker.Client
	container *docker.Container

	Failed, Superceded, Ready, Closing barrier.Barrier

	wg *sync.WaitGroup

	Errors  <-chan error
	errorsW chan<- error
}

type SourceType int

const (
	// Run build with current directory with context
	BuildCwd            SourceType = iota
	BuildTarballContent            // Build with specified io.Reader as context
	BuildTarballURL                // Build with specified remote URL as context
	DockerPull                     // Run a docker pull to obtain the image
)

type ContainerSource struct {
	Type                SourceType
	buildTarballContent io.Reader
	buildTarballURL     string
	dockerImageName     string
}

// Construct a *Container. When the `wg` WaitGroup is zero, there is nothing
// outstanding (such as firewall rules which need garbage collecting).
func NewContainer(client *docker.Client, name string, wg *sync.WaitGroup) *Container {

	errors := make(chan error)

	c := &Container{
		Name:    name,
		client:  client,
		wg:      wg,
		Errors:  errors,
		errorsW: errors,
	}

	// If the container fails we should assume it should be torn down.
	c.Failed.Forward(&c.Closing)

	return c
}

// Generate a docker image. This can be done through various mechanisms in
// response to an UpdateEvent (see SourceType constant declarations).
func (c *Container) Build(config UpdateEvent) error {
	if config.BuildComplete != nil {
		defer close(config.BuildComplete)
	}

	var err error
	bo := docker.BuildImageOptions{}
	bo.Name = c.Name
	bo.OutputStream = config.OutputStream
	if bo.OutputStream == nil {
		bo.OutputStream = os.Stderr
	}

	switch config.Source.Type {
	case BuildCwd:
		bo.ContextDir, err = os.Getwd()
		if err != nil {
			return err
		}
	case BuildTarballContent:
		bo.InputStream = config.Source.buildTarballContent
	default:
		return fmt.Errorf("Unimplemented ContainerSource: %v", config.Source.Type)
	}

	return c.client.BuildImage(bo)
}

// Pull an image from a docker repository.
func (c *Container) Pull(config UpdateEvent) error {
	if config.BuildComplete != nil {
		defer close(config.BuildComplete)
	}

	pio := docker.PullImageOptions{}
	pio.Repository = config.Source.dockerImageName
	pio.Registry = ""
	pio.Tag = "latest"
	pio.OutputStream = config.OutputStream
	if pio.OutputStream == nil {
		pio.OutputStream = os.Stderr
	}
	pio.RawJSONStream = false

	return c.client.PullImage(pio, docker.AuthConfiguration{})
}

// `docker create` the container.
func (c *Container) Create(source ContainerSource) error {
	opts := docker.CreateContainerOptions{
		Name: c.Name,
		Config: &docker.Config{
			Hostname:     c.Name,
			AttachStdout: true,
			AttachStderr: true,
			Env:          c.Env,
			Cmd:          c.Args,
		},
	}

	switch source.Type {
	case DockerPull:
		opts.Config.Image = source.dockerImageName
	case BuildCwd, BuildTarballContent:
		opts.Config.Image = c.Name
	}

	var err error
	c.container, err = c.client.CreateContainer(opts)

	return err
}

// CopyOutput copies the output of the container to `w` and blocks until
// completion
func (c *Container) CopyOutput() error {

	// TODO(pwaller): at some point move this on to 'c' for configurability?
	w := os.Stderr
	// Blocks until stream closed
	return c.client.AttachToContainer(docker.AttachToContainerOptions{
		Container:    c.container.ID,
		OutputStream: w,
		ErrorStream:  w,
		Logs:         true,
		Stdout:       true,
		Stderr:       true,
		Stream:       true,
	})
}

// :todo(drj): May want to return errors for truly broken containers (timeout).
// Poll for the program inside the container being ready to accept connections
// Returns `true` for success and `false` for failure.
func (c *Container) AwaitListening() bool {

	for _, port := range c.container.NetworkSettings.PortMappingAPI() {
		url := fmt.Sprint("http://", port.IP, ":", port.PublicPort, "/")
		for {
			response, err := http.Get(url)
			if err == nil && response.StatusCode == http.StatusOK {
				return true
			}
			time.Sleep(50 * time.Millisecond)

			select {
			case <-c.Closing.Barrier():
				// If the container has closed, cease waiting
				return false
			default:
			}
		}
	}

	return false
}

// Start the container (and notify it if c.Closing falls)
func (c *Container) Start() error {
	hc := &docker.HostConfig{
		PublishAllPorts: true,
	}
	err := c.client.StartContainer(c.container.ID, hc)
	if err != nil {
		return err
	}

	// Load container.NetworkSettings
	c.container, err = c.client.InspectContainer(c.container.ID)
	if err != nil {
		return err
	}

	// Listen on the Closing barrier and send a kill to the container if it
	// falls.
	c.wg.Add(1)
	go func() {
		defer c.wg.Done()
		<-c.Closing.Barrier()
		// If the container is signaled to close, send a kill signal
		err := c.client.KillContainer(docker.KillContainerOptions{
			ID: c.container.ID,
		})
		if err == nil {
			return
		}
		switch err := err.(type) {
		case *docker.NoSuchContainer:
			// The container already went away, who cares.
			return
		default:
			log.Println("Killing container failed:", c.container.ID, err)
		}
	}()
	return nil
}

// Wait until container exits
func (c *Container) Wait() (int, error) {
	return c.client.WaitContainer(c.container.ID)
}

// Internal function for raising an error.
func (c *Container) err(err error) {
	c.errorsW <- err
	c.Closing.Fall()
}

// Manage the whole lifecycle of the container in response to a request to
// start it.
func (c *Container) Run(event UpdateEvent) (int, error) {

	defer c.Closing.Fall()
	defer close(c.errorsW)

	switch event.Source.Type {
	case DockerPull:
		err := c.Pull(event)
		if err != nil {
			return -2, err
		}
	case BuildTarballContent, BuildCwd:
		err := c.Build(event)
		if err != nil {
			return -2, err
		}
	}

	err := c.Create(event.Source)
	if err != nil {
		return -1, err
	}
	defer c.Delete()

	c.wg.Add(1)
	go func() {
		defer c.wg.Done()
		err := c.CopyOutput()
		if err != nil {
			c.err(err)
		}
	}()

	err = c.Start()
	if err != nil {
		return -1, err
	}

	go func() {
		if !c.AwaitListening() {
			c.Failed.Fall()
			return
		}
		c.Ready.Fall()
	}()

	return c.Wait()
}

func (c *Container) Delete() {
	err := c.client.RemoveContainer(docker.RemoveContainerOptions{
		ID:            c.container.ID,
		RemoveVolumes: true,
		Force:         true,
	})
	if err != nil {
		log.Println("Warn: failed to delete container:", err)
	}
}
<commit_msg>Fix for file descriptor exhaustion<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/fsouza/go-dockerclient"
	"github.com/pwaller/barrier"
)

type Container struct {
	Name      string
	Args, Env []string

	client    *docker.Client
	container *docker.Container

	Failed, Superceded, Ready, Closing barrier.Barrier

	wg *sync.WaitGroup

	Errors  <-chan error
	errorsW chan<- error
}

type SourceType int

const (
	// Run build with current directory with context
	BuildCwd            SourceType = iota
	BuildTarballContent            // Build with specified io.Reader as context
	BuildTarballURL                // Build with specified remote URL as context
	DockerPull                     // Run a docker pull to obtain the image
)

type ContainerSource struct {
	Type                SourceType
	buildTarballContent io.Reader
	buildTarballURL     string
	dockerImageName     string
}

// Construct a *Container. When the `wg` WaitGroup is zero, there is nothing
// outstanding (such as firewall rules which need garbage collecting).
func NewContainer(client *docker.Client, name string, wg *sync.WaitGroup) *Container {

	errors := make(chan error)

	c := &Container{
		Name:    name,
		client:  client,
		wg:      wg,
		Errors:  errors,
		errorsW: errors,
	}

	// If the container fails we should assume it should be torn down.
	c.Failed.Forward(&c.Closing)

	return c
}

// Generate a docker image. This can be done through various mechanisms in
// response to an UpdateEvent (see SourceType constant declarations).
func (c *Container) Build(config UpdateEvent) error {
	if config.BuildComplete != nil {
		defer close(config.BuildComplete)
	}

	var err error
	bo := docker.BuildImageOptions{}
	bo.Name = c.Name
	bo.OutputStream = config.OutputStream
	if bo.OutputStream == nil {
		bo.OutputStream = os.Stderr
	}

	switch config.Source.Type {
	case BuildCwd:
		bo.ContextDir, err = os.Getwd()
		if err != nil {
			return err
		}
	case BuildTarballContent:
		bo.InputStream = config.Source.buildTarballContent
	default:
		return fmt.Errorf("Unimplemented ContainerSource: %v", config.Source.Type)
	}

	return c.client.BuildImage(bo)
}

// Pull an image from a docker repository.
func (c *Container) Pull(config UpdateEvent) error {
	if config.BuildComplete != nil {
		defer close(config.BuildComplete)
	}

	pio := docker.PullImageOptions{}
	pio.Repository = config.Source.dockerImageName
	pio.Registry = ""
	pio.Tag = "latest"
	pio.OutputStream = config.OutputStream
	if pio.OutputStream == nil {
		pio.OutputStream = os.Stderr
	}
	pio.RawJSONStream = false

	return c.client.PullImage(pio, docker.AuthConfiguration{})
}

// `docker create` the container.
func (c *Container) Create(source ContainerSource) error {
	opts := docker.CreateContainerOptions{
		Name: c.Name,
		Config: &docker.Config{
			Hostname:     c.Name,
			AttachStdout: true,
			AttachStderr: true,
			Env:          c.Env,
			Cmd:          c.Args,
		},
	}

	switch source.Type {
	case DockerPull:
		opts.Config.Image = source.dockerImageName
	case BuildCwd, BuildTarballContent:
		opts.Config.Image = c.Name
	}

	var err error
	c.container, err = c.client.CreateContainer(opts)

	return err
}

// CopyOutput copies the output of the container to `w` and blocks until
// completion
func (c *Container) CopyOutput() error {

	// TODO(pwaller): at some point move this on to 'c' for configurability?
	w := os.Stderr
	// Blocks until stream closed
	return c.client.AttachToContainer(docker.AttachToContainerOptions{
		Container:    c.container.ID,
		OutputStream: w,
		ErrorStream:  w,
		Logs:         true,
		Stdout:       true,
		Stderr:       true,
		Stream:       true,
	})
}

// :todo(drj): May want to return errors for truly broken containers (timeout).
// Poll for the program inside the container being ready to accept connections
// Returns `true` for success and `false` for failure.
func (c *Container) AwaitListening() bool {

	for _, port := range c.container.NetworkSettings.PortMappingAPI() {
		url := fmt.Sprint("http://", port.IP, ":", port.PublicPort, "/")
		for {
			response, err := http.Get(url)
			if response != nil && response.Body != nil {
				response.Body.Close()
			}
			if err == nil && response.StatusCode == http.StatusOK {
				return true
			}
			time.Sleep(50 * time.Millisecond)

			select {
			case <-c.Closing.Barrier():
				// If the container has closed, cease waiting
				return false
			default:
			}
		}
	}

	return false
}

// Start the container (and notify it if c.Closing falls)
func (c *Container) Start() error {
	hc := &docker.HostConfig{
		PublishAllPorts: true,
	}
	err := c.client.StartContainer(c.container.ID, hc)
	if err != nil {
		return err
	}

	// Load container.NetworkSettings
	c.container, err = c.client.InspectContainer(c.container.ID)
	if err != nil {
		return err
	}

	// Listen on the Closing barrier and send a kill to the container if it
	// falls.
	c.wg.Add(1)
	go func() {
		defer c.wg.Done()
		<-c.Closing.Barrier()
		// If the container is signaled to close, send a kill signal
		err := c.client.KillContainer(docker.KillContainerOptions{
			ID: c.container.ID,
		})
		if err == nil {
			return
		}
		switch err := err.(type) {
		case *docker.NoSuchContainer:
			// The container already went away, who cares.
			return
		default:
			log.Println("Killing container failed:", c.container.ID, err)
		}
	}()
	return nil
}

// Wait until container exits
func (c *Container) Wait() (int, error) {
	return c.client.WaitContainer(c.container.ID)
}

// Internal function for raising an error.
func (c *Container) err(err error) {
	c.errorsW <- err
	c.Closing.Fall()
}

// Manage the whole lifecycle of the container in response to a request to
// start it.
func (c *Container) Run(event UpdateEvent) (int, error) {

	defer c.Closing.Fall()
	defer close(c.errorsW)

	switch event.Source.Type {
	case DockerPull:
		err := c.Pull(event)
		if err != nil {
			return -2, err
		}
	case BuildTarballContent, BuildCwd:
		err := c.Build(event)
		if err != nil {
			return -2, err
		}
	}

	err := c.Create(event.Source)
	if err != nil {
		return -1, err
	}
	defer c.Delete()

	c.wg.Add(1)
	go func() {
		defer c.wg.Done()
		err := c.CopyOutput()
		if err != nil {
			c.err(err)
		}
	}()

	err = c.Start()
	if err != nil {
		return -1, err
	}

	go func() {
		if !c.AwaitListening() {
			c.Failed.Fall()
			return
		}
		c.Ready.Fall()
	}()

	return c.Wait()
}

func (c *Container) Delete() {
	err := c.client.RemoveContainer(docker.RemoveContainerOptions{
		ID:            c.container.ID,
		RemoveVolumes: true,
		Force:         true,
	})
	if err != nil {
		log.Println("Warn: failed to delete container:", err)
	}
}
<|endoftext|>


<commit_before>package restful

// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strings"
	"sync"

	"github.com/emicklei/go-restful/log"
)

// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
// The requests are further dispatched to routes of WebServices using a RouteSelector
type Container struct {
	webServicesLock        sync.RWMutex
	webServices            []*WebService
	ServeMux               *http.ServeMux
	isRegisteredOnRoot     bool
	containerFilters       []FilterFunction
	doNotRecover           bool // default is false
	recoverHandleFunc      RecoverHandleFunction
	serviceErrorHandleFunc ServiceErrorHandleFunction
	router                 RouteSelector // default is a RouterJSR311, CurlyRouter is the faster alternative
	contentEncodingEnabled bool          // default is false
}

// NewContainer creates a new Container using a new ServeMux and default router (RouterJSR311)
func NewContainer() *Container {
	return &Container{
		webServices:            []*WebService{},
		ServeMux:               http.NewServeMux(),
		isRegisteredOnRoot:     false,
		containerFilters:       []FilterFunction{},
		doNotRecover:           false,
		recoverHandleFunc:      logStackOnRecover,
		serviceErrorHandleFunc: writeServiceError,
		router:                 RouterJSR311{},
		contentEncodingEnabled: false}
}

// RecoverHandleFunction declares functions that can be used to handle a panic situation.
// The first argument is what recover() returns. The second must be used to communicate an error response.
type RecoverHandleFunction func(interface{}, http.ResponseWriter)

// RecoverHandler changes the default function (logStackOnRecover) to be called
// when a panic is detected. DoNotRecover must be have its default value (=false).
func (c *Container) RecoverHandler(handler RecoverHandleFunction) {
	c.recoverHandleFunc = handler
}

// ServiceErrorHandleFunction declares functions that can be used to handle a service error situation.
// The first argument is the service error, the second is the request that resulted in the error and
// the third must be used to communicate an error response.
type ServiceErrorHandleFunction func(ServiceError, *Request, *Response)

// ServiceErrorHandler changes the default function (writeServiceError) to be called
// when a ServiceError is detected.
func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) {
	c.serviceErrorHandleFunc = handler
}

// DoNotRecover controls whether panics will be caught to return HTTP 500.
// If set to true, Route functions are responsible for handling any error situation.
// Default value is false = recover from panics. This has performance implications.
func (c *Container) DoNotRecover(doNot bool) {
	c.doNotRecover = doNot
}

// Router changes the default Router (currently RouterJSR311)
func (c *Container) Router(aRouter RouteSelector) {
	c.router = aRouter
}

// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses.
func (c *Container) EnableContentEncoding(enabled bool) {
	c.contentEncodingEnabled = enabled
}

// Add a WebService to the Container. It will detect duplicate root paths and panic in that case.
func (c *Container) Add(service *WebService) *Container {
	c.webServicesLock.Lock()
	defer c.webServicesLock.Unlock()

	// if rootPath was not set then lazy initialize it
	if len(service.rootPath) == 0 {
		service.Path("/")
	}

	// cannot have duplicate root paths
	for _, each := range c.webServices {
		if each.RootPath() == service.RootPath() {
			log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)
			os.Exit(1)
		}
	}

	// If not registered on root then add specific mapping
	if !c.isRegisteredOnRoot {
		c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux)
	}
	c.webServices = append(c.webServices, service)
	return c
}

// addHandler may set a new HandleFunc for the serveMux
// this function must run insige the critical region protected by the webServicesLock.
// returns true if the function was registered on root ("/")
func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) bool {
	rootRegistered := false
	pattern := fixedPrefixPath(service.RootPath())
	// check if root path registration is needed
	if "/" == pattern || "" == pattern {
		serveMux.HandleFunc("/", c.dispatch)
		rootRegistered = true
	} else {
		// detect if registration already exists
		alreadyMapped := false
		for _, each := range c.webServices {
			if each.RootPath() == service.RootPath() {
				alreadyMapped = true
				break
			}
		}
		if !alreadyMapped {
			serveMux.HandleFunc(pattern, c.dispatch)
			if !strings.HasSuffix(pattern, "/") {
				serveMux.HandleFunc(pattern+"/", c.dispatch)
			}
		}
	}
	return rootRegistered
}

func (c *Container) Remove(ws *WebService) error {
	if c.ServeMux == http.DefaultServeMux {
		errMsg := fmt.Sprintf("[restful] cannot remove a WebService from a DefaultContainer:['%v']", ws)
		log.Printf(errMsg)
		return errors.New(errMsg)
	}
	c.webServicesLock.Lock()
	defer c.webServicesLock.Unlock()
	// build a new ServeMux and re-register all WebServices
	newServeMux := http.NewServeMux()
	newServices := []*WebService{}
	newIsRegisteredOnRoot := false
	for ix := range c.webServices {
		each := c.webServices[ix]
		if each.rootPath != ws.rootPath {
			// If not registered on root then add specific mapping
			if !newIsRegisteredOnRoot {
				newIsRegisteredOnRoot = c.addHandler(each, newServeMux)
			}
			newServices = append(newServices, each)
		}
	}
	c.webServices = newServices
	c.ServeMux = newServeMux
	c.isRegisteredOnRoot = newIsRegisteredOnRoot
	return nil
}

// logStackOnRecover is the default RecoverHandleFunction and is called
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
// Default implementation logs the stacktrace and writes the stacktrace on the response.
// This may be a security issue as it exposes sourcecode information.
func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("[restful] recover from panic situation: - %v\r\n", panicReason))
	for i := 2; ; i += 1 {
		_, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		buffer.WriteString(fmt.Sprintf("    %s:%d\r\n", file, line))
	}
	log.Print(buffer.String())
	httpWriter.WriteHeader(http.StatusInternalServerError)
	httpWriter.Write(buffer.Bytes())
}

// writeServiceError is the default ServiceErrorHandleFunction and is called
// when a ServiceError is returned during route selection. Default implementation
// calls resp.WriteErrorString(err.Code, err.Message)
func writeServiceError(err ServiceError, req *Request, resp *Response) {
	resp.WriteErrorString(err.Code, err.Message)
}

// Dispatch the incoming Http Request to a matching WebService.
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
	writer := httpWriter

	// CompressingResponseWriter should be closed after all operations are done
	defer func() {
		if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
			compressWriter.Close()
		}
	}()

	// Instal panic recovery unless told otherwise
	if !c.doNotRecover { // catch all for 500 response
		defer func() {
			if r := recover(); r != nil {
				c.recoverHandleFunc(r, writer)
				return
			}
		}()
	}
	// Install closing the request body (if any)
	defer func() {
		if nil != httpRequest.Body {
			httpRequest.Body.Close()
		}
	}()

	// Detect if compression is needed
	// assume without compression, test for override
	if c.contentEncodingEnabled {
		doCompress, encoding := wantsCompressedResponse(httpRequest)
		if doCompress {
			var err error
			writer, err = NewCompressingResponseWriter(httpWriter, encoding)
			if err != nil {
				log.Print("[restful] unable to install compressor: ", err)
				httpWriter.WriteHeader(http.StatusInternalServerError)
				return
			}
		}
	}
	// Find best match Route ; err is non nil if no match was found
	var webService *WebService
	var route *Route
	var err error
	func() {
		c.webServicesLock.RLock()
		defer c.webServicesLock.RUnlock()
		webService, route, err = c.router.SelectRoute(
			c.webServices,
			httpRequest)
	}()
	if err != nil {
		// a non-200 response has already been written
		// run container filters anyway ; they should not touch the response...
		chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
			switch err.(type) {
			case ServiceError:
				ser := err.(ServiceError)
				c.serviceErrorHandleFunc(ser, req, resp)
			}
			// TODO
		}}
		chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
		return
	}
	wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest)
	// pass through filters (if any)
	if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
		// compose filter chain
		allFilters := []FilterFunction{}
		allFilters = append(allFilters, c.containerFilters...)
		allFilters = append(allFilters, webService.filters...)
		allFilters = append(allFilters, route.Filters...)
		chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {
			// handle request by route after passing all filters
			route.Function(wrappedRequest, wrappedResponse)
		}}
		chain.ProcessFilter(wrappedRequest, wrappedResponse)
	} else {
		// no filters, handle request by route
		route.Function(wrappedRequest, wrappedResponse)
	}
}

// fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {}
func fixedPrefixPath(pathspec string) string {
	varBegin := strings.Index(pathspec, "{")
	if -1 == varBegin {
		return pathspec
	}
	return pathspec[:varBegin]
}

// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
func (c Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
	c.ServeMux.ServeHTTP(httpwriter, httpRequest)
}

// Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func (c Container) Handle(pattern string, handler http.Handler) {
	c.ServeMux.Handle(pattern, handler)
}

// HandleWithFilter registers the handler for the given pattern.
// Container's filter chain is applied for handler.
// If a handler already exists for pattern, HandleWithFilter panics.
func (c *Container) HandleWithFilter(pattern string, handler http.Handler) {
	f := func(httpResponse http.ResponseWriter, httpRequest *http.Request) {
		if len(c.containerFilters) == 0 {
			handler.ServeHTTP(httpResponse, httpRequest)
			return
		}

		chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
			handler.ServeHTTP(httpResponse, httpRequest)
		}}
		chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
	}

	c.Handle(pattern, http.HandlerFunc(f))
}

// Filter appends a container FilterFunction. These are called before dispatching
// a http.Request to a WebService from the container
func (c *Container) Filter(filter FilterFunction) {
	c.containerFilters = append(c.containerFilters, filter)
}

// RegisteredWebServices returns the collections of added WebServices
func (c Container) RegisteredWebServices() []*WebService {
	c.webServicesLock.RLock()
	defer c.webServicesLock.RUnlock()
	result := make([]*WebService, len(c.webServices))
	for ix := range c.webServices {
		result[ix] = c.webServices[ix]
	}
	return result
}

// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
func (c Container) computeAllowedMethods(req *Request) []string {
	// Go through all RegisteredWebServices() and all its Routes to collect the options
	methods := []string{}
	requestPath := req.Request.URL.Path
	for _, ws := range c.RegisteredWebServices() {
		matches := ws.pathExpr.Matcher.FindStringSubmatch(requestPath)
		if matches != nil {
			finalMatch := matches[len(matches)-1]
			for _, rt := range ws.Routes() {
				matches := rt.pathExpr.Matcher.FindStringSubmatch(finalMatch)
				if matches != nil {
					lastMatch := matches[len(matches)-1]
					if lastMatch == "" || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
						methods = append(methods, rt.Method)
					}
				}
			}
		}
	}
	// methods = append(methods, "OPTIONS")  not sure about this
	return methods
}

// newBasicRequestResponse creates a pair of Request,Response from its http versions.
// It is basic because no parameter or (produces) content-type information is given.
func newBasicRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
	resp := NewResponse(httpWriter)
	resp.requestAccept = httpRequest.Header.Get(HEADER_Accept)
	return NewRequest(httpRequest), resp
}
<commit_msg>clean up container funcs<commit_after>package restful

// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strings"
	"sync"

	"github.com/emicklei/go-restful/log"
)

// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
// The requests are further dispatched to routes of WebServices using a RouteSelector
type Container struct {
	webServicesLock        sync.RWMutex
	webServices            []*WebService
	ServeMux               *http.ServeMux
	isRegisteredOnRoot     bool
	containerFilters       []FilterFunction
	doNotRecover           bool // default is false
	recoverHandleFunc      RecoverHandleFunction
	serviceErrorHandleFunc ServiceErrorHandleFunction
	router                 RouteSelector // default is a RouterJSR311, CurlyRouter is the faster alternative
	contentEncodingEnabled bool          // default is false
}

// NewContainer creates a new Container using a new ServeMux and default router (RouterJSR311)
func NewContainer() *Container {
	return &Container{
		webServices:            []*WebService{},
		ServeMux:               http.NewServeMux(),
		isRegisteredOnRoot:     false,
		containerFilters:       []FilterFunction{},
		doNotRecover:           false,
		recoverHandleFunc:      logStackOnRecover,
		serviceErrorHandleFunc: writeServiceError,
		router:                 RouterJSR311{},
		contentEncodingEnabled: false}
}

// RecoverHandleFunction declares functions that can be used to handle a panic situation.
// The first argument is what recover() returns. The second must be used to communicate an error response.
type RecoverHandleFunction func(interface{}, http.ResponseWriter)

// RecoverHandler changes the default function (logStackOnRecover) to be called
// when a panic is detected. DoNotRecover must be have its default value (=false).
func (c *Container) RecoverHandler(handler RecoverHandleFunction) {
	c.recoverHandleFunc = handler
}

// ServiceErrorHandleFunction declares functions that can be used to handle a service error situation.
// The first argument is the service error, the second is the request that resulted in the error and
// the third must be used to communicate an error response.
type ServiceErrorHandleFunction func(ServiceError, *Request, *Response)

// ServiceErrorHandler changes the default function (writeServiceError) to be called
// when a ServiceError is detected.
func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) {
	c.serviceErrorHandleFunc = handler
}

// DoNotRecover controls whether panics will be caught to return HTTP 500.
// If set to true, Route functions are responsible for handling any error situation.
// Default value is false = recover from panics. This has performance implications.
func (c *Container) DoNotRecover(doNot bool) {
	c.doNotRecover = doNot
}

// Router changes the default Router (currently RouterJSR311)
func (c *Container) Router(aRouter RouteSelector) {
	c.router = aRouter
}

// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses.
func (c *Container) EnableContentEncoding(enabled bool) {
	c.contentEncodingEnabled = enabled
}

// Add a WebService to the Container. It will detect duplicate root paths and exit in that case.
func (c *Container) Add(service *WebService) *Container {
	c.webServicesLock.Lock()
	defer c.webServicesLock.Unlock()

	// if rootPath was not set then lazy initialize it
	if len(service.rootPath) == 0 {
		service.Path("/")
	}

	// cannot have duplicate root paths
	for _, each := range c.webServices {
		if each.RootPath() == service.RootPath() {
			log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)
			os.Exit(1)
		}
	}

	// If not registered on root then add specific mapping
	if !c.isRegisteredOnRoot {
		c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux)
	}
	c.webServices = append(c.webServices, service)
	return c
}

// addHandler may set a new HandleFunc for the serveMux
// this function must run inside the critical region protected by the webServicesLock.
// returns true if the function was registered on root ("/")
func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) bool {
	pattern := fixedPrefixPath(service.RootPath())
	// check if root path registration is needed
	if "/" == pattern || "" == pattern {
		serveMux.HandleFunc("/", c.dispatch)
		return true
	}
	// detect if registration already exists
	alreadyMapped := false
	for _, each := range c.webServices {
		if each.RootPath() == service.RootPath() {
			alreadyMapped = true
			break
		}
	}
	if !alreadyMapped {
		serveMux.HandleFunc(pattern, c.dispatch)
		if !strings.HasSuffix(pattern, "/") {
			serveMux.HandleFunc(pattern+"/", c.dispatch)
		}
	}
	return false
}

func (c *Container) Remove(ws *WebService) error {
	if c.ServeMux == http.DefaultServeMux {
		errMsg := fmt.Sprintf("[restful] cannot remove a WebService from a Container using the DefaultServeMux: ['%v']", ws)
		log.Printf(errMsg)
		return errors.New(errMsg)
	}
	c.webServicesLock.Lock()
	defer c.webServicesLock.Unlock()
	// build a new ServeMux and re-register all WebServices
	newServeMux := http.NewServeMux()
	newServices := []*WebService{}
	newIsRegisteredOnRoot := false
	for _, each := range c.webServices {
		if each.rootPath != ws.rootPath {
			// If not registered on root then add specific mapping
			if !newIsRegisteredOnRoot {
				newIsRegisteredOnRoot = c.addHandler(each, newServeMux)
			}
			newServices = append(newServices, each)
		}
	}
	c.webServices, c.ServeMux, c.isRegisteredOnRoot = newServices, newServeMux, newIsRegisteredOnRoot
	return nil
}

// logStackOnRecover is the default RecoverHandleFunction and is called
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
// Default implementation logs the stacktrace and writes the stacktrace on the response.
// This may be a security issue as it exposes sourcecode information.
func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("[restful] recover from panic situation: - %v\r\n", panicReason))
	for i := 2; ; i += 1 {
		_, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		buffer.WriteString(fmt.Sprintf("    %s:%d\r\n", file, line))
	}
	log.Print(buffer.String())
	httpWriter.WriteHeader(http.StatusInternalServerError)
	httpWriter.Write(buffer.Bytes())
}

// writeServiceError is the default ServiceErrorHandleFunction and is called
// when a ServiceError is returned during route selection. Default implementation
// calls resp.WriteErrorString(err.Code, err.Message)
func writeServiceError(err ServiceError, req *Request, resp *Response) {
	resp.WriteErrorString(err.Code, err.Message)
}

// Dispatch the incoming Http Request to a matching WebService.
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
	writer := httpWriter

	// CompressingResponseWriter should be closed after all operations are done
	defer func() {
		if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
			compressWriter.Close()
		}
	}()

	// Instal panic recovery unless told otherwise
	if !c.doNotRecover { // catch all for 500 response
		defer func() {
			if r := recover(); r != nil {
				c.recoverHandleFunc(r, writer)
				return
			}
		}()
	}
	// Install closing the request body (if any)
	defer func() {
		if nil != httpRequest.Body {
			httpRequest.Body.Close()
		}
	}()

	// Detect if compression is needed
	// assume without compression, test for override
	if c.contentEncodingEnabled {
		doCompress, encoding := wantsCompressedResponse(httpRequest)
		if doCompress {
			var err error
			writer, err = NewCompressingResponseWriter(httpWriter, encoding)
			if err != nil {
				log.Print("[restful] unable to install compressor: ", err)
				httpWriter.WriteHeader(http.StatusInternalServerError)
				return
			}
		}
	}
	// Find best match Route ; err is non nil if no match was found
	var webService *WebService
	var route *Route
	var err error
	func() {
		c.webServicesLock.RLock()
		defer c.webServicesLock.RUnlock()
		webService, route, err = c.router.SelectRoute(
			c.webServices,
			httpRequest)
	}()
	if err != nil {
		// a non-200 response has already been written
		// run container filters anyway ; they should not touch the response...
		chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
			switch err.(type) {
			case ServiceError:
				ser := err.(ServiceError)
				c.serviceErrorHandleFunc(ser, req, resp)
			}
			// TODO
		}}
		chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
		return
	}
	wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest)
	// pass through filters (if any)
	if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
		// compose filter chain
		allFilters := []FilterFunction{}
		allFilters = append(allFilters, c.containerFilters...)
		allFilters = append(allFilters, webService.filters...)
		allFilters = append(allFilters, route.Filters...)
		chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {
			// handle request by route after passing all filters
			route.Function(wrappedRequest, wrappedResponse)
		}}
		chain.ProcessFilter(wrappedRequest, wrappedResponse)
	} else {
		// no filters, handle request by route
		route.Function(wrappedRequest, wrappedResponse)
	}
}

// fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {}
func fixedPrefixPath(pathspec string) string {
	varBegin := strings.Index(pathspec, "{")
	if -1 == varBegin {
		return pathspec
	}
	return pathspec[:varBegin]
}

// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
func (c Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
	c.ServeMux.ServeHTTP(httpwriter, httpRequest)
}

// Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func (c Container) Handle(pattern string, handler http.Handler) {
	c.ServeMux.Handle(pattern, handler)
}

// HandleWithFilter registers the handler for the given pattern.
// Container's filter chain is applied for handler.
// If a handler already exists for pattern, HandleWithFilter panics.
func (c *Container) HandleWithFilter(pattern string, handler http.Handler) {
	f := func(httpResponse http.ResponseWriter, httpRequest *http.Request) {
		if len(c.containerFilters) == 0 {
			handler.ServeHTTP(httpResponse, httpRequest)
			return
		}

		chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
			handler.ServeHTTP(httpResponse, httpRequest)
		}}
		chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
	}

	c.Handle(pattern, http.HandlerFunc(f))
}

// Filter appends a container FilterFunction. These are called before dispatching
// a http.Request to a WebService from the container
func (c *Container) Filter(filter FilterFunction) {
	c.containerFilters = append(c.containerFilters, filter)
}

// RegisteredWebServices returns the collections of added WebServices
func (c Container) RegisteredWebServices() []*WebService {
	c.webServicesLock.RLock()
	defer c.webServicesLock.RUnlock()
	result := make([]*WebService, len(c.webServices))
	for ix := range c.webServices {
		result[ix] = c.webServices[ix]
	}
	return result
}

// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
func (c Container) computeAllowedMethods(req *Request) []string {
	// Go through all RegisteredWebServices() and all its Routes to collect the options
	methods := []string{}
	requestPath := req.Request.URL.Path
	for _, ws := range c.RegisteredWebServices() {
		matches := ws.pathExpr.Matcher.FindStringSubmatch(requestPath)
		if matches != nil {
			finalMatch := matches[len(matches)-1]
			for _, rt := range ws.Routes() {
				matches := rt.pathExpr.Matcher.FindStringSubmatch(finalMatch)
				if matches != nil {
					lastMatch := matches[len(matches)-1]
					if lastMatch == "" || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
						methods = append(methods, rt.Method)
					}
				}
			}
		}
	}
	// methods = append(methods, "OPTIONS")  not sure about this
	return methods
}

// newBasicRequestResponse creates a pair of Request,Response from its http versions.
// It is basic because no parameter or (produces) content-type information is given.
func newBasicRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
	resp := NewResponse(httpWriter)
	resp.requestAccept = httpRequest.Header.Get(HEADER_Accept)
	return NewRequest(httpRequest), resp
}
<|endoftext|>


<commit_before>package clicommand

import (
	"io/ioutil"
	"os"
	"strings"
	"time"

	"github.com/buildkite/agent/stdin"

	"github.com/buildkite/agent/agent"
	"github.com/buildkite/agent/api"
	"github.com/buildkite/agent/cliconfig"
	"github.com/buildkite/agent/logger"
	"github.com/buildkite/agent/retry"
	"github.com/urfave/cli"
)

var AnnotateHelpDescription = `Usage:

   buildkite-agent annotate <file> [arguments...]

Description:

   Build annotations allow you to customize the Buildkite build interface to
   show information that may surface from your builds. Some examples include:

   - Links to artifacts generated by your jobs
   - Test result summaries
   - Graphs that include analysis about your codebase
   - Helpful information for team members about what happened during a build

   Annotations can be written in either Markdown or HTML.

   You can update an existing annotation's body by running the annotate command
   again and provide the same context as the one you want to update. Or if you
   leave context blank, it will use the default context.

   You can also just update the style of an existing annotation by omitting the
   body and just providing a new style value.

Example:

   $ buildkite-agent annotate "All tests passed! :rocket:"
   $ cat annotation.md | buildkite-agent annotate --style "warning"
   $ buildkite-agent annotate --style "success" --context "junit"
   $ ./script/dynamic_annotation_generator | buildkite-agent annotate --style "success"`

type AnnotateConfig struct {
	Body             string `cli:"arg:0" label:"annotation body"`
	Style            string `cli:"style"`
	Context          string `cli:"context"`
	Append           bool   `cli:"append"`
	Job              string `cli:"job" validate:"required"`
	AgentAccessToken string `cli:"agent-access-token" validate:"required"`
	Endpoint         string `cli:"endpoint" validate:"required"`
	NoColor          bool   `cli:"no-color"`
	Debug            bool   `cli:"debug"`
	DebugHTTP        bool   `cli:"debug-http"`
}

var AnnotateCommand = cli.Command{
	Name:        "annotate",
	Usage:       "Annotate the build page within the Buildkite UI with text from within a Buildkite job",
	Description: AnnotateHelpDescription,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:   "context",
			Usage:  "The context of the annotation used to differentiate this annotation from others",
			EnvVar: "BUILDKITE_ANNOTATION_CONTEXT",
		},
		cli.StringFlag{
			Name:   "style",
			Usage:  "The style of the annotation (`success`, `info`, `warning` or `error`)",
			EnvVar: "BUILDKITE_ANNOTATION_STYLE",
		},
		cli.BoolFlag{
			Name:   "append",
			Usage:  "Append to the body of an existing annotation",
			EnvVar: "BUILDKITE_ANNOTATION_APPEND",
		},
		cli.StringFlag{
			Name:   "job",
			Value:  "",
			Usage:  "Which job should the annotation come from",
			EnvVar: "BUILDKITE_JOB_ID",
		},
		AgentAccessTokenFlag,
		EndpointFlag,
		NoColorFlag,
		DebugFlag,
		DebugHTTPFlag,
	},
	Action: func(c *cli.Context) {
		// The configuration will be loaded into this struct
		cfg := AnnotateConfig{}

		// Load the configuration
		loader := cliconfig.Loader{CLI: c, Config: &cfg}
		if err := loader.Load(); err != nil {
			logger.Fatal("%s", err)
		}

		// Setup the any global configuration options
		HandleGlobalFlags(cfg)

		var body string
		var err error

		if cfg.Body != "" {
			body = cfg.Body
		} else if stdin.IsReadable() {
			logger.Info("Reading annotation body from STDIN")

			// Actually read the file from STDIN
			stdin, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				logger.Fatal("Failed to read from STDIN: %s", err)
			}

			body = string(stdin[:])
		}

		// Create the API client
		client := agent.APIClient{
			Endpoint: cfg.Endpoint,
			Token:    cfg.AgentAccessToken,
		}.Create()

		// Create the annotation we'll send to the Buildkite API
		annotation := &api.Annotation{
			Body:    body,
			Style:   cfg.Style,
			Context: cfg.Context,
			Append:  cfg.Append,
		}

		// Retry the annotation a few times before giving up
		err = retry.Do(func(s *retry.Stats) error {
			// Attempt ot create the annotation
			resp, err := client.Annotations.Create(cfg.Job, annotation)

			// Don't bother retrying if the response was one of these statuses
			if resp != nil && (resp.StatusCode == 401 || resp.StatusCode == 404 || resp.StatusCode == 400) {
				s.Break()
				return err
			}

			// Show the unexpected error
			if err != nil {
				logger.Warn("%s (%s)", err, s)
			}

			return err
		}, &retry.Config{Maximum: 5, Interval: 1 * time.Second, Jitter: true})

		// Show a fatal error if we gave up trying to create the annotation
		if err != nil {
			logger.Fatal("Failed to annotate build: %s", err)
		}

		logger.Info("Successfully annotated build")
	},
}
<commit_msg>Remove unused import<commit_after>package clicommand

import (
	"io/ioutil"
	"os"
	"time"

	"github.com/buildkite/agent/stdin"

	"github.com/buildkite/agent/agent"
	"github.com/buildkite/agent/api"
	"github.com/buildkite/agent/cliconfig"
	"github.com/buildkite/agent/logger"
	"github.com/buildkite/agent/retry"
	"github.com/urfave/cli"
)

var AnnotateHelpDescription = `Usage:

   buildkite-agent annotate <file> [arguments...]

Description:

   Build annotations allow you to customize the Buildkite build interface to
   show information that may surface from your builds. Some examples include:

   - Links to artifacts generated by your jobs
   - Test result summaries
   - Graphs that include analysis about your codebase
   - Helpful information for team members about what happened during a build

   Annotations can be written in either Markdown or HTML.

   You can update an existing annotation's body by running the annotate command
   again and provide the same context as the one you want to update. Or if you
   leave context blank, it will use the default context.

   You can also just update the style of an existing annotation by omitting the
   body and just providing a new style value.

Example:

   $ buildkite-agent annotate "All tests passed! :rocket:"
   $ cat annotation.md | buildkite-agent annotate --style "warning"
   $ buildkite-agent annotate --style "success" --context "junit"
   $ ./script/dynamic_annotation_generator | buildkite-agent annotate --style "success"`

type AnnotateConfig struct {
	Body             string `cli:"arg:0" label:"annotation body"`
	Style            string `cli:"style"`
	Context          string `cli:"context"`
	Append           bool   `cli:"append"`
	Job              string `cli:"job" validate:"required"`
	AgentAccessToken string `cli:"agent-access-token" validate:"required"`
	Endpoint         string `cli:"endpoint" validate:"required"`
	NoColor          bool   `cli:"no-color"`
	Debug            bool   `cli:"debug"`
	DebugHTTP        bool   `cli:"debug-http"`
}

var AnnotateCommand = cli.Command{
	Name:        "annotate",
	Usage:       "Annotate the build page within the Buildkite UI with text from within a Buildkite job",
	Description: AnnotateHelpDescription,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:   "context",
			Usage:  "The context of the annotation used to differentiate this annotation from others",
			EnvVar: "BUILDKITE_ANNOTATION_CONTEXT",
		},
		cli.StringFlag{
			Name:   "style",
			Usage:  "The style of the annotation (`success`, `info`, `warning` or `error`)",
			EnvVar: "BUILDKITE_ANNOTATION_STYLE",
		},
		cli.BoolFlag{
			Name:   "append",
			Usage:  "Append to the body of an existing annotation",
			EnvVar: "BUILDKITE_ANNOTATION_APPEND",
		},
		cli.StringFlag{
			Name:   "job",
			Value:  "",
			Usage:  "Which job should the annotation come from",
			EnvVar: "BUILDKITE_JOB_ID",
		},
		AgentAccessTokenFlag,
		EndpointFlag,
		NoColorFlag,
		DebugFlag,
		DebugHTTPFlag,
	},
	Action: func(c *cli.Context) {
		// The configuration will be loaded into this struct
		cfg := AnnotateConfig{}

		// Load the configuration
		loader := cliconfig.Loader{CLI: c, Config: &cfg}
		if err := loader.Load(); err != nil {
			logger.Fatal("%s", err)
		}

		// Setup the any global configuration options
		HandleGlobalFlags(cfg)

		var body string
		var err error

		if cfg.Body != "" {
			body = cfg.Body
		} else if stdin.IsReadable() {
			logger.Info("Reading annotation body from STDIN")

			// Actually read the file from STDIN
			stdin, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				logger.Fatal("Failed to read from STDIN: %s", err)
			}

			body = string(stdin[:])
		}

		// Create the API client
		client := agent.APIClient{
			Endpoint: cfg.Endpoint,
			Token:    cfg.AgentAccessToken,
		}.Create()

		// Create the annotation we'll send to the Buildkite API
		annotation := &api.Annotation{
			Body:    body,
			Style:   cfg.Style,
			Context: cfg.Context,
			Append:  cfg.Append,
		}

		// Retry the annotation a few times before giving up
		err = retry.Do(func(s *retry.Stats) error {
			// Attempt ot create the annotation
			resp, err := client.Annotations.Create(cfg.Job, annotation)

			// Don't bother retrying if the response was one of these statuses
			if resp != nil && (resp.StatusCode == 401 || resp.StatusCode == 404 || resp.StatusCode == 400) {
				s.Break()
				return err
			}

			// Show the unexpected error
			if err != nil {
				logger.Warn("%s (%s)", err, s)
			}

			return err
		}, &retry.Config{Maximum: 5, Interval: 1 * time.Second, Jitter: true})

		// Show a fatal error if we gave up trying to create the annotation
		if err != nil {
			logger.Fatal("Failed to annotate build: %s", err)
		}

		logger.Info("Successfully annotated build")
	},
}
<|endoftext|>


<commit_before>package azurerm

import (
	"fmt"
	"testing"

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

func TestAccAzureRMSubnet_importBasic(t *testing.T) {
	resourceName := "azurerm_subnet.test"

	ri := acctest.RandInt()
	config := fmt.Sprintf(testAccAzureRMSubnet_basic, ri, ri, ri)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMSubnetDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: config,
			},

			resource.TestStep{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}
<commit_msg>Fixing the import test by adding missing formatting values<commit_after>package azurerm

import (
	"fmt"
	"testing"

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

func TestAccAzureRMSubnet_importBasic(t *testing.T) {
	resourceName := "azurerm_subnet.test"

	ri := acctest.RandInt()
	config := fmt.Sprintf(testAccAzureRMSubnet_basic, ri, ri, ri, ri, ri)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMSubnetDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: config,
			},

			resource.TestStep{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"os/exec"
	"strings"
	"time"
	"os"
)

type User struct {
	Username     string
	Hostname     string
	TimeDetected time.Time
	TimeNotified time.Time
}

func (user *User) updateTimeNotified(time time.Time) {
	user.TimeNotified = time
}

func createUsersSlice() []*User {
	users := make([]*User, 0)
	return users
}

func detectUser(userSlice []*User) []*User {
	tmpUserSlice := createUsersSlice()

	WhoCmd, lookErr := exec.LookPath("who")
	if lookErr != nil {
		panic(lookErr)
	}

	args := []string{"-u"}

	os.Setenv("LANG", "en_US.UTF-8")
	whoOut, execErr := exec.Command(WhoCmd, args...).Output()
	if execErr != nil {
		panic(execErr)
	}

	scanner := bufio.NewScanner(strings.NewReader(string(whoOut)))
	for scanner.Scan() {
		whoIsLoggedIn := strings.Fields(scanner.Text())
		username := whoIsLoggedIn[0]
		hostname := strings.Trim(whoIsLoggedIn[6], "(,)")
		if strings.Contains(hostname, ":") {
			hostname = string(strings.Split(hostname, ":")[0])
		}
		timestamp := time.Now()
		tmpUserSlice = append(tmpUserSlice, &User{Username: username, Hostname: hostname, TimeDetected: timestamp, TimeNotified: timestamp})
	}

	for _, user := range userSlice {
		if isUserNotInSlice(user.Username, user.Hostname, tmpUserSlice) {
			userSlice = removeUserFromSlice(user.Username, user.Hostname, userSlice)
		}
	}

	for _, user := range tmpUserSlice {
		if isUserNotInSlice(user.Username, user.Hostname, userSlice) {
			userSlice = append(userSlice, &User{Username: user.Username, Hostname: user.Hostname, TimeDetected: user.TimeDetected, TimeNotified: user.TimeNotified})
		}
	}
	tmpUserSlice = nil
	return userSlice
}

func isUserNotInSlice(username string, hostname string, userSlice []*User) bool {
	for _, user := range userSlice {
		if user.Username == username && user.Hostname == hostname {
			return false
		}
	}
	return true
}

func removeUserFromSlice(username string, hostname string, userSlice []*User) []*User {
	k := 0
	for _, user := range userSlice {
		if user.Username != username || user.Hostname != hostname {
			userSlice[k] = user
			k++
		}
	}
	return userSlice[:k]
}
<commit_msg>set fallback hostname if empty<commit_after>package main

import (
	"bufio"
	"os/exec"
	"strings"
	"time"
	"os"
)

type User struct {
	Username     string
	Hostname     string
	TimeDetected time.Time
	TimeNotified time.Time
}

func (user *User) updateTimeNotified(time time.Time) {
	user.TimeNotified = time
}

func createUsersSlice() []*User {
	users := make([]*User, 0)
	return users
}

func detectUser(userSlice []*User) []*User {
	tmpUserSlice := createUsersSlice()

	WhoCmd, lookErr := exec.LookPath("who")
	if lookErr != nil {
		panic(lookErr)
	}

	args := []string{"-u"}

	os.Setenv("LANG", "en_US.UTF-8")
	whoOut, execErr := exec.Command(WhoCmd, args...).Output()
	if execErr != nil {
		panic(execErr)
	}

	scanner := bufio.NewScanner(strings.NewReader(string(whoOut)))
	for scanner.Scan() {
		whoIsLoggedIn := strings.Fields(scanner.Text())
		username := whoIsLoggedIn[0]
		hostname := ""
		if strings.Contains(whoIsLoggedIn[1], "tty") {
		    hostname = "console"
		}	else {
		    hostname = strings.Trim(whoIsLoggedIn[6], "(,)")
		}
		if strings.Contains(hostname, ":") {
			hostname = string(strings.Split(hostname, ":")[0])
		}
		timestamp := time.Now()
		tmpUserSlice = append(tmpUserSlice, &User{Username: username, Hostname: hostname, TimeDetected: timestamp, TimeNotified: timestamp})
	}

	for _, user := range userSlice {
		if isUserNotInSlice(user.Username, user.Hostname, tmpUserSlice) {
			userSlice = removeUserFromSlice(user.Username, user.Hostname, userSlice)
		}
	}

	for _, user := range tmpUserSlice {
		if isUserNotInSlice(user.Username, user.Hostname, userSlice) {
			userSlice = append(userSlice, &User{Username: user.Username, Hostname: user.Hostname, TimeDetected: user.TimeDetected, TimeNotified: user.TimeNotified})
		}
	}
	tmpUserSlice = nil
	return userSlice
}

func isUserNotInSlice(username string, hostname string, userSlice []*User) bool {
	for _, user := range userSlice {
		if user.Username == username && user.Hostname == hostname {
			return false
		}
	}
	return true
}

func removeUserFromSlice(username string, hostname string, userSlice []*User) []*User {
	k := 0
	for _, user := range userSlice {
		if user.Username != username || user.Hostname != hostname {
			userSlice[k] = user
			k++
		}
	}
	return userSlice[:k]
}
<|endoftext|>


<commit_before>package oak

import (
	"image"
	"runtime"

	"bitbucket.org/oakmoundstudio/oak/dlog"
	pmouse "bitbucket.org/oakmoundstudio/oak/mouse"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/size"
)

// var (
// 	eFilter gesture.EventFilter
// )

func inputLoop() {
	//eFilter = gesture.EventFilter{EventDeque: windowControl}
	schedCt := 0
	for {
		//e := eFilter.Filter(eFilter.EventDeque.NextEvent()) //Filters an event to see if it fits a defined gesture
		switch e := windowControl.NextEvent().(type) {

		// We only currently respond to death lifecycle events.
		case lifecycle.Event:
			if e.To == lifecycle.StageDead {
				quitCh <- true
				return
			}
			// ... this is where we would respond to window focus events

		// Send key events
		//
		// Key events have two varieties:
		// The "KeyDown" and "KeyUp" events, which trigger for all keys
		// and specific "KeyDown$key", etc events which trigger only for $key.
		// The specific key that is pressed is passed as the data interface for
		// the former events, but not for the latter.
		case key.Event:
			k := GetKeyBind(e.Code.String()[4:])
			if e.Direction == key.DirPress {
				//dlog.Verb("--------------------", e.Code.String()[4:], k)
				setDown(k)
				eb.Trigger("KeyDown", k)
				eb.Trigger("KeyDown"+k, nil)
			} else if e.Direction == key.DirRelease {
				setUp(k)
				eb.Trigger("KeyUp", k)
				eb.Trigger("KeyUp"+k, nil)
			}

		// Send mouse events
		//
		// Mouse events are parsed based on their button
		// and direction into an event name and then triggered:
		// 'MousePress', 'MouseRelease', 'MouseScrollDown', 'MouseScrollUp', and 'MouseDrag'
		//
		// The basic event name is meant for entities which
		// want to respond to the mouse event happening -anywhere-.
		//
		// For events which have mouse collision enabled, they'll recieve
		// $eventName+"On" when the event occurs within their collision area.
		//
		// Mouse events all recieve an x, y, and button string.
		case mouse.Event:
			button := pmouse.GetMouseButton(int32(e.Button))
			//dlog.Verb("Mouse direction ", e.Direction.String(), " Button ", button)
			var eventName string
			if e.Direction == mouse.DirPress {
				setDown(button)
				eventName = "MousePress"
			} else if e.Direction == mouse.DirRelease {
				setUp(button)
				eventName = "MouseRelease"
			} else if e.Button == -2 {
				eventName = "MouseScrollDown"
			} else if e.Button == -1 {
				eventName = "MouseScrollUp"
			} else {
				eventName = "MouseDrag"
			}
			mevent := pmouse.Event{
				X:      e.X / float32(windowRect.Max.X) * float32(ScreenWidth),
				Y:      e.Y / float32(windowRect.Max.Y) * float32(ScreenHeight),
				Button: button,
				Event:  eventName,
			}

			pmouse.LastMouseEvent = mevent

			eb.Trigger(eventName, mevent)
			pmouse.Propagate(eventName+"On", mevent)

		// Uncomment this if using the filter
		// case gesture.Event:
		// 	eventName := "Gesture" + e.Type.String()
		// 	dlog.Verb(eventName)
		// 	eb.Trigger(eventName, pmouse.FromShinyGesture(e))

		// I don't really know what a paint event is to be honest.
		// We hypothetically don't allow the user to manually resize
		// their window, so we don't do anything special for such events.
		case size.Event:
			//dlog.Verb("Got size event", e)
			windowRect = image.Rect(0, 0, e.WidthPx, e.HeightPx)
		case error:
			dlog.Error(e)
		}
		schedCt++
		if schedCt > 1000 {
			schedCt = 0
			runtime.Gosched()
		}
		/*
			//TODO: Reimplement outside of the input loop so that it doesnt slow down the input loop itself
				// This is a hardcoded quit function bound to the escape key.
				esc, dur := IsHeld("Escape")
				if esc && dur > time.Second*1 {
					dlog.Warn("Quiting oak from holding ESCAPE")
					windowControl.Send(lifecycle.Event{
						From:        0,
						To:          0,
						DrawContext: nil,
					})
				}
		*/
	}
}
<commit_msg>main package notes<commit_after>package oak

import (
	"image"
	"runtime"

	"bitbucket.org/oakmoundstudio/oak/dlog"
	pmouse "bitbucket.org/oakmoundstudio/oak/mouse"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/size"
)

// Todo: bring this back in as an option, it was remove to reduce input lag
// var (
// 	eFilter gesture.EventFilter
// )

func inputLoop() {
	//eFilter = gesture.EventFilter{EventDeque: windowControl}
	schedCt := 0
	for {
		//e := eFilter.Filter(eFilter.EventDeque.NextEvent()) //Filters an event to see if it fits a defined gesture
		switch e := windowControl.NextEvent().(type) {
		// We only currently respond to death lifecycle events.
		case lifecycle.Event:
			if e.To == lifecycle.StageDead {
				quitCh <- true
				return
			}
			// ... this is where we would respond to window focus events

		// Send key events
		//
		// Key events have two varieties:
		// The "KeyDown" and "KeyUp" events, which trigger for all keys
		// and specific "KeyDown$key", etc events which trigger only for $key.
		// The specific key that is pressed is passed as the data interface for
		// the former events, but not for the latter.
		case key.Event:
			k := GetKeyBind(e.Code.String()[4:])
			if e.Direction == key.DirPress {
				//dlog.Verb("--------------------", e.Code.String()[4:], k)
				setDown(k)
				eb.Trigger("KeyDown", k)
				eb.Trigger("KeyDown"+k, nil)
			} else if e.Direction == key.DirRelease {
				setUp(k)
				eb.Trigger("KeyUp", k)
				eb.Trigger("KeyUp"+k, nil)
			}

		// Send mouse events
		//
		// Mouse events are parsed based on their button
		// and direction into an event name and then triggered:
		// 'MousePress', 'MouseRelease', 'MouseScrollDown', 'MouseScrollUp', and 'MouseDrag'
		//
		// The basic event name is meant for entities which
		// want to respond to the mouse event happening -anywhere-.
		//
		// For events which have mouse collision enabled, they'll recieve
		// $eventName+"On" when the event occurs within their collision area.
		//
		// Mouse events all recieve an x, y, and button string.
		case mouse.Event:
			button := pmouse.GetMouseButton(int32(e.Button))
			//dlog.Verb("Mouse direction ", e.Direction.String(), " Button ", button)
			var eventName string
			if e.Direction == mouse.DirPress {
				setDown(button)
				eventName = "MousePress"
			} else if e.Direction == mouse.DirRelease {
				setUp(button)
				eventName = "MouseRelease"
			} else if e.Button == -2 {
				eventName = "MouseScrollDown"
			} else if e.Button == -1 {
				eventName = "MouseScrollUp"
			} else {
				eventName = "MouseDrag"
			}
			mevent := pmouse.Event{
				X:      e.X / float32(windowRect.Max.X) * float32(ScreenWidth),
				Y:      e.Y / float32(windowRect.Max.Y) * float32(ScreenHeight),
				Button: button,
				Event:  eventName,
			}

			pmouse.LastMouseEvent = mevent

			eb.Trigger(eventName, mevent)
			pmouse.Propagate(eventName+"On", mevent)

		// Uncomment this if using the filter
		// case gesture.Event:
		// 	eventName := "Gesture" + e.Type.String()
		// 	dlog.Verb(eventName)
		// 	eb.Trigger(eventName, pmouse.FromShinyGesture(e))

		// I don't really know what a paint event is to be honest.
		// We hypothetically don't allow the user to manually resize
		// their window, so we don't do anything special for such events.
		case size.Event:
			//dlog.Verb("Got size event", e)
			windowRect = image.Rect(0, 0, e.WidthPx, e.HeightPx)
		case error:
			dlog.Error(e)
		}
		// This loop can be tight enough that the go scheduler never gets
		// a chance to take control from this thread. This is a hack that
		// solves that.
		schedCt++
		if schedCt > 1000 {
			schedCt = 0
			runtime.Gosched()
		}
		/*
			//TODO: Reimplement outside of the input loop so that it doesnt slow down the input loop itself
				// This is a hardcoded quit function bound to the escape key.
				esc, dur := IsHeld("Escape")
				if esc && dur > time.Second*1 {
					dlog.Warn("Quiting oak from holding ESCAPE")
					windowControl.Send(lifecycle.Event{
						From:        0,
						To:          0,
						DrawContext: nil,
					})
				}
		*/
	}
}
<|endoftext|>


<commit_before>package tenus

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
	"unicode"

	"github.com/docker/libcontainer/netlink"
	"github.com/docker/libcontainer/system"
)

// generates random string for makeNetInterfaceName()
func randomString(size int) string {
	alphanum := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	bytes := make([]byte, size)
	rand.Read(bytes)

	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}

	return string(bytes)
}

func MakeNetInterfaceName(base string) string {
	return makeNetInterfaceName(base)
}

// generates new unused network interfaces name with given prefix
func makeNetInterfaceName(base string) string {
	for {
		name := base + randomString(6)
		if _, err := net.InterfaceByName(name); err == nil {
			continue
		}

		return name
	}
}

// validates MTU LinkOption
func validMtu(mtu int) error {
	if mtu < 0 {
		return errors.New("MTU must be a positive integer!")
	}

	return nil
}

// validates MacAddress LinkOption
func validMacAddress(macaddr string) error {
	if _, err := net.ParseMAC(macaddr); err != nil {
		return fmt.Errorf("Can not parse MAC address: %s", err)
	}

	if _, err := FindInterfaceByMacAddress(macaddr); err == nil {
		return fmt.Errorf("MAC Address already assigned on the host: %s", macaddr)
	}

	return nil
}

// validates MacAddress LinkOption
func validNs(ns int) error {
	if ns < 0 {
		return fmt.Errorf("Incorrect Network Namespace PID specified: %d", ns)
	}

	return nil
}

// validates Flags LinkOption
func validFlags(flags net.Flags) error {
	if (flags & syscall.IFF_UP) != syscall.IFF_UP {
		return fmt.Errorf("Unsupported network flags specified: %v", flags)
	}

	return nil
}

// NetInterfaceNameValid checks if the network interface name is valid.
// It accepts interface name as a string. It returns error if invalid interface name is supplied.
func NetInterfaceNameValid(name string) (bool, error) {
	if name == "" {
		return false, errors.New("Interface name can not be empty")
	}

	if len(name) == 1 {
		return false, fmt.Errorf("Interface name too short: %s", name)
	}

	if len(name) > netlink.IFNAMSIZ {
		return false, fmt.Errorf("Interface name too long: %s", name)
	}

	for _, char := range name {
		if unicode.IsSpace(char) || char > 0x7F {
			return false, fmt.Errorf("Invalid characters in interface name: %s", name)
		}
	}

	return true, nil
}

// FindInterfaceByMacAddress returns *net.Interface which has a given MAC address assigned.
// It returns nil and error if invalid MAC address is supplied or if there is no network interface
// with the given MAC address assigned on Linux host.
func FindInterfaceByMacAddress(macaddr string) (*net.Interface, error) {
	if macaddr == "" {
		return nil, errors.New("Empty MAC address specified!")
	}

	ifcs, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	hwaddr, err := net.ParseMAC(macaddr)
	if err != nil {
		return nil, err
	}

	for _, ifc := range ifcs {
		if bytes.Equal(hwaddr, ifc.HardwareAddr) {
			return &ifc, nil
		}
	}

	return nil, fmt.Errorf("Could not find interface with MAC address on the host: %s", macaddr)
}

// DockerPidByName returns PID of the running docker container.
// It accepts Docker container name and Docker host as parameters and queries Docker API via HTTP.
// Docker host passed as an argument can be either full path to Docker UNIX socket or HOST:PORT address string.
// It returns error if Docker container can not be found or if an error occurs when querying Docker API.
func DockerPidByName(name string, dockerHost string) (int, error) {
	var network string

	if name == "" {
		return 0, errors.New("Docker name can not be empty!")
	}

	if dockerHost == "" {
		return 0, errors.New("Docker host can not be empty!")
	}

	if filepath.IsAbs(dockerHost) {
		network = "unix"
	} else {
		network = "tcp"
	}

	req, err := http.NewRequest("GET", "http://docker.socket/containers/"+name+"/json", nil)
	if err != nil {
		return 0, fmt.Errorf("Fail to create http request: %s", err)
	}

	timeout := time.Duration(2 * time.Second)
	httpTransport := &http.Transport{
		Dial: func(proto string, addr string) (net.Conn, error) {
			return net.DialTimeout(network, dockerHost, timeout)
		},
	}

	dockerClient := http.Client{Transport: httpTransport}

	resp, err := dockerClient.Do(req)
	if err != nil {
		return 0, fmt.Errorf("Failed to create http client: %s", err)
	}

	switch resp.StatusCode {
	case http.StatusNotFound:
		return 0, fmt.Errorf("Docker container \"%s\" does not seem to exist!", name)
	case http.StatusInternalServerError:
		return 0, fmt.Errorf("Could not retrieve Docker %s pid due to Docker server error", name)
	}

	data := struct {
		State struct {
			Pid float64
		}
	}{}

	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return 0, fmt.Errorf("Unable to decode json response: %s", err)
	}

	return int(data.State.Pid), nil
}

// NetNsHandle returns a file descriptor handle for network namespace specified by PID.
// It returns error if network namespace could not be found or if network namespace path could not be opened.
func NetNsHandle(nspid int) (uintptr, error) {
	if nspid <= 0 || nspid == 1 {
		return 0, fmt.Errorf("Incorred PID specified: %d", nspid)
	}

	nsPath := path.Join("/", "proc", strconv.Itoa(nspid), "ns/net")
	if nsPath == "" {
		return 0, fmt.Errorf("Could not find Network namespace for pid: %d", nspid)
	}

	file, err := os.Open(nsPath)
	if err != nil {
		return 0, fmt.Errorf("Could not open Network Namespace: %s", err)
	}

	return file.Fd(), nil
}

// SetNetNsToPid sets network namespace to the one specied by PID.
// It returns error if the network namespace could not be set.
func SetNetNsToPid(nspid int) error {
	if nspid <= 0 || nspid == 1 {
		return fmt.Errorf("Incorred PID specified: %d", nspid)
	}

	nsFd, err := NetNsHandle(nspid)
	if err != nil {
		return fmt.Errorf("Could not get network namespace handle: %s", err)
	}

	if err := system.Setns(nsFd, syscall.CLONE_NEWNET); err != nil {
		return fmt.Errorf("Unable to set the network namespace: %v", err)
	}

	return nil
}
<commit_msg>Do not forget to close filedescriptor<commit_after>package tenus

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
	"unicode"

	"github.com/docker/libcontainer/netlink"
	"github.com/docker/libcontainer/system"
)

// generates random string for makeNetInterfaceName()
func randomString(size int) string {
	alphanum := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	bytes := make([]byte, size)
	rand.Read(bytes)

	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}

	return string(bytes)
}

func MakeNetInterfaceName(base string) string {
	return makeNetInterfaceName(base)
}

// generates new unused network interfaces name with given prefix
func makeNetInterfaceName(base string) string {
	for {
		name := base + randomString(6)
		if _, err := net.InterfaceByName(name); err == nil {
			continue
		}

		return name
	}
}

// validates MTU LinkOption
func validMtu(mtu int) error {
	if mtu < 0 {
		return errors.New("MTU must be a positive integer!")
	}

	return nil
}

// validates MacAddress LinkOption
func validMacAddress(macaddr string) error {
	if _, err := net.ParseMAC(macaddr); err != nil {
		return fmt.Errorf("Can not parse MAC address: %s", err)
	}

	if _, err := FindInterfaceByMacAddress(macaddr); err == nil {
		return fmt.Errorf("MAC Address already assigned on the host: %s", macaddr)
	}

	return nil
}

// validates MacAddress LinkOption
func validNs(ns int) error {
	if ns < 0 {
		return fmt.Errorf("Incorrect Network Namespace PID specified: %d", ns)
	}

	return nil
}

// validates Flags LinkOption
func validFlags(flags net.Flags) error {
	if (flags & syscall.IFF_UP) != syscall.IFF_UP {
		return fmt.Errorf("Unsupported network flags specified: %v", flags)
	}

	return nil
}

// NetInterfaceNameValid checks if the network interface name is valid.
// It accepts interface name as a string. It returns error if invalid interface name is supplied.
func NetInterfaceNameValid(name string) (bool, error) {
	if name == "" {
		return false, errors.New("Interface name can not be empty")
	}

	if len(name) == 1 {
		return false, fmt.Errorf("Interface name too short: %s", name)
	}

	if len(name) > netlink.IFNAMSIZ {
		return false, fmt.Errorf("Interface name too long: %s", name)
	}

	for _, char := range name {
		if unicode.IsSpace(char) || char > 0x7F {
			return false, fmt.Errorf("Invalid characters in interface name: %s", name)
		}
	}

	return true, nil
}

// FindInterfaceByMacAddress returns *net.Interface which has a given MAC address assigned.
// It returns nil and error if invalid MAC address is supplied or if there is no network interface
// with the given MAC address assigned on Linux host.
func FindInterfaceByMacAddress(macaddr string) (*net.Interface, error) {
	if macaddr == "" {
		return nil, errors.New("Empty MAC address specified!")
	}

	ifcs, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	hwaddr, err := net.ParseMAC(macaddr)
	if err != nil {
		return nil, err
	}

	for _, ifc := range ifcs {
		if bytes.Equal(hwaddr, ifc.HardwareAddr) {
			return &ifc, nil
		}
	}

	return nil, fmt.Errorf("Could not find interface with MAC address on the host: %s", macaddr)
}

// DockerPidByName returns PID of the running docker container.
// It accepts Docker container name and Docker host as parameters and queries Docker API via HTTP.
// Docker host passed as an argument can be either full path to Docker UNIX socket or HOST:PORT address string.
// It returns error if Docker container can not be found or if an error occurs when querying Docker API.
func DockerPidByName(name string, dockerHost string) (int, error) {
	var network string

	if name == "" {
		return 0, errors.New("Docker name can not be empty!")
	}

	if dockerHost == "" {
		return 0, errors.New("Docker host can not be empty!")
	}

	if filepath.IsAbs(dockerHost) {
		network = "unix"
	} else {
		network = "tcp"
	}

	req, err := http.NewRequest("GET", "http://docker.socket/containers/"+name+"/json", nil)
	if err != nil {
		return 0, fmt.Errorf("Fail to create http request: %s", err)
	}

	timeout := time.Duration(2 * time.Second)
	httpTransport := &http.Transport{
		Dial: func(proto string, addr string) (net.Conn, error) {
			return net.DialTimeout(network, dockerHost, timeout)
		},
	}

	dockerClient := http.Client{Transport: httpTransport}

	resp, err := dockerClient.Do(req)
	if err != nil {
		return 0, fmt.Errorf("Failed to create http client: %s", err)
	}

	switch resp.StatusCode {
	case http.StatusNotFound:
		return 0, fmt.Errorf("Docker container \"%s\" does not seem to exist!", name)
	case http.StatusInternalServerError:
		return 0, fmt.Errorf("Could not retrieve Docker %s pid due to Docker server error", name)
	}

	data := struct {
		State struct {
			Pid float64
		}
	}{}

	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return 0, fmt.Errorf("Unable to decode json response: %s", err)
	}

	return int(data.State.Pid), nil
}

// NetNsHandle returns a file descriptor handle for network namespace specified by PID.
// It returns error if network namespace could not be found or if network namespace path could not be opened.
func NetNsHandle(nspid int) (uintptr, error) {
	if nspid <= 0 || nspid == 1 {
		return 0, fmt.Errorf("Incorred PID specified: %d", nspid)
	}

	nsPath := path.Join("/", "proc", strconv.Itoa(nspid), "ns/net")
	if nsPath == "" {
		return 0, fmt.Errorf("Could not find Network namespace for pid: %d", nspid)
	}

	file, err := os.Open(nsPath)
	if err != nil {
		return 0, fmt.Errorf("Could not open Network Namespace: %s", err)
	}

	return file.Fd(), nil
}

// SetNetNsToPid sets network namespace to the one specied by PID.
// It returns error if the network namespace could not be set.
func SetNetNsToPid(nspid int) error {
	if nspid <= 0 || nspid == 1 {
		return fmt.Errorf("Incorred PID specified: %d", nspid)
	}

	nsFd, err := NetNsHandle(nspid)
	defer syscall.Close(int(nsFd))
	if err != nil {
		return fmt.Errorf("Could not get network namespace handle: %s", err)
	}

	if err := system.Setns(nsFd, syscall.CLONE_NEWNET); err != nil {
		return fmt.Errorf("Unable to set the network namespace: %v", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package henchman

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh"
	"fmt"
	"github.com/sudharsh/henchman/ansi"
	"log"
	"text/template"
)

type Task struct {
	Name   string
	Action string
	IgnoreErrors bool `yaml:"ignore_errors"`
}

func prepareTemplate(data string, vars TaskVars) (string, error) {
	var buf bytes.Buffer
	tmpl, err := template.New("test").Parse(data)
	if err != nil {
		panic(err)
	}
	err = tmpl.Execute(&buf, vars)
	return string(buf.Bytes()), err
}

func (task *Task) Prepare(vars TaskVars) {
	var err error
	task.Name, err = prepareTemplate(task.Name, vars)
	if err != nil {
		panic(err)
	}
	task.Action, err = prepareTemplate(task.Action, vars)
	if err != nil {
		panic(err)
	}
}

func (task *Task) RunOn(machine *Machine, vars TaskVars, status chan string) {
	task.Prepare(vars)
	reset := ansi.ColorCode("reset")

	client, err := ssh.Dial("tcp", machine.Hostname+":22", machine.SSHConfig)
	if err != nil {
		log.Fatalf("Failed to dial: " + err.Error())
	}

	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		log.Fatalf("Unable to create session: " + err.Error())
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ECHO:          0,
		TTY_OP_ISPEED: 14400,
		TTY_OP_OSPEED: 14400,
	}
	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		log.Fatalf("request for pseudo terminal failed: " + err.Error())
	}

	var b bytes.Buffer
	session.Stdout = &b
	log.Printf("**** Running task: %s\n", task.Name)
	log.Printf("---- Cmd: %s\n", task.Action)
	log.Printf("---- Host: %s\n", machine.Hostname)

	var escapeCode string

	if err := session.Run(task.Action); err != nil {
		fmt.Printf("%s\n", task)
		if task.IgnoreErrors {
			log.Printf("Ignoring this task's error")
			status <- "ignored"
			escapeCode = ansi.ColorCode("yellow")
		} else {
			log.Printf("Failed to run: " + err.Error())
			status <- "failure"
			escapeCode = ansi.ColorCode("red")
		}
	} else {
		log.Printf("---- Success: \n")
		escapeCode = ansi.ColorCode("green")
		status <- "success"
	}
	fmt.Print(escapeCode + b.String() + reset)
	log.Print("--------------------")
}
<commit_msg>gofmt<commit_after>package henchman

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh"
	"fmt"
	"github.com/sudharsh/henchman/ansi"
	"log"
	"text/template"
)

type Task struct {
	Name         string
	Action       string
	IgnoreErrors bool `yaml:"ignore_errors"`
}

func prepareTemplate(data string, vars TaskVars) (string, error) {
	var buf bytes.Buffer
	tmpl, err := template.New("test").Parse(data)
	if err != nil {
		panic(err)
	}
	err = tmpl.Execute(&buf, vars)
	return string(buf.Bytes()), err
}

func (task *Task) Prepare(vars TaskVars) {
	var err error
	task.Name, err = prepareTemplate(task.Name, vars)
	if err != nil {
		panic(err)
	}
	task.Action, err = prepareTemplate(task.Action, vars)
	if err != nil {
		panic(err)
	}
}

func (task *Task) RunOn(machine *Machine, vars TaskVars, status chan string) {
	task.Prepare(vars)
	reset := ansi.ColorCode("reset")

	client, err := ssh.Dial("tcp", machine.Hostname+":22", machine.SSHConfig)
	if err != nil {
		log.Fatalf("Failed to dial: " + err.Error())
	}

	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		log.Fatalf("Unable to create session: " + err.Error())
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ECHO:          0,
		TTY_OP_ISPEED: 14400,
		TTY_OP_OSPEED: 14400,
	}
	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		log.Fatalf("request for pseudo terminal failed: " + err.Error())
	}

	var b bytes.Buffer
	session.Stdout = &b
	log.Printf("**** Running task: %s\n", task.Name)
	log.Printf("---- Cmd: %s\n", task.Action)
	log.Printf("---- Host: %s\n", machine.Hostname)

	var escapeCode string

	if err := session.Run(task.Action); err != nil {
		fmt.Printf("%s\n", task)
		if task.IgnoreErrors {
			log.Printf("Ignoring this task's error")
			status <- "ignored"
			escapeCode = ansi.ColorCode("yellow")
		} else {
			log.Printf("Failed to run: " + err.Error())
			status <- "failure"
			escapeCode = ansi.ColorCode("red")
		}
	} else {
		log.Printf("---- Success: \n")
		escapeCode = ansi.ColorCode("green")
		status <- "success"
	}
	fmt.Print(escapeCode + b.String() + reset)
	log.Print("--------------------")
}
<|endoftext|>


<commit_before>package driver

import (
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"strings"

	"github.com/concourse/baggageclaim/fs"
	"github.com/pivotal-golang/lager"
)

type BtrFSDriver struct {
	fs *fs.BtrfsFilesystem

	logger lager.Logger
}

func NewBtrFSDriver(logger lager.Logger) *BtrFSDriver {
	return &BtrFSDriver{
		logger: logger,
	}
}

func (driver *BtrFSDriver) CreateVolume(path string) error {
	_, _, err := driver.run("btrfs", "subvolume", "create", path)
	return err
}

func (driver *BtrFSDriver) DestroyVolume(path string) error {
	_, _, err := driver.run("btrfs", "subvolume", "delete", path)
	return err
}

func (driver *BtrFSDriver) CreateCopyOnWriteLayer(path string, parent string) error {
	_, _, err := driver.run("btrfs", "subvolume", "snapshot", parent, path)
	return err
}

func (driver *BtrFSDriver) GetVolumeSize(path string) (uint, error) {
	_, _, err := driver.run("btrfs", "quota", "enable", path)
	if err != nil {
		return 0, err
	}

	output, _, err := driver.run("btrfs", "qgroup", "show", "-F", "--raw", path)
	if err != nil {
		return 0, err
	}

	qgroupsLines := strings.Split(strings.TrimSpace(output), "\n")
	qgroupFields := strings.Fields(qgroupsLines[len(qgroupsLines)-1])

	if len(qgroupFields) != 3 {
		return 0, errors.New("unable-to-parse-btrfs-qgroup-show")
	}

	var exclusiveSize uint
	_, err = fmt.Sscanf(qgroupFields[2], "%d", &exclusiveSize)

	if err != nil {
		return 0, err
	}

	return exclusiveSize, nil
}

func (driver *BtrFSDriver) run(command string, args ...string) (string, string, error) {
	cmd := exec.Command(command, args...)

	logger := driver.logger.Session("run-command", lager.Data{
		"command": command,
		"args":    args,
	})

	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	cmd.Stdout = stdout
	cmd.Stderr = stderr

	err := cmd.Run()

	loggerData := lager.Data{
		"stdout": stdout.String(),
		"stderr": stderr.String(),
	}

	if err != nil {
		logger.Error("failed", err, loggerData)
		return "", "", err
	}

	logger.Debug("ran", loggerData)

	return stdout.String(), stderr.String(), nil
}
<commit_msg>enable quotas on volume create, not on size collection<commit_after>package driver

import (
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"strings"

	"github.com/concourse/baggageclaim/fs"
	"github.com/pivotal-golang/lager"
)

type BtrFSDriver struct {
	fs *fs.BtrfsFilesystem

	logger lager.Logger
}

func NewBtrFSDriver(logger lager.Logger) *BtrFSDriver {
	return &BtrFSDriver{
		logger: logger,
	}
}

func (driver *BtrFSDriver) CreateVolume(path string) error {
	_, _, err := driver.run("btrfs", "subvolume", "create", path)
	if err != nil {
		return err
	}

	_, _, err = driver.run("btrfs", "quota", "enable", path)
	if err != nil {
		return err
	}

	return nil
}

func (driver *BtrFSDriver) DestroyVolume(path string) error {
	_, _, err := driver.run("btrfs", "subvolume", "delete", path)
	return err
}

func (driver *BtrFSDriver) CreateCopyOnWriteLayer(path string, parent string) error {
	_, _, err := driver.run("btrfs", "subvolume", "snapshot", parent, path)
	return err
}

func (driver *BtrFSDriver) GetVolumeSize(path string) (uint, error) {
	output, _, err := driver.run("btrfs", "qgroup", "show", "-F", "--raw", path)
	if err != nil {
		return 0, err
	}

	qgroupsLines := strings.Split(strings.TrimSpace(output), "\n")
	qgroupFields := strings.Fields(qgroupsLines[len(qgroupsLines)-1])

	if len(qgroupFields) != 3 {
		return 0, errors.New("unable-to-parse-btrfs-qgroup-show")
	}

	var exclusiveSize uint
	_, err = fmt.Sscanf(qgroupFields[2], "%d", &exclusiveSize)

	if err != nil {
		return 0, err
	}

	return exclusiveSize, nil
}

func (driver *BtrFSDriver) run(command string, args ...string) (string, string, error) {
	cmd := exec.Command(command, args...)

	logger := driver.logger.Session("run-command", lager.Data{
		"command": command,
		"args":    args,
	})

	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	cmd.Stdout = stdout
	cmd.Stderr = stderr

	err := cmd.Run()

	loggerData := lager.Data{
		"stdout": stdout.String(),
		"stderr": stderr.String(),
	}

	if err != nil {
		logger.Error("failed", err, loggerData)
		return "", "", err
	}

	logger.Debug("ran", loggerData)

	return stdout.String(), stderr.String(), nil
}
<|endoftext|>


<commit_before>// Package goldie provides test assertions based on golden files. It's typically
// used for testing responses with larger data bodies.
//
// The concept is straight forward. Valid response data is stored in a "golden
// file". The actual response data will be byte compared with the golden file
// and the test will fail if there is a difference.
//
// Updating the golden file can be done by running `go test -update ./...`.
package goldie

import (
	"flag"
	"fmt"
	"os"
	"testing"

	"github.com/pmezard/go-difflib/difflib"
	"github.com/sergi/go-diff/diffmatchpatch"
)

const (
	// FixtureDir is the folder name for where the fixtures are stored. It's
	// relative to the "go test" path.
	defaultFixtureDir = "testdata"

	// FileNameSuffix is the suffix appended to the fixtures. Set to empty
	// string to disable file name suffixes.
	defaultFileNameSuffix = ".golden"

	// FilePerms is used to set the permissions on the golden fixture files.
	defaultFilePerms os.FileMode = 0644

	// DirPerms is used to set the permissions on the golden fixture folder.
	defaultDirPerms os.FileMode = 0755
)

var (
	// update determines if the actual received data should be written to the
	// golden files or not. This should be true when you need to update the
	// data, but false when actually running the tests.
	update = flag.Bool("update", false, "Update golden test file fixture")
)

// Option defines the signature of a functional option method that can apply
// options to an OptionProcessor.
type Option func(OptionProcessor) error

// Tester defines the methods that any golden tester should support.
type Tester interface {
	Assert(t *testing.T, name string, actualData []byte)
	AssertJson(t *testing.T, name string, actualJsonData interface{})
	AssertWithTemplate(t *testing.T, name string, data interface{}, actualData []byte)
	Update(t *testing.T, name string, actualData []byte) error
}

// DiffFn takes in an actual and expected and will return a diff string
// representing the differences between the two.
type DiffFn func(actual string, expected string) string

// DiffEngine is used to enumerate the diff engine processors that are
// available.
type DiffEngine int

const (
	// UndefinedDiff represents any undefined diff processor.  If a new diff
	// engine is implemented, it should be added to this enumeration and to the
	// `diff` helper function.
	UndefinedDiff DiffEngine = iota

	// ClassicDiff produces a diff similar to what the `diff` tool would
	// produce.
	//		+++ Actual
	//		@@ -1 +1 @@
	//		-Lorem dolor sit amet.
	//		+Lorem ipsum dolor.
	//
	ClassicDiff

	// ColoredDiff produces a diff that will use red and green colors to
	// distinguish the diffs between the two values.
	ColoredDiff
)

// OptionProcessor defines the functions that can be called to set values for
// a tester.  To expand this list, add a function to this interface and then
// implement the generic option setter below.
type OptionProcessor interface {
	// WithFixtureDir sets the directory that will be used to store the
	// fixtures.
	//
	// Defaults to `testdata`.
	WithFixtureDir(dir string) error
	WithNameSuffix(suffix string) error
	WithFilePerms(mode os.FileMode) error
	WithDirPerms(mode os.FileMode) error

	WithDiffEngine(engine DiffEngine) error
	WithDiffFn(fn DiffFn) error
	WithIgnoreTemplateErrors(ignoreErrors bool) error
	WithTestNameForDir(use bool) error
	WithSubTestNameForDir(use bool) error
}

// === OptionProcessor ===============================

func WithFixtureDir(dir string) Option {
	return func(o OptionProcessor) error {
		return o.WithFixtureDir(dir)
	}
}

// WithNameSuffix sets the file suffix to be used for the golden file.
//
// Defaults to `.golden`
func WithNameSuffix(suffix string) Option {
	return func(o OptionProcessor) error {
		return o.WithNameSuffix(suffix)
	}
}

// WithFilePerms sets the file permissions on the golden files that are
// created.
//
// Defaults to 0644.
func WithFilePerms(mode os.FileMode) Option {
	return func(o OptionProcessor) error {
		return o.WithFilePerms(mode)
	}
}

// WithDirPerms sets the directory permissions for the directories in which the
// golden files are created.
//
// Defaults to 0755.
func WithDirPerms(mode os.FileMode) Option {
	return func(o OptionProcessor) error {
		return o.WithDirPerms(mode)
	}
}

// WithDiffEngine sets the `diff` engine that will be used to generate the
// `diff` text.
func WithDiffEngine(engine DiffEngine) Option {
	return func(o OptionProcessor) error {
		return o.WithDiffEngine(engine)
	}
}

// WithDiffFn sets the `diff` engine to be a function that implements the
// DiffFn signature. This allows for any customized diff logic you would like
// to create.
func WithDiffFn(fn DiffFn) Option {
	return func(o OptionProcessor) error {
		return o.WithDiffFn(fn)
	}
}

// WithIgnoreTemplateErrors allows template processing to ignore any variables
// in the template that do not have corresponding data values passed in.
//
// Default value is false.
func WithIgnoreTemplateErrors(ignoreErrors bool) Option {
	return func(o OptionProcessor) error {
		return o.WithIgnoreTemplateErrors(ignoreErrors)
	}
}

// WithTestNameForDir will create a directory with the test's name in the
// fixture directory to store all the golden files.
func WithTestNameForDir(use bool) Option {
	return func(o OptionProcessor) error {
		return o.WithTestNameForDir(use)
	}
}

// WithSubTestNameForDir will create a directory with the sub test's name to
// store all the golden files. If WithTestNameForDir is enabled, it will be in
// the test name's directory. Otherwise, it will be in the fixture directory.
func WithSubTestNameForDir(use bool) Option {
	return func(o OptionProcessor) error {
		return o.WithSubTestNameForDir(use)
	}
}

// === Create new testers ==================================

// New creates a new golden file tester. If there is an issue with applying any
// of the options, an error will be reported and t.FailNow() will be called.
func New(t *testing.T, options ...Option) *goldie {
	g := goldie{
		fixtureDir:     defaultFixtureDir,
		fileNameSuffix: defaultFileNameSuffix,
		filePerms:      defaultFilePerms,
		dirPerms:       defaultDirPerms,
	}

	var err error
	for _, option := range options {
		err = option(&g)
		if err != nil {
			t.Error(fmt.Errorf("Could not apply option: %w", err))
			t.FailNow()
		}
	}

	return &g
}

// Diff generates a string that shows the difference between the actual and the
// expected. This method could be called in your own DiffFn in case you want
// to leverage any of the engines defined.
func Diff(engine DiffEngine, actual string, expected string) string {
	var diff string
	switch engine {
	case ClassicDiff:
		diff, _ = difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
			A:        difflib.SplitLines(expected),
			B:        difflib.SplitLines(actual),
			FromFile: "Expected",
			FromDate: "",
			ToFile:   "Actual",
			ToDate:   "",
			Context:  1,
		})

	case ColoredDiff:
		dmp := diffmatchpatch.New()
		diffs := dmp.DiffMain(actual, expected, false)
		diff = dmp.DiffPrettyText(diffs)
	}
	return diff
}
<commit_msg>[refactor] Remove duplicate package description<commit_after>package goldie

import (
	"flag"
	"fmt"
	"os"
	"testing"

	"github.com/pmezard/go-difflib/difflib"
	"github.com/sergi/go-diff/diffmatchpatch"
)

const (
	// FixtureDir is the folder name for where the fixtures are stored. It's
	// relative to the "go test" path.
	defaultFixtureDir = "testdata"

	// FileNameSuffix is the suffix appended to the fixtures. Set to empty
	// string to disable file name suffixes.
	defaultFileNameSuffix = ".golden"

	// FilePerms is used to set the permissions on the golden fixture files.
	defaultFilePerms os.FileMode = 0644

	// DirPerms is used to set the permissions on the golden fixture folder.
	defaultDirPerms os.FileMode = 0755
)

var (
	// update determines if the actual received data should be written to the
	// golden files or not. This should be true when you need to update the
	// data, but false when actually running the tests.
	update = flag.Bool("update", false, "Update golden test file fixture")
)

// Option defines the signature of a functional option method that can apply
// options to an OptionProcessor.
type Option func(OptionProcessor) error

// Tester defines the methods that any golden tester should support.
type Tester interface {
	Assert(t *testing.T, name string, actualData []byte)
	AssertJson(t *testing.T, name string, actualJsonData interface{})
	AssertWithTemplate(t *testing.T, name string, data interface{}, actualData []byte)
	Update(t *testing.T, name string, actualData []byte) error
}

// DiffFn takes in an actual and expected and will return a diff string
// representing the differences between the two.
type DiffFn func(actual string, expected string) string

// DiffEngine is used to enumerate the diff engine processors that are
// available.
type DiffEngine int

const (
	// UndefinedDiff represents any undefined diff processor.  If a new diff
	// engine is implemented, it should be added to this enumeration and to the
	// `diff` helper function.
	UndefinedDiff DiffEngine = iota

	// ClassicDiff produces a diff similar to what the `diff` tool would
	// produce.
	//		+++ Actual
	//		@@ -1 +1 @@
	//		-Lorem dolor sit amet.
	//		+Lorem ipsum dolor.
	//
	ClassicDiff

	// ColoredDiff produces a diff that will use red and green colors to
	// distinguish the diffs between the two values.
	ColoredDiff
)

// OptionProcessor defines the functions that can be called to set values for
// a tester.  To expand this list, add a function to this interface and then
// implement the generic option setter below.
type OptionProcessor interface {
	// WithFixtureDir sets the directory that will be used to store the
	// fixtures.
	//
	// Defaults to `testdata`.
	WithFixtureDir(dir string) error
	WithNameSuffix(suffix string) error
	WithFilePerms(mode os.FileMode) error
	WithDirPerms(mode os.FileMode) error

	WithDiffEngine(engine DiffEngine) error
	WithDiffFn(fn DiffFn) error
	WithIgnoreTemplateErrors(ignoreErrors bool) error
	WithTestNameForDir(use bool) error
	WithSubTestNameForDir(use bool) error
}

// === OptionProcessor ===============================

func WithFixtureDir(dir string) Option {
	return func(o OptionProcessor) error {
		return o.WithFixtureDir(dir)
	}
}

// WithNameSuffix sets the file suffix to be used for the golden file.
//
// Defaults to `.golden`
func WithNameSuffix(suffix string) Option {
	return func(o OptionProcessor) error {
		return o.WithNameSuffix(suffix)
	}
}

// WithFilePerms sets the file permissions on the golden files that are
// created.
//
// Defaults to 0644.
func WithFilePerms(mode os.FileMode) Option {
	return func(o OptionProcessor) error {
		return o.WithFilePerms(mode)
	}
}

// WithDirPerms sets the directory permissions for the directories in which the
// golden files are created.
//
// Defaults to 0755.
func WithDirPerms(mode os.FileMode) Option {
	return func(o OptionProcessor) error {
		return o.WithDirPerms(mode)
	}
}

// WithDiffEngine sets the `diff` engine that will be used to generate the
// `diff` text.
func WithDiffEngine(engine DiffEngine) Option {
	return func(o OptionProcessor) error {
		return o.WithDiffEngine(engine)
	}
}

// WithDiffFn sets the `diff` engine to be a function that implements the
// DiffFn signature. This allows for any customized diff logic you would like
// to create.
func WithDiffFn(fn DiffFn) Option {
	return func(o OptionProcessor) error {
		return o.WithDiffFn(fn)
	}
}

// WithIgnoreTemplateErrors allows template processing to ignore any variables
// in the template that do not have corresponding data values passed in.
//
// Default value is false.
func WithIgnoreTemplateErrors(ignoreErrors bool) Option {
	return func(o OptionProcessor) error {
		return o.WithIgnoreTemplateErrors(ignoreErrors)
	}
}

// WithTestNameForDir will create a directory with the test's name in the
// fixture directory to store all the golden files.
func WithTestNameForDir(use bool) Option {
	return func(o OptionProcessor) error {
		return o.WithTestNameForDir(use)
	}
}

// WithSubTestNameForDir will create a directory with the sub test's name to
// store all the golden files. If WithTestNameForDir is enabled, it will be in
// the test name's directory. Otherwise, it will be in the fixture directory.
func WithSubTestNameForDir(use bool) Option {
	return func(o OptionProcessor) error {
		return o.WithSubTestNameForDir(use)
	}
}

// === Create new testers ==================================

// New creates a new golden file tester. If there is an issue with applying any
// of the options, an error will be reported and t.FailNow() will be called.
func New(t *testing.T, options ...Option) *goldie {
	g := goldie{
		fixtureDir:     defaultFixtureDir,
		fileNameSuffix: defaultFileNameSuffix,
		filePerms:      defaultFilePerms,
		dirPerms:       defaultDirPerms,
	}

	var err error
	for _, option := range options {
		err = option(&g)
		if err != nil {
			t.Error(fmt.Errorf("Could not apply option: %w", err))
			t.FailNow()
		}
	}

	return &g
}

// Diff generates a string that shows the difference between the actual and the
// expected. This method could be called in your own DiffFn in case you want
// to leverage any of the engines defined.
func Diff(engine DiffEngine, actual string, expected string) string {
	var diff string
	switch engine {
	case ClassicDiff:
		diff, _ = difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
			A:        difflib.SplitLines(expected),
			B:        difflib.SplitLines(actual),
			FromFile: "Expected",
			FromDate: "",
			ToFile:   "Actual",
			ToDate:   "",
			Context:  1,
		})

	case ColoredDiff:
		dmp := diffmatchpatch.New()
		diffs := dmp.DiffMain(actual, expected, false)
		diff = dmp.DiffPrettyText(diffs)
	}
	return diff
}
<|endoftext|>