<commit_before>package opencl

import (
	"fmt"
	"image"
	"image/png"
	"math/rand"
	"os"
	"time"

	"github.com/achilleasa/go-pathtrace/tracer"
)

// Debug flags.
type DebugFlag uint16

const (
	Off                         DebugFlag = 0
	PrimaryRayIntersectionDepth           = 1 << iota
	PrimaryRayIntersectionNormals
	AllEmissiveSamples
	VisibleEmissiveSamples
	OccludedEmissiveSamples
	Throughput
	Accumulator
	FrameBuffer
)

// An alias for functions that can be used as part of the rendering pipeline.
type PipelineStage func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error)

// The list of pluggable of stages that are used to render the scene.
type Pipeline struct {
	// Reset the tracer state. This stage is executed whenever the camera
	// is moved or the sample counter is reset.
	Reset PipelineStage

	// This stage is executed whenever the tracer generates a new set
	// of primary rays. Depending on the samples per pixel this stage
	// may be invoked more than once.
	PrimaryRayGenerator PipelineStage

	// This stage implements an integrator function to trace the primary
	// rays and add their contribution into the accumulation buffer.
	Integrator PipelineStage

	// A set of post-processing stages that are executed prior to
	// rendering the final frame.
	PostProcess []PipelineStage
}

func DefaultPipeline(debugFlags DebugFlag, numBounces uint32, exposure float32) *Pipeline {
	pipeline := &Pipeline{
		Reset:               ClearAccumulator(),
		PrimaryRayGenerator: PerspectiveCamera(),
		Integrator:          MonteCarloIntegrator(debugFlags, numBounces),
		PostProcess: []PipelineStage{
			TonemapSimpleReinhard(exposure),
		},
	}

	if debugFlags&FrameBuffer == FrameBuffer {
		pipeline.PostProcess = append(pipeline.PostProcess, DebugFrameBuffer("debug-fb.png"))
	}

	return pipeline
}

// Clear the accumulator buffer.
func ClearAccumulator() PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		return tr.resources.ClearAccumulator(blockReq)
	}
}

// Use a perspective camera for the primary ray generation stage.
func PerspectiveCamera() PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		return tr.resources.GeneratePrimaryRays(blockReq, tr.cameraPosition, tr.cameraFrustrum)
	}
}

// Apply simple Reinhard tone-mapping.
func TonemapSimpleReinhard(exposure float32) PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		return tr.resources.TonemapSimpleReinhard(blockReq, exposure)
	}
}

// Use a montecarlo pathtracer implementation.
func MonteCarloIntegrator(debugFlags DebugFlag, numBounces uint32) PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		var err error

		start := time.Now()
		numPixels := int(blockReq.FrameW * blockReq.BlockH)
		numEmissives := uint32(len(tr.sceneData.EmissivePrimitives))

		var activeRayBuf uint32 = 0

		// Intersect primary rays outside of the loop
		// TODO: Use packet query
		_, err = tr.resources.RayIntersectionQuery(activeRayBuf, numPixels)
		if err != nil {
			return time.Since(start), err
		}

		if debugFlags&PrimaryRayIntersectionDepth == PrimaryRayIntersectionDepth {
			_, err = tr.resources.DebugPrimaryRayIntersectionDepth(blockReq)
			err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, "debug-primary-intersection-depth.png")
			if err != nil {
				return time.Since(start), err
			}
		}

		if debugFlags&PrimaryRayIntersectionNormals == PrimaryRayIntersectionNormals {
			_, err = tr.resources.DebugPrimaryRayIntersectionNormals(blockReq)
			err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, "debug-primary-intersection-normals.png")
			if err != nil {
				return time.Since(start), err
			}
		}

		var bounce uint32
		for bounce = 0; bounce < numBounces; bounce++ {
			// Shade misses
			if tr.sceneData.SceneDiffuseMatIndex != -1 {
				if bounce == 0 {
					_, err = tr.resources.ShadePrimaryRayMisses(uint32(tr.sceneData.SceneDiffuseMatIndex), activeRayBuf, numPixels)
				} else {
					_, err = tr.resources.ShadeIndirectRayMisses(uint32(tr.sceneData.SceneDiffuseMatIndex), activeRayBuf, numPixels)
				}
				if err != nil {
					return time.Since(start), err
				}
			}

			// Shade hits
			_, err = tr.resources.ShadeHits(bounce, rand.Uint32(), numEmissives, activeRayBuf, numPixels)
			if err != nil {
				return time.Since(start), err
			}

			if debugFlags&Throughput == Throughput {
				_, err = tr.resources.DebugThroughput(blockReq)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-throughput-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			// Process intersections for occlusion rays and accumulate emissive samples for non occluded paths
			_, err := tr.resources.RayIntersectionTest(2, numPixels)
			if err != nil {
				return time.Since(start), err
			}

			_, err = tr.resources.AccumulateEmissiveSamples(2, numPixels)
			if err != nil {
				return time.Since(start), err
			}

			if debugFlags&AllEmissiveSamples == AllEmissiveSamples {
				_, err = tr.resources.DebugEmissiveSamples(blockReq, 0, 0)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-emissive-all-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			if debugFlags&VisibleEmissiveSamples == VisibleEmissiveSamples {
				_, err = tr.resources.DebugEmissiveSamples(blockReq, 1, 0)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-emissive-vis-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			if debugFlags&OccludedEmissiveSamples == OccludedEmissiveSamples {
				_, err = tr.resources.DebugEmissiveSamples(blockReq, 0, 1)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-emissive-occ-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			if debugFlags&Accumulator == Accumulator {
				_, err = tr.resources.DebugAccumulator(blockReq)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-accumulator-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			// Process intersections for indirect rays
			if bounce+1 < numBounces {
				activeRayBuf = 1 - activeRayBuf
				_, err = tr.resources.RayIntersectionQuery(activeRayBuf, numPixels)
				if err != nil {
					return time.Since(start), err
				}
			}
		}
		return time.Since(start), nil
	}
}

// Dump a copy of the RGBA framebuffer.
func DebugFrameBuffer(imgFile string) PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		start := time.Now()

		f, err := os.Create(imgFile)
		if err != nil {
			return 0, err
		}
		defer f.Close()

		im := image.NewRGBA(image.Rect(0, 0, int(blockReq.FrameW), int(blockReq.FrameH)))
		err = tr.resources.buffers.FrameBuffer.ReadData(0, 0, tr.resources.buffers.FrameBuffer.Size(), im.Pix)
		if err != nil {
			return 0, err
		}

		return time.Since(start), png.Encode(f, im)
	}
}

// Dump debug buffer to png file.
func dumpDebugBuffer(debugKernelError error, dr *deviceResources, frameW, frameH uint32, imgFile string) error {
	if debugKernelError != nil {
		return debugKernelError
	}
	f, err := os.Create(imgFile)
	if err != nil {
		return err
	}
	defer f.Close()

	im := image.NewRGBA(image.Rect(0, 0, int(frameW), int(frameH)))
	err = dr.buffers.DebugOutput.ReadData(0, 0, dr.buffers.DebugOutput.Size(), im.Pix)
	if err != nil {
		return err
	}

	return png.Encode(f, im)
}

func readCounter(dr *deviceResources, counterIndex uint32) uint32 {
	out := make([]uint32, 1)
	dr.buffers.RayCounters[counterIndex].ReadData(0, 0, 4, out)
	return out[0]
}
<commit_msg>Use packet intersector for primary rays on GPUs<commit_after>package opencl

import (
	"fmt"
	"image"
	"image/png"
	"math/rand"
	"os"
	"time"

	"github.com/achilleasa/go-pathtrace/tracer"
	"github.com/achilleasa/go-pathtrace/tracer/opencl/device"
)

// Debug flags.
type DebugFlag uint16

const (
	Off                         DebugFlag = 0
	PrimaryRayIntersectionDepth           = 1 << iota
	PrimaryRayIntersectionNormals
	AllEmissiveSamples
	VisibleEmissiveSamples
	OccludedEmissiveSamples
	Throughput
	Accumulator
	FrameBuffer
)

// An alias for functions that can be used as part of the rendering pipeline.
type PipelineStage func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error)

// The list of pluggable of stages that are used to render the scene.
type Pipeline struct {
	// Reset the tracer state. This stage is executed whenever the camera
	// is moved or the sample counter is reset.
	Reset PipelineStage

	// This stage is executed whenever the tracer generates a new set
	// of primary rays. Depending on the samples per pixel this stage
	// may be invoked more than once.
	PrimaryRayGenerator PipelineStage

	// This stage implements an integrator function to trace the primary
	// rays and add their contribution into the accumulation buffer.
	Integrator PipelineStage

	// A set of post-processing stages that are executed prior to
	// rendering the final frame.
	PostProcess []PipelineStage
}

func DefaultPipeline(debugFlags DebugFlag, numBounces uint32, exposure float32) *Pipeline {
	pipeline := &Pipeline{
		Reset:               ClearAccumulator(),
		PrimaryRayGenerator: PerspectiveCamera(),
		Integrator:          MonteCarloIntegrator(debugFlags, numBounces),
		PostProcess: []PipelineStage{
			TonemapSimpleReinhard(exposure),
		},
	}

	if debugFlags&FrameBuffer == FrameBuffer {
		pipeline.PostProcess = append(pipeline.PostProcess, DebugFrameBuffer("debug-fb.png"))
	}

	return pipeline
}

// Clear the accumulator buffer.
func ClearAccumulator() PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		return tr.resources.ClearAccumulator(blockReq)
	}
}

// Use a perspective camera for the primary ray generation stage.
func PerspectiveCamera() PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		return tr.resources.GeneratePrimaryRays(blockReq, tr.cameraPosition, tr.cameraFrustrum)
	}
}

// Apply simple Reinhard tone-mapping.
func TonemapSimpleReinhard(exposure float32) PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		return tr.resources.TonemapSimpleReinhard(blockReq, exposure)
	}
}

// Use a montecarlo pathtracer implementation.
func MonteCarloIntegrator(debugFlags DebugFlag, numBounces uint32) PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		var err error

		start := time.Now()
		numPixels := int(blockReq.FrameW * blockReq.BlockH)
		numEmissives := uint32(len(tr.sceneData.EmissivePrimitives))

		var activeRayBuf uint32 = 0

		// Intersect primary rays outside of the loop
		// Use packet query intersector for GPUs as opencl forces CPU
		// to use a local workgroup size equal to 1
		if tr.device.Type == device.GpuDevice {
			_, err = tr.resources.RayPacketIntersectionQuery(activeRayBuf, numPixels)
		} else {
			_, err = tr.resources.RayIntersectionQuery(activeRayBuf, numPixels)
		}
		if err != nil {
			return time.Since(start), err
		}

		if debugFlags&PrimaryRayIntersectionDepth == PrimaryRayIntersectionDepth {
			_, err = tr.resources.DebugPrimaryRayIntersectionDepth(blockReq)
			err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, "debug-primary-intersection-depth.png")
			if err != nil {
				return time.Since(start), err
			}
		}
		if debugFlags&PrimaryRayIntersectionNormals == PrimaryRayIntersectionNormals {
			_, err = tr.resources.DebugPrimaryRayIntersectionNormals(blockReq)
			err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, "debug-primary-intersection-normals.png")
			if err != nil {
				return time.Since(start), err
			}
		}

		var bounce uint32
		for bounce = 0; bounce < numBounces; bounce++ {
			// Shade misses
			if tr.sceneData.SceneDiffuseMatIndex != -1 {
				if bounce == 0 {
					_, err = tr.resources.ShadePrimaryRayMisses(uint32(tr.sceneData.SceneDiffuseMatIndex), activeRayBuf, numPixels)
				} else {
					_, err = tr.resources.ShadeIndirectRayMisses(uint32(tr.sceneData.SceneDiffuseMatIndex), activeRayBuf, numPixels)
				}
				if err != nil {
					return time.Since(start), err
				}
			}

			// Shade hits
			_, err = tr.resources.ShadeHits(bounce, rand.Uint32(), numEmissives, activeRayBuf, numPixels)
			if err != nil {
				return time.Since(start), err
			}

			if debugFlags&Throughput == Throughput {
				_, err = tr.resources.DebugThroughput(blockReq)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-throughput-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			// Process intersections for occlusion rays and accumulate emissive samples for non occluded paths
			_, err := tr.resources.RayIntersectionTest(2, numPixels)
			if err != nil {
				return time.Since(start), err
			}

			_, err = tr.resources.AccumulateEmissiveSamples(2, numPixels)
			if err != nil {
				return time.Since(start), err
			}

			if debugFlags&AllEmissiveSamples == AllEmissiveSamples {
				_, err = tr.resources.DebugEmissiveSamples(blockReq, 0, 0)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-emissive-all-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			if debugFlags&VisibleEmissiveSamples == VisibleEmissiveSamples {
				_, err = tr.resources.DebugEmissiveSamples(blockReq, 1, 0)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-emissive-vis-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			if debugFlags&OccludedEmissiveSamples == OccludedEmissiveSamples {
				_, err = tr.resources.DebugEmissiveSamples(blockReq, 0, 1)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-emissive-occ-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			if debugFlags&Accumulator == Accumulator {
				_, err = tr.resources.DebugAccumulator(blockReq)
				err = dumpDebugBuffer(err, tr.resources, blockReq.FrameW, blockReq.FrameH, fmt.Sprintf("debug-accumulator-%03d.png", bounce))
				if err != nil {
					return time.Since(start), err
				}
			}

			// Process intersections for indirect rays
			if bounce+1 < numBounces {
				activeRayBuf = 1 - activeRayBuf
				_, err = tr.resources.RayIntersectionQuery(activeRayBuf, numPixels)
				if err != nil {
					return time.Since(start), err
				}
			}
		}
		return time.Since(start), nil
	}
}

// Dump a copy of the RGBA framebuffer.
func DebugFrameBuffer(imgFile string) PipelineStage {
	return func(tr *Tracer, blockReq *tracer.BlockRequest) (time.Duration, error) {
		start := time.Now()

		f, err := os.Create(imgFile)
		if err != nil {
			return 0, err
		}
		defer f.Close()

		im := image.NewRGBA(image.Rect(0, 0, int(blockReq.FrameW), int(blockReq.FrameH)))
		err = tr.resources.buffers.FrameBuffer.ReadData(0, 0, tr.resources.buffers.FrameBuffer.Size(), im.Pix)
		if err != nil {
			return 0, err
		}

		return time.Since(start), png.Encode(f, im)
	}
}

// Dump debug buffer to png file.
func dumpDebugBuffer(debugKernelError error, dr *deviceResources, frameW, frameH uint32, imgFile string) error {
	if debugKernelError != nil {
		return debugKernelError
	}
	f, err := os.Create(imgFile)
	if err != nil {
		return err
	}
	defer f.Close()

	im := image.NewRGBA(image.Rect(0, 0, int(frameW), int(frameH)))
	err = dr.buffers.DebugOutput.ReadData(0, 0, dr.buffers.DebugOutput.Size(), im.Pix)
	if err != nil {
		return err
	}

	return png.Encode(f, im)
}

func readCounter(dr *deviceResources, counterIndex uint32) uint32 {
	out := make([]uint32, 1)
	dr.buffers.RayCounters[counterIndex].ReadData(0, 0, 4, out)
	return out[0]
}
<|endoftext|>


<commit_before>package subcmd

import (
	"os"
	"testing"
)

var (
	TEST_DIR = os.Getenv("GOPATH") + "/src/github.com/ieee0824/thor/test"
)

func TestReadExternalVariablesFromFile(t *testing.T) {
	var EXTERNSL_DIR = TEST_DIR + "/readExternalVariablesFromFiles"
	result, err := readExternalVariablesFromFile(EXTERNSL_DIR)
	if err != nil {
		t.Error(err)
	} else if len(result) != 2 {
		t.Errorf("The number of elements is invalid. Originally it should be %v, but it is actually %v.", 2, len(result))
	}
}

func TestReadExternalVariables(t *testing.T) {
	var EXTERNSL_DIRS = []string{
		"readExternalVariablesFiles/dir1",
		"readExternalVariablesFiles/dir2",
		"readExternalVariablesFiles/dir3",
	}

	result, err := readExternalVariables(EXTERNSL_DIRS...)
	if err != nil {
		t.Error(err)
	} else if len(result) != 2 {
		t.Errorf("The number of elements is invalid. Originally it should be %v, but it is actually %v.", 2, len(result))
	}
}
<commit_msg>test用にディレクトリを作る<commit_after>package subcmd

import (
	"os"
	"testing"
)

var (
	TEST_DIR = os.Getenv("GOPATH") + "/src/github.com/ieee0824/thor/test"
)

func TestReadExternalVariablesFromFile(t *testing.T) {
	var EXTERNSL_DIR = TEST_DIR + "/readExternalVariablesFromFiles"
	result, err := readExternalVariablesFromFile(EXTERNSL_DIR)
	if err != nil {
		t.Error(err)
	} else if len(result) != 2 {
		t.Errorf("The number of elements is invalid. Originally it should be %v, but it is actually %v.", 2, len(result))
	}
}

func TestReadExternalVariables(t *testing.T) {
	var EXTERNSL_DIRS = []string{
		TEST_DIR + "readExternalVariablesFiles/dir1",
		TEST_DIR + "readExternalVariablesFiles/dir2",
		TEST_DIR + "readExternalVariablesFiles/dir3",
	}

	// test用にディレクトリを作る
	if err := os.Mkdir(TEST_DIR+"readExternalVariablesFiles/dir3", 0777); err != nil {
		t.Error(err)
	}

	result, err := readExternalVariables(EXTERNSL_DIRS...)
	if err != nil {
		t.Error(err)
	} else if len(result) != 2 {
		t.Errorf("The number of elements is invalid. Originally it should be %v, but it is actually %v.", 2, len(result))
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package entrypoint

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"syscall"
	"time"

	"github.com/sirupsen/logrus"
)

const (
	// InternalErrorCode is what we write to the marker file to
	// indicate that we failed to start the wrapped command
	InternalErrorCode = 127
	// AbortedErrorCode is what we write to the marker file to
	// indicate that we were terminated via a signal.
	AbortedErrorCode = 130

	// DefaultTimeout is the default timeout for the test
	// process before SIGINT is sent
	DefaultTimeout = 120 * time.Minute

	// DefaultGracePeriod is the default timeout for the test
	// process after SIGINT is sent before SIGKILL is sent
	DefaultGracePeriod = 15 * time.Second
)

var (
	// errTimedOut is used as the command's error when the command
	// is terminated after the timeout is reached
	errTimedOut = errors.New("process timed out")
	// errAborted is used as the command's error when the command
	// is shut down by an external signal
	errAborted = errors.New("process aborted")
)

// Run executes the test process then writes the exit code to the marker file.
// This function returns the status code that should be passed to os.Exit().
func (o Options) Run() int {
	code, err := o.ExecuteProcess()
	if err != nil {
		logrus.WithError(err).Error("Error executing test process: %v.", err)
	}
	if err := o.mark(code); err != nil {
		logrus.WithError(err).Error("Error writing exit code to marker file: %v.", err)
		return InternalErrorCode
	}
	return code
}

// ExecuteProcess creates the artifact directory then executes the process as
// configured, writing the output to the process log.
func (o Options) ExecuteProcess() (int, error) {
	if o.ArtifactDir != "" {
		if err := os.MkdirAll(o.ArtifactDir, os.ModePerm); err != nil {
			return InternalErrorCode, fmt.Errorf("could not create artifact directory(%s): %v", o.ArtifactDir, err)
		}
	}
	processLogFile, err := os.Create(o.ProcessLog)
	if err != nil {
		return InternalErrorCode, fmt.Errorf("could not create process logfile(%s): %v", o.ProcessLog, err)
	}
	defer processLogFile.Close()

	output := io.MultiWriter(os.Stdout, processLogFile)
	logrus.SetOutput(output)
	defer logrus.SetOutput(os.Stdout)

	executable := o.Args[0]
	var arguments []string
	if len(o.Args) > 1 {
		arguments = o.Args[1:]
	}
	command := exec.Command(executable, arguments...)
	command.Stderr = output
	command.Stdout = output
	if err := command.Start(); err != nil {
		return InternalErrorCode, fmt.Errorf("could not start the process: %v", err)
	}

	// if we get asked to terminate we need to forward
	// that to the wrapped process as if it timed out
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)

	timeout := optionOrDefault(o.Timeout, DefaultTimeout)
	gracePeriod := optionOrDefault(o.GracePeriod, DefaultGracePeriod)
	var commandErr error
	cancelled, aborted := false, false
	done := make(chan error)
	go func() {
		done <- command.Wait()
	}()
	select {
	case err := <-done:
		commandErr = err
	case <-time.After(timeout):
		logrus.Errorf("Process did not finish before %s timeout", timeout)
		cancelled = true
		gracefullyTerminate(command, done, gracePeriod)
	case s := <-interrupt:
		logrus.Errorf("Entrypoint received interrupt: %v", s)
		cancelled = true
		aborted = true
		gracefullyTerminate(command, done, gracePeriod)
	}

	var returnCode int
	if cancelled {
		if aborted {
			commandErr = errAborted
			returnCode = AbortedErrorCode
		} else {
			commandErr = errTimedOut
			returnCode = InternalErrorCode
		}
	} else {
		if status, ok := command.ProcessState.Sys().(syscall.WaitStatus); ok {
			returnCode = status.ExitStatus()
		} else if commandErr == nil {
			returnCode = 0
		} else {
			returnCode = 1
		}

		if returnCode != 0 {
			commandErr = fmt.Errorf("wrapped process failed: %v", commandErr)
		}
	}
	return returnCode, commandErr
}

func (o *Options) mark(exitCode int) error {
	content := []byte(strconv.Itoa(exitCode))

	// create temp file in the same directory as the desired marker file
	dir := filepath.Dir(o.MarkerFile)
	tempFile, err := ioutil.TempFile(dir, "temp-marker")
	if err != nil {
		return fmt.Errorf("could not create temp marker file in %s: %v", dir, err)
	}
	// write the exit code to the tempfile, sync to disk and close
	if _, err = tempFile.Write(content); err != nil {
		return fmt.Errorf("could not write to temp marker file (%s): %v", tempFile.Name(), err)
	}
	if err = tempFile.Sync(); err != nil {
		return fmt.Errorf("could not sync temp marker file (%s): %v", tempFile.Name(), err)
	}
	tempFile.Close()
	// set desired permission bits, then rename to the desired file name
	if err = os.Chmod(tempFile.Name(), os.ModePerm); err != nil {
		return fmt.Errorf("could not chmod (%x) temp marker file (%s): %v", os.ModePerm, tempFile.Name(), err)
	}
	if err := os.Rename(tempFile.Name(), o.MarkerFile); err != nil {
		return fmt.Errorf("could not move marker file to destination path (%s): %v", o.MarkerFile, err)
	}
	return nil
}

// optionOrDefault defaults to a value if option
// is the zero value
func optionOrDefault(option, defaultValue time.Duration) time.Duration {
	if option == 0 {
		return defaultValue
	}

	return option
}

func gracefullyTerminate(command *exec.Cmd, done <-chan error, gracePeriod time.Duration) {
	if err := command.Process.Signal(os.Interrupt); err != nil {
		logrus.WithError(err).Error("Could not interrupt process after timeout")
	}
	select {
	case <-done:
		logrus.Errorf("Process gracefully exited before %s grace period", gracePeriod)
		// but we ignore the output error as we will want errTimedOut
	case <-time.After(gracePeriod):
		logrus.Errorf("Process did not exit before %s grace period", gracePeriod)
		if err := command.Process.Kill(); err != nil {
			logrus.WithError(err).Error("Could not kill process after grace period")
		}
	}
}
<commit_msg>Format errors in entrypoint correctly<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package entrypoint

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"syscall"
	"time"

	"github.com/sirupsen/logrus"
)

const (
	// InternalErrorCode is what we write to the marker file to
	// indicate that we failed to start the wrapped command
	InternalErrorCode = 127
	// AbortedErrorCode is what we write to the marker file to
	// indicate that we were terminated via a signal.
	AbortedErrorCode = 130

	// DefaultTimeout is the default timeout for the test
	// process before SIGINT is sent
	DefaultTimeout = 120 * time.Minute

	// DefaultGracePeriod is the default timeout for the test
	// process after SIGINT is sent before SIGKILL is sent
	DefaultGracePeriod = 15 * time.Second
)

var (
	// errTimedOut is used as the command's error when the command
	// is terminated after the timeout is reached
	errTimedOut = errors.New("process timed out")
	// errAborted is used as the command's error when the command
	// is shut down by an external signal
	errAborted = errors.New("process aborted")
)

// Run executes the test process then writes the exit code to the marker file.
// This function returns the status code that should be passed to os.Exit().
func (o Options) Run() int {
	code, err := o.ExecuteProcess()
	if err != nil {
		logrus.WithError(err).Error("Error executing test process")
	}
	if err := o.mark(code); err != nil {
		logrus.WithError(err).Error("Error writing exit code to marker file")
		return InternalErrorCode
	}
	return code
}

// ExecuteProcess creates the artifact directory then executes the process as
// configured, writing the output to the process log.
func (o Options) ExecuteProcess() (int, error) {
	if o.ArtifactDir != "" {
		if err := os.MkdirAll(o.ArtifactDir, os.ModePerm); err != nil {
			return InternalErrorCode, fmt.Errorf("could not create artifact directory(%s): %v", o.ArtifactDir, err)
		}
	}
	processLogFile, err := os.Create(o.ProcessLog)
	if err != nil {
		return InternalErrorCode, fmt.Errorf("could not create process logfile(%s): %v", o.ProcessLog, err)
	}
	defer processLogFile.Close()

	output := io.MultiWriter(os.Stdout, processLogFile)
	logrus.SetOutput(output)
	defer logrus.SetOutput(os.Stdout)

	executable := o.Args[0]
	var arguments []string
	if len(o.Args) > 1 {
		arguments = o.Args[1:]
	}
	command := exec.Command(executable, arguments...)
	command.Stderr = output
	command.Stdout = output
	if err := command.Start(); err != nil {
		return InternalErrorCode, fmt.Errorf("could not start the process: %v", err)
	}

	// if we get asked to terminate we need to forward
	// that to the wrapped process as if it timed out
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)

	timeout := optionOrDefault(o.Timeout, DefaultTimeout)
	gracePeriod := optionOrDefault(o.GracePeriod, DefaultGracePeriod)
	var commandErr error
	cancelled, aborted := false, false
	done := make(chan error)
	go func() {
		done <- command.Wait()
	}()
	select {
	case err := <-done:
		commandErr = err
	case <-time.After(timeout):
		logrus.Errorf("Process did not finish before %s timeout", timeout)
		cancelled = true
		gracefullyTerminate(command, done, gracePeriod)
	case s := <-interrupt:
		logrus.Errorf("Entrypoint received interrupt: %v", s)
		cancelled = true
		aborted = true
		gracefullyTerminate(command, done, gracePeriod)
	}

	var returnCode int
	if cancelled {
		if aborted {
			commandErr = errAborted
			returnCode = AbortedErrorCode
		} else {
			commandErr = errTimedOut
			returnCode = InternalErrorCode
		}
	} else {
		if status, ok := command.ProcessState.Sys().(syscall.WaitStatus); ok {
			returnCode = status.ExitStatus()
		} else if commandErr == nil {
			returnCode = 0
		} else {
			returnCode = 1
		}

		if returnCode != 0 {
			commandErr = fmt.Errorf("wrapped process failed: %v", commandErr)
		}
	}
	return returnCode, commandErr
}

func (o *Options) mark(exitCode int) error {
	content := []byte(strconv.Itoa(exitCode))

	// create temp file in the same directory as the desired marker file
	dir := filepath.Dir(o.MarkerFile)
	tempFile, err := ioutil.TempFile(dir, "temp-marker")
	if err != nil {
		return fmt.Errorf("could not create temp marker file in %s: %v", dir, err)
	}
	// write the exit code to the tempfile, sync to disk and close
	if _, err = tempFile.Write(content); err != nil {
		return fmt.Errorf("could not write to temp marker file (%s): %v", tempFile.Name(), err)
	}
	if err = tempFile.Sync(); err != nil {
		return fmt.Errorf("could not sync temp marker file (%s): %v", tempFile.Name(), err)
	}
	tempFile.Close()
	// set desired permission bits, then rename to the desired file name
	if err = os.Chmod(tempFile.Name(), os.ModePerm); err != nil {
		return fmt.Errorf("could not chmod (%x) temp marker file (%s): %v", os.ModePerm, tempFile.Name(), err)
	}
	if err := os.Rename(tempFile.Name(), o.MarkerFile); err != nil {
		return fmt.Errorf("could not move marker file to destination path (%s): %v", o.MarkerFile, err)
	}
	return nil
}

// optionOrDefault defaults to a value if option
// is the zero value
func optionOrDefault(option, defaultValue time.Duration) time.Duration {
	if option == 0 {
		return defaultValue
	}

	return option
}

func gracefullyTerminate(command *exec.Cmd, done <-chan error, gracePeriod time.Duration) {
	if err := command.Process.Signal(os.Interrupt); err != nil {
		logrus.WithError(err).Error("Could not interrupt process after timeout")
	}
	select {
	case <-done:
		logrus.Errorf("Process gracefully exited before %s grace period", gracePeriod)
		// but we ignore the output error as we will want errTimedOut
	case <-time.After(gracePeriod):
		logrus.Errorf("Process did not exit before %s grace period", gracePeriod)
		if err := command.Process.Kill(); err != nil {
			logrus.WithError(err).Error("Could not kill process after grace period")
		}
	}
}
<|endoftext|>


<commit_before>package pathutil_test

import (
	"encoding/json"
	"github.com/JaSei/pathutil-go"
	"github.com/stretchr/testify/assert"
	"testing"
)

type FileSource struct {
	Path string `json:"path"`
	Size int    `json:"size"`
}

type FileInfo struct {
	FileId  string       `json:"file_id"`
	Sources []FileSource `json:"sources"`
}

var expected = FileInfo{
	FileId: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",
	Sources: []FileSource{
		FileSource{Path: "c:\\tmp\\empty_file", Size: 0},
		FileSource{Path: "/tmp/empty_file", Size: 0},
	},
}

func TestLoadJsonViaReader(t *testing.T) {
	path, err := pathutil.NewPath("example.json")
	assert.Nil(t, err)

	reader, file, err := path.OpenReader()
	assert.Nil(t, err)
	defer func() {
		assert.Nil(t, file.Close())
	}()
	assert.NotNil(t, reader)

	decodedJson := new(FileInfo)

	err = json.NewDecoder(reader).Decode(decodedJson)
	if !assert.Nil(t, err) {
		t.Log(err)
	}

	assert.Equal(t, &expected, decodedJson)
}

func TestLoadJsonViaSlurp(t *testing.T) {
	path, err := pathutil.NewPath("example.json")
	assert.Nil(t, err)

	jsonBytes, err := path.Slurp()
	assert.Nil(t, err)

	decodedJson := new(FileInfo)
	json.Unmarshal(jsonBytes, decodedJson)

	assert.Equal(t, &expected, decodedJson)

}
<commit_msg>fix test<commit_after>package pathutil_test

import (
	"encoding/json"
	"github.com/JaSei/pathutil-go"
	"github.com/stretchr/testify/assert"
	"testing"
)

type FileSource struct {
	Path string `json:"path"`
	Size int    `json:"size"`
}

type FileInfo struct {
	FileId  string       `json:"file_id"`
	Sources []FileSource `json:"sources"`
}

var expected = FileInfo{
	FileId: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",
	Sources: []FileSource{
		FileSource{Path: "c:\\tmp\\empty_file", Size: 0},
		FileSource{Path: "/tmp/empty_file", Size: 0},
	},
}

func TestLoadJsonViaReader(t *testing.T) {
	path, err := pathutil.NewPath("example.json")
	assert.Nil(t, err)

	reader, file, err := path.OpenReader()
	assert.Nil(t, err)
	defer func() {
		assert.Nil(t, file.Close())
	}()
	assert.NotNil(t, reader)

	decodedJson := new(FileInfo)

	err = json.NewDecoder(reader).Decode(decodedJson)
	if !assert.Nil(t, err) {
		t.Log(err)
	}

	assert.Equal(t, &expected, decodedJson)
}

func TestLoadJsonViaSlurp(t *testing.T) {
	path, err := pathutil.NewPath("example.json")
	assert.Nil(t, err)

	jsonBytes, err := path.SlurpBytes()
	assert.Nil(t, err)

	decodedJson := new(FileInfo)
	json.Unmarshal(jsonBytes, decodedJson)

	assert.Equal(t, &expected, decodedJson)

}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/common/response"

	"github.com/codahale/sneaker"
)

var (
	ErrPathNotFound           = errors.New("required a path name to store keys")
	ErrRequiredValuesNotFound = errors.New("required fields not found to store")
)

// KeyValue holds the credentials whatever you want as key-value pair
type KeyValue map[string]interface{}

type Credentials struct {
	KeyValues []KeyValue `json:"keyValue"`
}

type SneakerS3 struct {
	*sneaker.Manager
}

// Store stores the given credentials on s3
func (s *SneakerS3) Store(u *url.URL, h http.Header, cr *Credentials, context *models.Context) (int, http.Header, interface{}, error) {
	pathName := u.Query().Get("pathName")
	if pathName == "" {
		return response.NewBadRequest(ErrPathNotFound)
	}

	if !context.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	if cr.KeyValues == nil {
		return response.NewBadRequest(ErrRequiredValuesNotFound)
	}

	// convert credentials to bytes
	byt, err := json.Marshal(cr)
	if err != nil {
		return response.NewBadRequest(err)
	}

	// bytes need to imlement io.Reader interface
	// then we can use struct as 2.parameter of manager.Upload function
	aa := bytes.NewReader(byt)

	// if another requeest comes to same pathName, its data will be updated.
	// and new incoming data is gonna override the old data
	err = s.Manager.Upload(pathName, aa)
	if err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(nil)
}

func (s *SneakerS3) Get(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	pathName := u.Query().Get("pathName")
	if pathName == "" {
		return response.NewBadRequest(ErrPathNotFound)
	}

	if !context.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	downArray := []string{pathName}
	down, err := s.Manager.Download(downArray)
	if err != nil {
		return response.NewBadRequest(err)
	}

	x := &Credentials{}

	downX := bytes.NewReader(down[pathName])
	if err := json.NewDecoder(downX).Decode(x); err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(x)
}
<commit_msg>go: remove array keyvalue<commit_after>package api

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/common/response"

	"github.com/codahale/sneaker"
)

var (
	ErrPathNotFound           = errors.New("required a path name to store keys")
	ErrRequiredValuesNotFound = errors.New("required fields not found to store")
)

// KeyValue holds the credentials whatever you want as key-value pair
type KeyValue map[string]interface{}

type Credentials struct {
	KeyValue KeyValue `json:"keyValue"`
}

type SneakerS3 struct {
	*sneaker.Manager
}

// Store stores the given credentials on s3
func (s *SneakerS3) Store(u *url.URL, h http.Header, cr *Credentials, context *models.Context) (int, http.Header, interface{}, error) {
	pathName := u.Query().Get("pathName")
	if pathName == "" {
		return response.NewBadRequest(ErrPathNotFound)
	}

	if !context.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	if cr.KeyValue == nil {
		return response.NewBadRequest(ErrRequiredValuesNotFound)
	}

	// convert credentials to bytes
	byt, err := json.Marshal(cr)
	if err != nil {
		return response.NewBadRequest(err)
	}

	// bytes need to imlement io.Reader interface
	// then we can use struct as 2.parameter of manager.Upload function
	aa := bytes.NewReader(byt)

	// if another requeest comes to same pathName, its data will be updated.
	// and new incoming data is gonna override the old data
	err = s.Manager.Upload(pathName, aa)
	if err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(nil)
}

func (s *SneakerS3) Get(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) {
	pathName := u.Query().Get("pathName")
	if pathName == "" {
		return response.NewBadRequest(ErrPathNotFound)
	}

	if !context.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	downArray := []string{pathName}
	down, err := s.Manager.Download(downArray)
	if err != nil {
		return response.NewBadRequest(err)
	}

	x := &Credentials{}

	downX := bytes.NewReader(down[pathName])
	if err := json.NewDecoder(downX).Decode(x); err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(x)
}
<|endoftext|>


<commit_before>package errlist

import (
	"bytes"
	"fmt"
)

// Errors is a list of errors.
type Errors []error

// New returns a list of errors optionally populated with the given errors.
func New(errors ...error) Errors {
	return Errors(errors)
}

// Append adds a non-nil error to the list and returns it.
func (e Errors) Append(err error) Errors {
	if err == nil {
		return e
	}
	return append(e, err)
}

// ErrorOrNil returns the error list if it contains at least one error.
// Otherwise nil is returned.
func (e Errors) ErrorOrNil() error {
	if len(e) == 0 {
		return nil
	}
	return e
}

// Error implements the error interface.
// A single error is formatted as usual.
// Multiple errors are formatted per line with a summary of the error count.
func (e Errors) Error() string {
	if len(e) == 1 {
		return e[0].Error()
	}
	buf := &bytes.Buffer{}
	fmt.Fprintf(buf, "%d errors occurred:\n", len(e))
	for _, err := range e {
		fmt.Fprintf(buf, "* %s\n", err.Error())
	}
	return buf.String()
}
<commit_msg>Add Errors.HasError<commit_after>package errlist

import (
	"bytes"
	"fmt"
)

// Errors is a list of errors.
type Errors []error

// New returns a list of errors optionally populated with the given errors.
func New(errors ...error) Errors {
	return Errors(errors)
}

// Append adds a non-nil error to the list and returns it.
func (e Errors) Append(err error) Errors {
	if err == nil {
		return e
	}
	return append(e, err)
}

// ErrorOrNil returns the error list if it contains at least one error.
// Otherwise nil is returned.
func (e Errors) ErrorOrNil() error {
	if len(e) == 0 {
		return nil
	}
	return e
}

// HasError returns true if the list contains at least one error.
func (e Errors) HasError() bool {
	return len(e) > 0
}

// Error implements the error interface.
// A single error is formatted as usual.
// Multiple errors are formatted per line with a summary of the error count.
func (e Errors) Error() string {
	if len(e) == 1 {
		return e[0].Error()
	}
	buf := &bytes.Buffer{}
	fmt.Fprintf(buf, "%d errors occurred:\n", len(e))
	for _, err := range e {
		fmt.Fprintf(buf, "* %s\n", err.Error())
	}
	return buf.String()
}
<|endoftext|>


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

package graphics

import (
	"errors"
	"fmt"
	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)

func glMatrix(m *[4][4]float64) []float32 {
	return []float32{
		float32(m[0][0]), float32(m[1][0]), float32(m[2][0]), float32(m[3][0]),
		float32(m[0][1]), float32(m[1][1]), float32(m[2][1]), float32(m[3][1]),
		float32(m[0][2]), float32(m[1][2]), float32(m[2][2]), float32(m[3][2]),
		float32(m[0][3]), float32(m[1][3]), float32(m[2][3]), float32(m[3][3]),
	}
}

type Matrix interface {
	Element(i, j int) float64
}

var vertices = make([]int16, 16*quadsMaxNum)

var shadersInitialized = false

func drawTexture(c *opengl.Context, texture opengl.Texture, projectionMatrix *[4][4]float64, quads TextureQuads, geo Matrix, color Matrix, mode opengl.CompositeMode) error {
	c.BlendFunc(mode)

	// NOTE: WebGL doesn't seem to have Check gl.MAX_ELEMENTS_VERTICES or gl.MAX_ELEMENTS_INDICES so far.
	// Let's use them to compare to len(quads) in the future.

	if !shadersInitialized {
		if err := initialize(c); err != nil {
			return err
		}
		shadersInitialized = true
	}

	l := quads.Len()
	if l == 0 {
		return nil
	}
	if quadsMaxNum < l {
		return errors.New(fmt.Sprintf("len(quads) must be equal to or less than %d", quadsMaxNum))
	}

	p := programContext{
		program:          programTexture,
		context:          c,
		projectionMatrix: glMatrix(projectionMatrix),
		texture:          texture,
		geoM:             geo,
		colorM:           color,
	}
	p.begin()
	defer p.end()
	n := quads.SetVertices(vertices)
	if n == 0 {
		return nil
	}
	c.BufferSubData(c.ArrayBuffer, vertices[:16*n])
	c.DrawElements(c.Triangles, 6*n)
	return nil
}
<commit_msg>graphics: Early return when num of vertices is 0<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package graphics

import (
	"errors"
	"fmt"
	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)

func glMatrix(m *[4][4]float64) []float32 {
	return []float32{
		float32(m[0][0]), float32(m[1][0]), float32(m[2][0]), float32(m[3][0]),
		float32(m[0][1]), float32(m[1][1]), float32(m[2][1]), float32(m[3][1]),
		float32(m[0][2]), float32(m[1][2]), float32(m[2][2]), float32(m[3][2]),
		float32(m[0][3]), float32(m[1][3]), float32(m[2][3]), float32(m[3][3]),
	}
}

type Matrix interface {
	Element(i, j int) float64
}

var vertices = make([]int16, 16*quadsMaxNum)

var shadersInitialized = false

func drawTexture(c *opengl.Context, texture opengl.Texture, projectionMatrix *[4][4]float64, quads TextureQuads, geo Matrix, color Matrix, mode opengl.CompositeMode) error {
	c.BlendFunc(mode)

	// NOTE: WebGL doesn't seem to have Check gl.MAX_ELEMENTS_VERTICES or gl.MAX_ELEMENTS_INDICES so far.
	// Let's use them to compare to len(quads) in the future.

	if !shadersInitialized {
		if err := initialize(c); err != nil {
			return err
		}
		shadersInitialized = true
	}

	l := quads.Len()
	if l == 0 {
		return nil
	}
	if quadsMaxNum < l {
		return errors.New(fmt.Sprintf("len(quads) must be equal to or less than %d", quadsMaxNum))
	}

	p := programContext{
		program:          programTexture,
		context:          c,
		projectionMatrix: glMatrix(projectionMatrix),
		texture:          texture,
		geoM:             geo,
		colorM:           color,
	}
	n := quads.SetVertices(vertices)
	if n == 0 {
		return nil
	}
	p.begin()
	defer p.end()
	c.BufferSubData(c.ArrayBuffer, vertices[:16*n])
	c.DrawElements(c.Triangles, 6*n)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Alex Browne and Soroush Pour.
// Allrights reserved. Use of this source code is
// governed by the MIT license, which can be found
// in the LICENSE file.

package router

import (
	"log"
	"regexp"
	"strings"

	"github.com/go-humble/detect"
	"github.com/gopherjs/gopherjs/js"
	"honnef.co/go/js/dom"
)

var (
	// browserSupportsPushState will be true if the current browser
	// supports history.pushState and the onpopstate event.
	browserSupportsPushState bool
	document                 dom.HTMLDocument
)

func init() {
	if detect.IsBrowser() {
		// We only want to initialize certain things if we are running
		// inside a browser. Otherwise, they will cause the program to
		// panic.
		var ok bool
		document, ok = dom.GetWindow().Document().(dom.HTMLDocument)
		if !ok {
			panic("Could not convert document to dom.HTMLDocument")
		}
		browserSupportsPushState = (js.Global.Get("onpopstate") != js.Undefined) &&
			(js.Global.Get("history") != js.Undefined) &&
			(js.Global.Get("history").Get("pushState") != js.Undefined)
	}
}

// Router is responsible for handling routes. If history.pushState is
// supported, it uses it to navigate from page to page and will listen
// to the "onpopstate" event. Otherwise, it sets the hash component of the
// url and listens to changes via the "onhashchange" event.
type Router struct {
	// routes is the set of routes for this router.
	routes []*route
	// ShouldInterceptLinks tells the router whether or not to intercept click events
	// on links and call the Navigate method instead of the default behavior.
	// If it is set to true, the router will automatically intercept links when
	// Start, Navigate, or Back are called, or when the onpopstate event is triggered.
	ShouldInterceptLinks bool
	// ForceHashURL tells the router to use the hash component of the url to
	// represent different routes, even if history.pushState is supported.
	ForceHashURL bool
	// Verbose determines whether or not the router will log to console.log.
	// If true, the router will log a message if, e.g., a match cannot be found for
	// a particular path.
	Verbose bool
	// listener is the js.Object representation of a listener callback.
	// It is required in order to use the RemoveEventListener method
	listener func(*js.Object)
}

// Context is used as an argument to Handlers
type Context struct {
	// Params is the parameters from the url as a map of names to values.
	Params map[string]string
	// Path is the path that triggered this particular route. If the hash
	// fallback is being used, the value of path does not include the '#'
	// symbol.
	Path string
	// InitialLoad is true iff this route was triggered during the initial
	// page load. I.e. it is true if this is the first path that the browser
	// was visiting when the javascript finished loading.
	InitialLoad bool
}

// Handler is a function which is run in response to a specific
// route. A Handler takes a Context as an argument, which gives
// handler functions access to path parameters and other important
// information.
type Handler func(context *Context)

// New creates and returns a new router
func New() *Router {
	return &Router{
		routes: []*route{},
	}
}

// route is a representation of a specific route
type route struct {
	// regex is a regex pattern that matches route
	regex *regexp.Regexp
	// paramNames is an ordered list of parameters expected
	// by route handler
	paramNames []string
	// handler called when route is matched
	handler Handler
}

// HandleFunc will cause the router to call f whenever window.location.pathname
// (or window.location.hash, if history.pushState is not supported) matches path.
// path can contain any number of parameters which are denoted with curly brackets.
// So, for example, a path argument of "users/{id}" will be triggered when the user
// visits users/123 and will call the handler function with params["id"] = "123".
func (r *Router) HandleFunc(path string, handler Handler) {
	r.routes = append(r.routes, newRoute(path, handler))
}

// newRoute returns a route with the given arguments. paramNames and regex
// are calculated from the path
func newRoute(path string, handler Handler) *route {
	route := &route{
		handler: handler,
	}
	strs := strings.Split(path, "/")
	strs = removeEmptyStrings(strs)
	pattern := `^`
	for _, str := range strs {
		if str[0] == '{' && str[len(str)-1] == '}' {
			pattern += `/`
			pattern += `([\w+-]*)`
			route.paramNames = append(route.paramNames, str[1:(len(str)-1)])
		} else {
			pattern += `/`
			pattern += str
		}
	}
	pattern += `/?$`
	route.regex = regexp.MustCompile(pattern)
	return route
}

// Start causes the router to listen for changes to window.location and
// trigger the appropriate handler whenever there is a change.
func (r *Router) Start() {
	if browserSupportsPushState && !r.ForceHashURL {
		r.pathChanged(getPath(), true)
		r.watchHistory()
	} else {
		r.setInitialHash()
		r.watchHash()
	}
	if r.ShouldInterceptLinks {
		r.InterceptLinks()
	}
}

// Stop causes the router to stop listening for changes, and therefore
// the router will not trigger any more router.Handler functions.
func (r *Router) Stop() {
	if browserSupportsPushState && !r.ForceHashURL {
		js.Global.Set("onpopstate", nil)
	} else {
		js.Global.Set("onhashchange", nil)
	}
}

// Navigate will trigger the handler associated with the given path
// and update window.location accordingly. If the browser supports
// history.pushState, that will be used. Otherwise, Navigate will
// set the hash component of window.location to the given path.
func (r *Router) Navigate(path string) {
	if browserSupportsPushState && !r.ForceHashURL {
		pushState(path)
		r.pathChanged(path, false)
	} else {
		setHash(path)
	}
	if r.ShouldInterceptLinks {
		r.InterceptLinks()
	}
}

// CanNavigate returns true if the specified path can be navigated by the
// router, and false otherwise
func (r *Router) CanNavigate(path string) bool {
	if bestRoute, _ := r.findBestRoute(path); bestRoute != nil {
		return true
	}
	return false
}

// Back will cause the browser to go back to the previous page.
// It has the same effect as the user pressing the back button,
// and is just a wrapper around history.back()
func (r *Router) Back() {
	js.Global.Get("history").Call("back")
	if r.ShouldInterceptLinks {
		r.InterceptLinks()
	}
}

// InterceptLinks intercepts click events on links of the form
// and calls router.Navigate("/foo") instead, which triggers the appropriate Handler
// instead of requesting a new page from the server. Since InterceptLinks works by
// setting event listeners in the DOM, you must call this function whenever the DOM
// is changed. Alternatively, you can set r.ShouldInterceptLinks to true, which will
// trigger this function whenever Start, Navigate, or Back are called, or when the
// onpopstate event is triggered. Even with r.ShouldInterceptLinks set to true, you
// may still need to call this function if you change the DOM manually without
// triggering a route.
func (r *Router) InterceptLinks() {
	for _, link := range document.Links() {
		href := link.GetAttribute("href")
		switch {
		case href == "":
			return
		case strings.HasPrefix(href, "http://"), strings.HasPrefix(href, "https://"), strings.HasPrefix(href, "//"):
			// These are external links and should behave normally.
			return
		case strings.HasPrefix(href, "#"):
			// These are anchor links and should behave normally.
			// Recall that even when we are using the hash trick, href
			// attributes should be relative paths without the "#" and
			// router will handle them appropriately.
			return
		case strings.HasPrefix(href, "/"):
			// These are relative links. The kind that we want to intercept.
			if r.listener != nil {
				// Remove the old listener (if any)
				link.RemoveEventListener("click", true, r.listener)
			}
			r.listener = link.AddEventListener("click", true, r.interceptLink)
		}
	}
}

// interceptLink is intended to be used as a callback function. It stops
// the default behavior of event and instead calls r.Navigate, passing through
// the link's href property.
func (r *Router) interceptLink(event dom.Event) {
	path := event.CurrentTarget().GetAttribute("href")
	// Only intercept the click event if we have a route which matches
	// Otherwise, just do the default.
	if bestRoute, _ := r.findBestRoute(path); bestRoute != nil {
		event.PreventDefault()
		go r.Navigate(path)
	}
}

// setInitialHash will set hash to / if there is currently no hash.
func (r *Router) setInitialHash() {
	if getHash() == "" {
		setHash("/")
	} else {
		r.pathChanged(getPathFromHash(getHash()), true)
	}
}

// pathChanged should be called whenever the path changes and will trigger
// the appropriate handler. initial should be true iff this is the first
// time the javascript is loaded on the page.
func (r *Router) pathChanged(path string, initial bool) {
	bestRoute, tokens := r.findBestRoute(path)
	// If no routes match, we throw console error and no handlers are called
	if bestRoute == nil {
		if r.Verbose {
			log.Println("Could not find route to match: " + path)
		}
		return
	}
	// Create the context and pass it through to the handler
	c := &Context{
		Path:        path,
		InitialLoad: initial,
		Params:      map[string]string{},
	}
	for i, token := range tokens {
		c.Params[bestRoute.paramNames[i]] = token
	}
	bestRoute.handler(c)
}

// findBestRoute compares the given path against regex patterns of routes.
// Preference given to routes with most literal (non-parameter) matches. For
// example if we have the following:
//   Route 1: /todos/work
//   Route 2: /todos/{category}
// And the path argument is "/todos/work", the bestRoute would be todos/work
// because the string "work" matches the literal in Route 1.
func (r Router) findBestRoute(path string) (bestRoute *route, tokens []string) {
	leastParams := -1
	for _, route := range r.routes {
		matches := route.regex.FindStringSubmatch(path)
		if matches != nil {
			if (leastParams == -1) || (len(matches) < leastParams) {
				leastParams = len(matches)
				bestRoute = route
				tokens = matches[1:]
			}
		}
	}
	return bestRoute, tokens
}

// removeEmptyStrings removes any empty strings from strings
func removeEmptyStrings(strings []string) []string {
	result := []string{}
	for _, s := range strings {
		if s != "" {
			result = append(result, s)
		}
	}
	return result
}

// watchHash listens to the onhashchange event and calls r.pathChanged when
// it changes
func (r *Router) watchHash() {
	js.Global.Set("onhashchange", func() {
		go func() {
			path := getPathFromHash(getHash())
			r.pathChanged(path, false)
		}()
	})
}

// watchHistory listens to the onpopstate event and calls r.pathChanged when
// it changes
func (r *Router) watchHistory() {
	js.Global.Set("onpopstate", func() {
		go func() {
			r.pathChanged(getPath(), false)
			if r.ShouldInterceptLinks {
				r.InterceptLinks()
			}
		}()
	})
}

// getPathFromHash returns everything after the "#" character in hash.
func getPathFromHash(hash string) string {
	return strings.SplitN(hash, "#", 2)[1]
}

// getHash is an alias for js.Global.Get("location").Get("hash").String()
func getHash() string {
	return js.Global.Get("location").Get("hash").String()
}

// setHash is an alias for js.Global.Get("location").Set("hash", hash)
func setHash(hash string) {
	js.Global.Get("location").Set("hash", hash)
}

// getPath is an alias for js.Global.Get("location").Get("pathname").String()
func getPath() string {
	return js.Global.Get("location").Get("pathname").String()
}

// pushState is an alias for js.Global.Get("history").Call("pushState", nil, "", path)
func pushState(path string) {
	js.Global.Get("history").Call("pushState", nil, "", path)
}
<commit_msg>InterceptLinks should iterate all links - don't abort iteration certain href are matched<commit_after>// Copyright 2015 Alex Browne and Soroush Pour.
// Allrights reserved. Use of this source code is
// governed by the MIT license, which can be found
// in the LICENSE file.

package router

import (
	"log"
	"regexp"
	"strings"

	"github.com/go-humble/detect"
	"github.com/gopherjs/gopherjs/js"
	"honnef.co/go/js/dom"
)

var (
	// browserSupportsPushState will be true if the current browser
	// supports history.pushState and the onpopstate event.
	browserSupportsPushState bool
	document                 dom.HTMLDocument
)

func init() {
	if detect.IsBrowser() {
		// We only want to initialize certain things if we are running
		// inside a browser. Otherwise, they will cause the program to
		// panic.
		var ok bool
		document, ok = dom.GetWindow().Document().(dom.HTMLDocument)
		if !ok {
			panic("Could not convert document to dom.HTMLDocument")
		}
		browserSupportsPushState = (js.Global.Get("onpopstate") != js.Undefined) &&
			(js.Global.Get("history") != js.Undefined) &&
			(js.Global.Get("history").Get("pushState") != js.Undefined)
	}
}

// Router is responsible for handling routes. If history.pushState is
// supported, it uses it to navigate from page to page and will listen
// to the "onpopstate" event. Otherwise, it sets the hash component of the
// url and listens to changes via the "onhashchange" event.
type Router struct {
	// routes is the set of routes for this router.
	routes []*route
	// ShouldInterceptLinks tells the router whether or not to intercept click events
	// on links and call the Navigate method instead of the default behavior.
	// If it is set to true, the router will automatically intercept links when
	// Start, Navigate, or Back are called, or when the onpopstate event is triggered.
	ShouldInterceptLinks bool
	// ForceHashURL tells the router to use the hash component of the url to
	// represent different routes, even if history.pushState is supported.
	ForceHashURL bool
	// Verbose determines whether or not the router will log to console.log.
	// If true, the router will log a message if, e.g., a match cannot be found for
	// a particular path.
	Verbose bool
	// listener is the js.Object representation of a listener callback.
	// It is required in order to use the RemoveEventListener method
	listener func(*js.Object)
}

// Context is used as an argument to Handlers
type Context struct {
	// Params is the parameters from the url as a map of names to values.
	Params map[string]string
	// Path is the path that triggered this particular route. If the hash
	// fallback is being used, the value of path does not include the '#'
	// symbol.
	Path string
	// InitialLoad is true iff this route was triggered during the initial
	// page load. I.e. it is true if this is the first path that the browser
	// was visiting when the javascript finished loading.
	InitialLoad bool
}

// Handler is a function which is run in response to a specific
// route. A Handler takes a Context as an argument, which gives
// handler functions access to path parameters and other important
// information.
type Handler func(context *Context)

// New creates and returns a new router
func New() *Router {
	return &Router{
		routes: []*route{},
	}
}

// route is a representation of a specific route
type route struct {
	// regex is a regex pattern that matches route
	regex *regexp.Regexp
	// paramNames is an ordered list of parameters expected
	// by route handler
	paramNames []string
	// handler called when route is matched
	handler Handler
}

// HandleFunc will cause the router to call f whenever window.location.pathname
// (or window.location.hash, if history.pushState is not supported) matches path.
// path can contain any number of parameters which are denoted with curly brackets.
// So, for example, a path argument of "users/{id}" will be triggered when the user
// visits users/123 and will call the handler function with params["id"] = "123".
func (r *Router) HandleFunc(path string, handler Handler) {
	r.routes = append(r.routes, newRoute(path, handler))
}

// newRoute returns a route with the given arguments. paramNames and regex
// are calculated from the path
func newRoute(path string, handler Handler) *route {
	route := &route{
		handler: handler,
	}
	strs := strings.Split(path, "/")
	strs = removeEmptyStrings(strs)
	pattern := `^`
	for _, str := range strs {
		if str[0] == '{' && str[len(str)-1] == '}' {
			pattern += `/`
			pattern += `([\w+-]*)`
			route.paramNames = append(route.paramNames, str[1:(len(str)-1)])
		} else {
			pattern += `/`
			pattern += str
		}
	}
	pattern += `/?$`
	route.regex = regexp.MustCompile(pattern)
	return route
}

// Start causes the router to listen for changes to window.location and
// trigger the appropriate handler whenever there is a change.
func (r *Router) Start() {
	if browserSupportsPushState && !r.ForceHashURL {
		r.pathChanged(getPath(), true)
		r.watchHistory()
	} else {
		r.setInitialHash()
		r.watchHash()
	}
	if r.ShouldInterceptLinks {
		r.InterceptLinks()
	}
}

// Stop causes the router to stop listening for changes, and therefore
// the router will not trigger any more router.Handler functions.
func (r *Router) Stop() {
	if browserSupportsPushState && !r.ForceHashURL {
		js.Global.Set("onpopstate", nil)
	} else {
		js.Global.Set("onhashchange", nil)
	}
}

// Navigate will trigger the handler associated with the given path
// and update window.location accordingly. If the browser supports
// history.pushState, that will be used. Otherwise, Navigate will
// set the hash component of window.location to the given path.
func (r *Router) Navigate(path string) {
	if browserSupportsPushState && !r.ForceHashURL {
		pushState(path)
		r.pathChanged(path, false)
	} else {
		setHash(path)
	}
	if r.ShouldInterceptLinks {
		r.InterceptLinks()
	}
}

// CanNavigate returns true if the specified path can be navigated by the
// router, and false otherwise
func (r *Router) CanNavigate(path string) bool {
	if bestRoute, _ := r.findBestRoute(path); bestRoute != nil {
		return true
	}
	return false
}

// Back will cause the browser to go back to the previous page.
// It has the same effect as the user pressing the back button,
// and is just a wrapper around history.back()
func (r *Router) Back() {
	js.Global.Get("history").Call("back")
	if r.ShouldInterceptLinks {
		r.InterceptLinks()
	}
}

// InterceptLinks intercepts click events on links of the form
// and calls router.Navigate("/foo") instead, which triggers the appropriate Handler
// instead of requesting a new page from the server. Since InterceptLinks works by
// setting event listeners in the DOM, you must call this function whenever the DOM
// is changed. Alternatively, you can set r.ShouldInterceptLinks to true, which will
// trigger this function whenever Start, Navigate, or Back are called, or when the
// onpopstate event is triggered. Even with r.ShouldInterceptLinks set to true, you
// may still need to call this function if you change the DOM manually without
// triggering a route.
func (r *Router) InterceptLinks() {
	for _, link := range document.Links() {
		href := link.GetAttribute("href")
		switch {
		case href == "":

		case strings.HasPrefix(href, "http://"), strings.HasPrefix(href, "https://"), strings.HasPrefix(href, "//"):
			// These are external links and should behave normally.

		case strings.HasPrefix(href, "#"):
			// These are anchor links and should behave normally.
			// Recall that even when we are using the hash trick, href
			// attributes should be relative paths without the "#" and
			// router will handle them appropriately.

		case strings.HasPrefix(href, "/"):
			// These are relative links. The kind that we want to intercept.
			if r.listener != nil {
				// Remove the old listener (if any)
				link.RemoveEventListener("click", true, r.listener)
			}
			r.listener = link.AddEventListener("click", true, r.interceptLink)
		}
	}
}

// interceptLink is intended to be used as a callback function. It stops
// the default behavior of event and instead calls r.Navigate, passing through
// the link's href property.
func (r *Router) interceptLink(event dom.Event) {
	path := event.CurrentTarget().GetAttribute("href")
	// Only intercept the click event if we have a route which matches
	// Otherwise, just do the default.
	if bestRoute, _ := r.findBestRoute(path); bestRoute != nil {
		event.PreventDefault()
		go r.Navigate(path)
	}
}

// setInitialHash will set hash to / if there is currently no hash.
func (r *Router) setInitialHash() {
	if getHash() == "" {
		setHash("/")
	} else {
		r.pathChanged(getPathFromHash(getHash()), true)
	}
}

// pathChanged should be called whenever the path changes and will trigger
// the appropriate handler. initial should be true iff this is the first
// time the javascript is loaded on the page.
func (r *Router) pathChanged(path string, initial bool) {
	bestRoute, tokens := r.findBestRoute(path)
	// If no routes match, we throw console error and no handlers are called
	if bestRoute == nil {
		if r.Verbose {
			log.Println("Could not find route to match: " + path)
		}
		return
	}
	// Create the context and pass it through to the handler
	c := &Context{
		Path:        path,
		InitialLoad: initial,
		Params:      map[string]string{},
	}
	for i, token := range tokens {
		c.Params[bestRoute.paramNames[i]] = token
	}
	bestRoute.handler(c)
}

// findBestRoute compares the given path against regex patterns of routes.
// Preference given to routes with most literal (non-parameter) matches. For
// example if we have the following:
//   Route 1: /todos/work
//   Route 2: /todos/{category}
// And the path argument is "/todos/work", the bestRoute would be todos/work
// because the string "work" matches the literal in Route 1.
func (r Router) findBestRoute(path string) (bestRoute *route, tokens []string) {
	leastParams := -1
	for _, route := range r.routes {
		matches := route.regex.FindStringSubmatch(path)
		if matches != nil {
			if (leastParams == -1) || (len(matches) < leastParams) {
				leastParams = len(matches)
				bestRoute = route
				tokens = matches[1:]
			}
		}
	}
	return bestRoute, tokens
}

// removeEmptyStrings removes any empty strings from strings
func removeEmptyStrings(strings []string) []string {
	result := []string{}
	for _, s := range strings {
		if s != "" {
			result = append(result, s)
		}
	}
	return result
}

// watchHash listens to the onhashchange event and calls r.pathChanged when
// it changes
func (r *Router) watchHash() {
	js.Global.Set("onhashchange", func() {
		go func() {
			path := getPathFromHash(getHash())
			r.pathChanged(path, false)
		}()
	})
}

// watchHistory listens to the onpopstate event and calls r.pathChanged when
// it changes
func (r *Router) watchHistory() {
	js.Global.Set("onpopstate", func() {
		go func() {
			r.pathChanged(getPath(), false)
			if r.ShouldInterceptLinks {
				r.InterceptLinks()
			}
		}()
	})
}

// getPathFromHash returns everything after the "#" character in hash.
func getPathFromHash(hash string) string {
	return strings.SplitN(hash, "#", 2)[1]
}

// getHash is an alias for js.Global.Get("location").Get("hash").String()
func getHash() string {
	return js.Global.Get("location").Get("hash").String()
}

// setHash is an alias for js.Global.Get("location").Set("hash", hash)
func setHash(hash string) {
	js.Global.Get("location").Set("hash", hash)
}

// getPath is an alias for js.Global.Get("location").Get("pathname").String()
func getPath() string {
	return js.Global.Get("location").Get("pathname").String()
}

// pushState is an alias for js.Global.Get("history").Call("pushState", nil, "", path)
func pushState(path string) {
	js.Global.Get("history").Call("pushState", nil, "", path)
}
<|endoftext|>


<commit_before>package common

import (
	"cgl.tideland.biz/identifier"
	"encoding/hex"
	"fmt"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"time"
)

type StepSecurityGroup struct {
	SecurityGroupId string
	SSHPort         int
	VpcId           string

	createdGroupId string
}

func (s *StepSecurityGroup) Run(state map[string]interface{}) multistep.StepAction {
	ec2conn := state["ec2"].(*ec2.EC2)
	ui := state["ui"].(packer.Ui)

	if s.SecurityGroupId != "" {
		log.Printf("Using specified security group: %s", s.SecurityGroupId)
		state["securityGroupId"] = s.SecurityGroupId
		return multistep.ActionContinue
	}

	if s.SSHPort == 0 {
		panic("SSHPort must be set to a non-zero value.")
	}

	// Create the group
	ui.Say("Creating temporary security group for this instance...")
	groupName := fmt.Sprintf("packer %s", hex.EncodeToString(identifier.NewUUID().Raw()))
	log.Printf("Temporary group name: %s", groupName)
	group := ec2.SecurityGroup{
		Name:        groupName,
		Description: "Temporary group for Packer",
		VpcId:       s.VpcId,
	}
	groupResp, err := ec2conn.CreateSecurityGroup(group)
	if err != nil {
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the group ID so we can delete it later
	s.createdGroupId = groupResp.Id

	// Authorize the SSH access
	perms := []ec2.IPPerm{
		ec2.IPPerm{
			Protocol:  "tcp",
			FromPort:  s.SSHPort,
			ToPort:    s.SSHPort,
			SourceIPs: []string{"0.0.0.0/0"},
		},
	}

	ui.Say("Authorizing SSH access on the temporary security group...")
	if _, err := ec2conn.AuthorizeSecurityGroup(groupResp.SecurityGroup, perms); err != nil {
		err := fmt.Errorf("Error creating temporary security group: %s", err)
		state["error"] = err
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set some state data for use in future steps
	state["securityGroupId"] = s.createdGroupId

	return multistep.ActionContinue
}

func (s *StepSecurityGroup) Cleanup(state map[string]interface{}) {
	if s.createdGroupId == "" {
		return
	}

	ec2conn := state["ec2"].(*ec2.EC2)
	ui := state["ui"].(packer.Ui)

	ui.Say("Deleting temporary security group...")

	var err error
	for i := 0; i < 5; i++ {
		_, err = ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: s.createdGroupId})
		if err != nil {
			log.Printf("Error deleting security group: %s", err)
			time.Sleep(5 * time.Second)
			continue
		}
	}

	if err != nil {
		ui.Error(fmt.Sprintf(
			"Error cleaning up security group. Please delete the group manually: %s", s.createdGroupId))
	}
}
<commit_msg>builder/amazon/common: correct logic in deleting secutiry group<commit_after>package common

import (
	"cgl.tideland.biz/identifier"
	"encoding/hex"
	"fmt"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"time"
)

type StepSecurityGroup struct {
	SecurityGroupId string
	SSHPort         int
	VpcId           string

	createdGroupId string
}

func (s *StepSecurityGroup) Run(state map[string]interface{}) multistep.StepAction {
	ec2conn := state["ec2"].(*ec2.EC2)
	ui := state["ui"].(packer.Ui)

	if s.SecurityGroupId != "" {
		log.Printf("Using specified security group: %s", s.SecurityGroupId)
		state["securityGroupId"] = s.SecurityGroupId
		return multistep.ActionContinue
	}

	if s.SSHPort == 0 {
		panic("SSHPort must be set to a non-zero value.")
	}

	// Create the group
	ui.Say("Creating temporary security group for this instance...")
	groupName := fmt.Sprintf("packer %s", hex.EncodeToString(identifier.NewUUID().Raw()))
	log.Printf("Temporary group name: %s", groupName)
	group := ec2.SecurityGroup{
		Name:        groupName,
		Description: "Temporary group for Packer",
		VpcId:       s.VpcId,
	}
	groupResp, err := ec2conn.CreateSecurityGroup(group)
	if err != nil {
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the group ID so we can delete it later
	s.createdGroupId = groupResp.Id

	// Authorize the SSH access
	perms := []ec2.IPPerm{
		ec2.IPPerm{
			Protocol:  "tcp",
			FromPort:  s.SSHPort,
			ToPort:    s.SSHPort,
			SourceIPs: []string{"0.0.0.0/0"},
		},
	}

	ui.Say("Authorizing SSH access on the temporary security group...")
	if _, err := ec2conn.AuthorizeSecurityGroup(groupResp.SecurityGroup, perms); err != nil {
		err := fmt.Errorf("Error creating temporary security group: %s", err)
		state["error"] = err
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set some state data for use in future steps
	state["securityGroupId"] = s.createdGroupId

	return multistep.ActionContinue
}

func (s *StepSecurityGroup) Cleanup(state map[string]interface{}) {
	if s.createdGroupId == "" {
		return
	}

	ec2conn := state["ec2"].(*ec2.EC2)
	ui := state["ui"].(packer.Ui)

	ui.Say("Deleting temporary security group...")

	var err error
	for i := 0; i < 5; i++ {
		_, err = ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: s.createdGroupId})
		if err == nil {
			break
		}

		log.Printf("Error deleting security group: %s", err)
		time.Sleep(5 * time.Second)
	}

	if err != nil {
		ui.Error(fmt.Sprintf(
			"Error cleaning up security group. Please delete the group manually: %s", s.createdGroupId))
	}
}
<|endoftext|>


<commit_before>package tradier

import (
	"bytes"
	"encoding/json"
	"reflect"
	"testing"
	"time"
)

var timesalesJSONSingle = []byte(`{
	"series": {
		"data": {
      "price": 4.32,
      "time": "2017-01-27T09:30:00",
      "timestamp": 1485527400,
      "volume": 125
    }
	}
}`)

var timesalesJSONArray = []byte(`{
	"series": {
		"data": [
      {
        "price": 4.32,
        "time": "2017-01-27T09:30:00",
        "timestamp": 1485527400,
        "volume": 125
      },
      {
        "price": 4.34,
        "time": "2017-01-27T09:31:20",
        "timestamp": 1485527480,
        "volume": 100
      }
    ]
	}
}`)

var timesaleJSONEmpty = []byte(`{
	"series": "null"
}`)

var timesalesSingle = &Series{
	Data: []*Data{
		&Data{
			Price:     Float64(4.32),
			Time:      &Time{time.Date(2017, 01, 27, 9, 30, 0, 0, time.UTC)},
			Timestamp: Int(1485527400),
			Volume:    Int(125),
		},
	},
}

var timesalesArray = &Series{
	Data: []*Data{
		&Data{
			Price:     Float64(4.32),
			Time:      &Time{time.Date(2017, 01, 27, 9, 30, 0, 0, time.UTC)},
			Timestamp: Int(1485527400),
			Volume:    Int(125),
		},
		&Data{
			Price:     Float64(4.34),
			Time:      &Time{time.Date(2017, 01, 27, 9, 31, 20, 0, time.UTC)},
			Timestamp: Int(1485527480),
			Volume:    Int(100),
		},
	},
}

var timesalesEmpty = &Series{}

func TestTimesales_UnmarshalJSON_Single(t *testing.T) {
	want := timesalesSingle

	got := &Series{}
	err := json.Unmarshal(timesalesJSONSingle, got)
	if err != nil {
		t.Errorf("Series.UnmarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %+v want: %+v", got, want)
	}
}

func TestTimesales_MarshalJSON_Single(t *testing.T) {
	buf := &bytes.Buffer{}
	err := json.Compact(buf, timesalesJSONSingle)
	want := buf.Bytes()
	if err != nil {
		t.Error(err)
	}

	got, err := json.Marshal(timesalesSingle)
	if err != nil {
		t.Errorf("Accounts.MarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %s want: %s", got, want)
	}
}

func TestTimesales_UnmarshalJSON_Array(t *testing.T) {
	want := timesalesArray

	got := &Series{}
	err := json.Unmarshal(timesalesJSONArray, got)
	if err != nil {
		t.Errorf("Accounts.UnmarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %+v want: %+v", got, want)
	}
}

func TestTimesales_MarshalJSON_Array(t *testing.T) {
	buf := &bytes.Buffer{}
	err := json.Compact(buf, timesalesJSONArray)
	want := buf.Bytes()
	if err != nil {
		t.Error(err)
	}

	got, err := json.Marshal(timesalesArray)
	if err != nil {
		t.Errorf("Accounts.MarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %s want: %s", got, want)
	}
}

// func TestAccounts_UnmarshalJSON_Null(t *testing.T) {
// 	want := accountsNull
//
// 	got := &Accounts{}
// 	err := json.Unmarshal(accountsJSONNull, got)
// 	if err != nil {
// 		t.Errorf("Accounts.UnmarshalJSON error: %s", err)
// 	}
//
// 	if !reflect.DeepEqual(got, want) {
// 		t.Errorf("got: %+v want: %+v", got, want)
// 	}
// }
//
// func TestAccounts_MarshalJSON_Null(t *testing.T) {
// 	buf := &bytes.Buffer{}
// 	err := json.Compact(buf, accountsJSONNull)
// 	want := buf.Bytes()
// 	if err != nil {
// 		t.Error(err)
// 	}
//
// 	got, err := json.Marshal(&accountsNull)
// 	if err != nil {
// 		t.Errorf("Accounts.MarshalJSON error: %s", err)
// 	}
//
// 	if !reflect.DeepEqual(got, want) {
// 		t.Errorf("got: %s want: %s", got, want)
// 	}
// }
<commit_msg>Add marshal and unmarshal tests for empty series<commit_after>package tradier

import (
	"bytes"
	"encoding/json"
	"reflect"
	"testing"
	"time"
)

var timesalesJSONSingle = []byte(`{
	"series": {
		"data": {
      "price": 4.32,
      "time": "2017-01-27T09:30:00",
      "timestamp": 1485527400,
      "volume": 125
    }
	}
}`)

var timesalesJSONArray = []byte(`{
	"series": {
		"data": [
      {
        "price": 4.32,
        "time": "2017-01-27T09:30:00",
        "timestamp": 1485527400,
        "volume": 125
      },
      {
        "price": 4.34,
        "time": "2017-01-27T09:31:20",
        "timestamp": 1485527480,
        "volume": 100
      }
    ]
	}
}`)

var timesaleJSONEmpty = []byte(`{
	"series": "null"
}`)

var timesalesSingle = &Series{
	Data: []*Data{
		&Data{
			Price:     Float64(4.32),
			Time:      &Time{time.Date(2017, 01, 27, 9, 30, 0, 0, time.UTC)},
			Timestamp: Int(1485527400),
			Volume:    Int(125),
		},
	},
}

var timesalesArray = &Series{
	Data: []*Data{
		&Data{
			Price:     Float64(4.32),
			Time:      &Time{time.Date(2017, 01, 27, 9, 30, 0, 0, time.UTC)},
			Timestamp: Int(1485527400),
			Volume:    Int(125),
		},
		&Data{
			Price:     Float64(4.34),
			Time:      &Time{time.Date(2017, 01, 27, 9, 31, 20, 0, time.UTC)},
			Timestamp: Int(1485527480),
			Volume:    Int(100),
		},
	},
}

var timesalesEmpty = &Series{}

func TestTimesales_UnmarshalJSON_Single(t *testing.T) {
	want := timesalesSingle

	got := &Series{}
	err := json.Unmarshal(timesalesJSONSingle, got)
	if err != nil {
		t.Errorf("Series.UnmarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %+v want: %+v", got, want)
	}
}

func TestTimesales_MarshalJSON_Single(t *testing.T) {
	buf := &bytes.Buffer{}
	err := json.Compact(buf, timesalesJSONSingle)
	want := buf.Bytes()
	if err != nil {
		t.Error(err)
	}

	got, err := json.Marshal(timesalesSingle)
	if err != nil {
		t.Errorf("Series.MarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %s want: %s", got, want)
	}
}

func TestTimesales_UnmarshalJSON_Array(t *testing.T) {
	want := timesalesArray

	got := &Series{}
	err := json.Unmarshal(timesalesJSONArray, got)
	if err != nil {
		t.Errorf("Series.UnmarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %+v want: %+v", got, want)
	}
}

func TestTimesales_MarshalJSON_Array(t *testing.T) {
	buf := &bytes.Buffer{}
	err := json.Compact(buf, timesalesJSONArray)
	want := buf.Bytes()
	if err != nil {
		t.Error(err)
	}

	got, err := json.Marshal(timesalesArray)
	if err != nil {
		t.Errorf("Series.MarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %s want: %s", got, want)
	}
}

func TestTimesales_UnmarshalJSON_Null(t *testing.T) {
	want := timesalesEmpty

	got := &Series{}
	err := json.Unmarshal(timesaleJSONEmpty, got)
	if err != nil {
		t.Errorf("Series.UnmarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %+v want: %+v", got, want)
	}
}

func TestTimesales_MarshalJSON_Null(t *testing.T) {
	buf := &bytes.Buffer{}
	err := json.Compact(buf, timesaleJSONEmpty)
	want := buf.Bytes()
	if err != nil {
		t.Error(err)
	}

	got, err := json.Marshal(&timesalesEmpty)
	if err != nil {
		t.Errorf("Series.MarshalJSON error: %s", err)
	}

	if !reflect.DeepEqual(got, want) {
		t.Errorf("got: %s want: %s", got, want)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017-2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dhclient

import (
	"net"
	"net/url"

	"github.com/u-root/dhcp4"
	"github.com/u-root/dhcp4/dhcp4opts"
)

// Packet4 implements convenience functions for DHCPv4 packets.
type Packet4 struct {
	P *dhcp4.Packet
}

// NewPacket4 wraps a DHCPv4 packet with some convenience methods.
func NewPacket4(p *dhcp4.Packet) *Packet4 {
	return &Packet4{
		P: p,
	}
}

// Lease returns the IPNet assigned.
func (p *Packet4) Lease() *net.IPNet {
	netmask, err := dhcp4opts.GetSubnetMask(p.P.Options)
	if err != nil {
		// If they did not offer a subnet mask, we choose the most
		// restrictive option.
		netmask = []byte{255, 255, 255, 255}
	}

	return &net.IPNet{
		IP:   p.P.YIAddr,
		Mask: net.IPMask(netmask),
	}
}

// Gateway returns the gateway IP assigned.
//
// OptionRouter is used as opposed to GIAddr, which seems unused by most DHCP
// servers?
func (p *Packet4) Gateway() net.IP {
	gw, err := dhcp4opts.GetRouters(p.P.Options)
	if err != nil {
		return nil
	}
	return gw[0]
}

// DNS returns DNS IPs assigned.
func (p *Packet4) DNS() []net.IP {
	ips, err := dhcp4opts.GetDomainNameServers(p.P.Options)
	if err != nil {
		return nil
	}
	return []net.IP(ips)
}

// Boot returns the boot file assigned.
func (p *Packet4) Boot() (url.URL, error) {
	// TODO: This is not 100% right -- if a certain option is set, this
	// stuff is encoded in options instead of in the packet's BootFile and
	// ServerName fields.

	// While the default is tftp, servers may specify HTTP or FTP URIs.
	u, err := url.Parse(p.P.BootFile)
	if err != nil {
		return url.URL{}, err
	}

	if len(u.Scheme) == 0 {
		// Defaults to tftp is not specified.
		u.Scheme = "tftp"
		u.Path = p.P.BootFile
		if len(p.P.ServerName) == 0 {
			server, err := dhcp4opts.GetServerIdentifier(p.P.Options)
			if err != nil {
				return url.URL{}, err
			}
			u.Host = net.IP(server).String()
		} else {
			u.Host = p.P.ServerName
		}
	}
	return *u, nil
}
<commit_msg>dhclient: Fix errors resulting from dep update<commit_after>// Copyright 2017-2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dhclient

import (
	"net"
	"net/url"

	"github.com/u-root/dhcp4"
	"github.com/u-root/dhcp4/dhcp4opts"
)

// Packet4 implements convenience functions for DHCPv4 packets.
type Packet4 struct {
	P *dhcp4.Packet
}

// NewPacket4 wraps a DHCPv4 packet with some convenience methods.
func NewPacket4(p *dhcp4.Packet) *Packet4 {
	return &Packet4{
		P: p,
	}
}

// Lease returns the IPNet assigned.
func (p *Packet4) Lease() *net.IPNet {
	netmask := dhcp4opts.GetSubnetMask(p.P.Options)
	if netmask == nil {
		// If they did not offer a subnet mask, we choose the most
		// restrictive option.
		netmask = []byte{255, 255, 255, 255}
	}

	return &net.IPNet{
		IP:   p.P.YIAddr,
		Mask: net.IPMask(netmask),
	}
}

// Gateway returns the gateway IP assigned.
//
// OptionRouter is used as opposed to GIAddr, which seems unused by most DHCP
// servers?
func (p *Packet4) Gateway() net.IP {
	gw := dhcp4opts.GetRouters(p.P.Options)
	if gw == nil {
		return nil
	}
	return gw[0]
}

// DNS returns DNS IPs assigned.
func (p *Packet4) DNS() []net.IP {
	ips := dhcp4opts.GetDomainNameServers(p.P.Options)
	if ips == nil {
		return nil
	}
	return []net.IP(ips)
}

// Boot returns the boot file assigned.
func (p *Packet4) Boot() (url.URL, error) {
	// TODO: This is not 100% right -- if a certain option is set, this
	// stuff is encoded in options instead of in the packet's BootFile and
	// ServerName fields.

	// While the default is tftp, servers may specify HTTP or FTP URIs.
	u, err := url.Parse(p.P.BootFile)
	if err != nil {
		return url.URL{}, err
	}

	if len(u.Scheme) == 0 {
		// Defaults to tftp is not specified.
		u.Scheme = "tftp"
		u.Path = p.P.BootFile
		if len(p.P.ServerName) == 0 {
			server := dhcp4opts.GetServerIdentifier(p.P.Options)
			if server == nil {
				return url.URL{}, err
			}
			u.Host = net.IP(server).String()
		} else {
			u.Host = p.P.ServerName
		}
	}
	return *u, nil
}
<|endoftext|>


<commit_before>package logging

import (
	"fmt"
	"os"
	"runtime/debug"
	"strings"
	"sync"

	"github.com/hashicorp/go-hclog"
)

// This output is shown if a panic happens.
const panicOutput = `
!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!

Terraform crashed! This is always indicative of a bug within Terraform.
Please report the crash with Terraform[1] so that we can fix this.

When reporting bugs, please include your terraform version, the stack trace
shown below, and any additional information which may help replicate the issue.

[1]: https://github.com/hashicorp/terraform/issues

!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!

`

// PanicHandler is called to recover from an internal panic in Terraform, and
// augments the standard stack trace with a more user friendly error message.
// PanicHandler must be called as a defered function.
func PanicHandler() {
	recovered := recover()
	if recovered == nil {
		return
	}

	fmt.Fprint(os.Stderr, panicOutput)
	fmt.Fprint(os.Stderr, recovered)

	// When called from a deferred function, debug.PrintStack will include the
	// full stack from the point of the pending panic.
	debug.PrintStack()
	os.Exit(2)
}

const pluginPanicOutput = `
Stack trace from the %[1]s plugin:

%s

Error: The %[1]s plugin crashed!

This is always indicative of a bug within the plugin. It would be immensely
helpful if you could report the crash with the plugin's maintainers so that it
can be fixed. The output above should help diagnose the issue.
`

// PluginPanics returns a series of provider panics that were collected during
// execution, and formatted for output.
func PluginPanics() []string {
	return panics.allPanics()
}

// panicRecorder provides a registry to check for plugin panics that may have
// happened when a plugin suddenly terminates.
type panicRecorder struct {
	sync.Mutex

	// panics maps the plugin name to the panic output lines received from
	// the logger.
	panics map[string][]string

	// maxLines is the max number of lines we'll record after seeing a
	// panic header. Since this is going to be printed in the UI output, we
	// don't want to destroy the scrollback. In most cases, the first few lines
	// of the stack trace is all that are required.
	maxLines int
}

// registerPlugin returns an accumulator function which will accept lines of
// a panic stack trace to collect into an error when requested.
func (p *panicRecorder) registerPlugin(name string) func(string) {
	p.Lock()
	defer p.Unlock()

	// In most cases we shouldn't be starting a plugin if it already
	// panicked, but clear out previous entries just in case.
	delete(p.panics, name)

	count := 0

	// this callback is used by the logger to store panic output
	return func(line string) {
		p.Lock()
		defer p.Unlock()

		// stop recording if there are too many lines.
		if count > p.maxLines {
			return
		}
		count++

		p.panics[name] = append(p.panics[name], line)
	}
}

func (p *panicRecorder) allPanics() []string {
	p.Lock()
	defer p.Unlock()

	var res []string
	for name, lines := range p.panics {
		if len(lines) == 0 {
			continue
		}

		res = append(res, fmt.Sprintf(pluginPanicOutput, name, strings.Join(lines, "\n")))
	}
	return res
}

// logPanicWrapper wraps an hclog.Logger and intercepts and records any output
// that appears to be a panic.
type logPanicWrapper struct {
	hclog.Logger
	panicRecorder func(string)
	inPanic       bool
}

// go-plugin will create a new named logger for each plugin binary.
func (l *logPanicWrapper) Named(name string) hclog.Logger {
	return &logPanicWrapper{
		Logger:        l.Logger.Named(name),
		panicRecorder: panics.registerPlugin(name),
	}
}

// we only need to implement Debug, since that is the default output level used
// by go-plugin when encountering unstructured output on stderr.
func (l *logPanicWrapper) Debug(msg string, args ...interface{}) {
	// We don't have access to the binary itself, so guess based on the stderr
	// output if this is the start of the traceback. An occasional false
	// positive shouldn't be a big deal, since this is only retrieved after an
	// error of some sort.

	panicPrefix := strings.HasPrefix(msg, "panic: ") || strings.HasPrefix(msg, "fatal error: ")

	l.inPanic = l.inPanic || panicPrefix

	if l.inPanic && l.panicRecorder != nil {
		l.panicRecorder(msg)
	}

	l.Logger.Debug(msg, args...)
}
<commit_msg>add note about calling PanicHandler first<commit_after>package logging

import (
	"fmt"
	"os"
	"runtime/debug"
	"strings"
	"sync"

	"github.com/hashicorp/go-hclog"
)

// This output is shown if a panic happens.
const panicOutput = `
!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!

Terraform crashed! This is always indicative of a bug within Terraform.
Please report the crash with Terraform[1] so that we can fix this.

When reporting bugs, please include your terraform version, the stack trace
shown below, and any additional information which may help replicate the issue.

[1]: https://github.com/hashicorp/terraform/issues

!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!

`

// PanicHandler is called to recover from an internal panic in Terraform, and
// augments the standard stack trace with a more user friendly error message.
// PanicHandler must be called as a defered function, and must be the first
// defer called at the start of a new goroutine.
func PanicHandler() {
	recovered := recover()
	if recovered == nil {
		return
	}

	fmt.Fprint(os.Stderr, panicOutput)
	fmt.Fprint(os.Stderr, recovered)

	// When called from a deferred function, debug.PrintStack will include the
	// full stack from the point of the pending panic.
	debug.PrintStack()
	os.Exit(2)
}

const pluginPanicOutput = `
Stack trace from the %[1]s plugin:

%s

Error: The %[1]s plugin crashed!

This is always indicative of a bug within the plugin. It would be immensely
helpful if you could report the crash with the plugin's maintainers so that it
can be fixed. The output above should help diagnose the issue.
`

// PluginPanics returns a series of provider panics that were collected during
// execution, and formatted for output.
func PluginPanics() []string {
	return panics.allPanics()
}

// panicRecorder provides a registry to check for plugin panics that may have
// happened when a plugin suddenly terminates.
type panicRecorder struct {
	sync.Mutex

	// panics maps the plugin name to the panic output lines received from
	// the logger.
	panics map[string][]string

	// maxLines is the max number of lines we'll record after seeing a
	// panic header. Since this is going to be printed in the UI output, we
	// don't want to destroy the scrollback. In most cases, the first few lines
	// of the stack trace is all that are required.
	maxLines int
}

// registerPlugin returns an accumulator function which will accept lines of
// a panic stack trace to collect into an error when requested.
func (p *panicRecorder) registerPlugin(name string) func(string) {
	p.Lock()
	defer p.Unlock()

	// In most cases we shouldn't be starting a plugin if it already
	// panicked, but clear out previous entries just in case.
	delete(p.panics, name)

	count := 0

	// this callback is used by the logger to store panic output
	return func(line string) {
		p.Lock()
		defer p.Unlock()

		// stop recording if there are too many lines.
		if count > p.maxLines {
			return
		}
		count++

		p.panics[name] = append(p.panics[name], line)
	}
}

func (p *panicRecorder) allPanics() []string {
	p.Lock()
	defer p.Unlock()

	var res []string
	for name, lines := range p.panics {
		if len(lines) == 0 {
			continue
		}

		res = append(res, fmt.Sprintf(pluginPanicOutput, name, strings.Join(lines, "\n")))
	}
	return res
}

// logPanicWrapper wraps an hclog.Logger and intercepts and records any output
// that appears to be a panic.
type logPanicWrapper struct {
	hclog.Logger
	panicRecorder func(string)
	inPanic       bool
}

// go-plugin will create a new named logger for each plugin binary.
func (l *logPanicWrapper) Named(name string) hclog.Logger {
	return &logPanicWrapper{
		Logger:        l.Logger.Named(name),
		panicRecorder: panics.registerPlugin(name),
	}
}

// we only need to implement Debug, since that is the default output level used
// by go-plugin when encountering unstructured output on stderr.
func (l *logPanicWrapper) Debug(msg string, args ...interface{}) {
	// We don't have access to the binary itself, so guess based on the stderr
	// output if this is the start of the traceback. An occasional false
	// positive shouldn't be a big deal, since this is only retrieved after an
	// error of some sort.

	panicPrefix := strings.HasPrefix(msg, "panic: ") || strings.HasPrefix(msg, "fatal error: ")

	l.inPanic = l.inPanic || panicPrefix

	if l.inPanic && l.panicRecorder != nil {
		l.panicRecorder(msg)
	}

	l.Logger.Debug(msg, args...)
}
<|endoftext|>