<commit_before>package configuration

import (
	"cf"
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
)

const (
	filePermissions      = 0644
	dirPermissions       = 0700
	currentConfigVersion = 2
)

var singleton *Configuration

type ConfigurationRepository interface {
	Get() (config *Configuration, err error)
	Delete()
	Save() (err error)
	ClearTokens() (err error)
	ClearSession() (err error)
	SetOrganization(org cf.OrganizationFields) (err error)
	SetSpace(space cf.SpaceFields) (err error)
}

type ConfigurationDiskRepository struct{}

func NewConfigurationDiskRepository() (repo ConfigurationDiskRepository) {
	return ConfigurationDiskRepository{}
}

func (repo ConfigurationDiskRepository) SetOrganization(org cf.OrganizationFields) (err error) {
	config, err := repo.Get()
	if err != nil {
		return
	}

	config.OrganizationFields = org
	config.SpaceFields = cf.SpaceFields{}

	return saveConfiguration(config)
}

func (repo ConfigurationDiskRepository) SetSpace(space cf.SpaceFields) (err error) {
	config, err := repo.Get()
	if err != nil {
		return
	}

	config.SpaceFields = space

	return saveConfiguration(config)
}

func (repo ConfigurationDiskRepository) Get() (c *Configuration, err error) {
	if singleton == nil {
		singleton, err = repo.load()

		if err != nil {
			return
		}
	}

	return singleton, nil
}

func (repo ConfigurationDiskRepository) Delete() {
	file, err := ConfigFile()

	if err != nil {
		return
	}

	os.Remove(file)
	singleton = nil
}

func (repo ConfigurationDiskRepository) Save() (err error) {
	c, err := repo.Get()
	if err != nil {
		return
	}
	return saveConfiguration(c)
}

func (repo ConfigurationDiskRepository) ClearTokens() (err error) {
	c, err := repo.Get()
	if err != nil {
		return
	}
	c.AccessToken = ""
	c.RefreshToken = ""
	return
}

func (repo ConfigurationDiskRepository) ClearSession() (err error) {
	err = repo.ClearTokens()
	if err != nil {
		return
	}

	c, err := repo.Get()
	if err != nil {
		return
	}
	c.OrganizationFields = cf.OrganizationFields{}
	c.SpaceFields = cf.SpaceFields{}

	return saveConfiguration(c)
}

// Keep this one public for configtest/configuration.go
func ConfigFile() (file string, err error) {

	configDir := filepath.Join(userHomeDir(), ".cf")

	err = os.MkdirAll(configDir, dirPermissions)

	if err != nil {
		return
	}

	file = filepath.Join(configDir, "config.json")
	return
}

// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func userHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}

	return os.Getenv("HOME")
}

func defaultConfig() (c *Configuration) {
	c = new(Configuration)
	c.Target = ""
	c.ApiVersion = ""
	c.AuthorizationEndpoint = ""
	c.ApplicationStartTimeout = 30 // seconds
	c.ConfigVersion = currentConfigVersion

	return
}

func (repo ConfigurationDiskRepository) load() (c *Configuration, parseError error) {
	file, readError := ConfigFile()
	c = new(Configuration)

	if readError != nil {
		c := defaultConfig()
		return c, saveConfiguration(c)
	}

	data, readError := ioutil.ReadFile(file)

	if readError != nil {
		c := defaultConfig()
		return c, saveConfiguration(c)
	}

	parseError = json.Unmarshal(data, c)
	if parseError != nil {
		return
	}

	if c.ConfigVersion < currentConfigVersion {
		c = defaultConfig()
		return c, nil
	}

	return
}

func saveConfiguration(config *Configuration) (err error) {
	bytes, err := json.Marshal(config)
	if err != nil {
		return
	}

	file, err := ConfigFile()

	if err != nil {
		return
	}
	err = ioutil.WriteFile(file, bytes, filePermissions)

	return
}
<commit_msg>config file should not be world readable<commit_after>package configuration

import (
	"cf"
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
)

const (
	filePermissions      = 0600
	dirPermissions       = 0700
	currentConfigVersion = 2
)

var singleton *Configuration

type ConfigurationRepository interface {
	Get() (config *Configuration, err error)
	Delete()
	Save() (err error)
	ClearTokens() (err error)
	ClearSession() (err error)
	SetOrganization(org cf.OrganizationFields) (err error)
	SetSpace(space cf.SpaceFields) (err error)
}

type ConfigurationDiskRepository struct{}

func NewConfigurationDiskRepository() (repo ConfigurationDiskRepository) {
	return ConfigurationDiskRepository{}
}

func (repo ConfigurationDiskRepository) SetOrganization(org cf.OrganizationFields) (err error) {
	config, err := repo.Get()
	if err != nil {
		return
	}

	config.OrganizationFields = org
	config.SpaceFields = cf.SpaceFields{}

	return saveConfiguration(config)
}

func (repo ConfigurationDiskRepository) SetSpace(space cf.SpaceFields) (err error) {
	config, err := repo.Get()
	if err != nil {
		return
	}

	config.SpaceFields = space

	return saveConfiguration(config)
}

func (repo ConfigurationDiskRepository) Get() (c *Configuration, err error) {
	if singleton == nil {
		singleton, err = repo.load()

		if err != nil {
			return
		}
	}

	return singleton, nil
}

func (repo ConfigurationDiskRepository) Delete() {
	file, err := ConfigFile()

	if err != nil {
		return
	}

	os.Remove(file)
	singleton = nil
}

func (repo ConfigurationDiskRepository) Save() (err error) {
	c, err := repo.Get()
	if err != nil {
		return
	}
	return saveConfiguration(c)
}

func (repo ConfigurationDiskRepository) ClearTokens() (err error) {
	c, err := repo.Get()
	if err != nil {
		return
	}
	c.AccessToken = ""
	c.RefreshToken = ""
	return
}

func (repo ConfigurationDiskRepository) ClearSession() (err error) {
	err = repo.ClearTokens()
	if err != nil {
		return
	}

	c, err := repo.Get()
	if err != nil {
		return
	}
	c.OrganizationFields = cf.OrganizationFields{}
	c.SpaceFields = cf.SpaceFields{}

	return saveConfiguration(c)
}

// Keep this one public for configtest/configuration.go
func ConfigFile() (file string, err error) {

	configDir := filepath.Join(userHomeDir(), ".cf")

	err = os.MkdirAll(configDir, dirPermissions)

	if err != nil {
		return
	}

	file = filepath.Join(configDir, "config.json")
	return
}

// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func userHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}

	return os.Getenv("HOME")
}

func defaultConfig() (c *Configuration) {
	c = new(Configuration)
	c.Target = ""
	c.ApiVersion = ""
	c.AuthorizationEndpoint = ""
	c.ApplicationStartTimeout = 30 // seconds
	c.ConfigVersion = currentConfigVersion

	return
}

func (repo ConfigurationDiskRepository) load() (c *Configuration, parseError error) {
	file, readError := ConfigFile()
	c = new(Configuration)

	if readError != nil {
		c := defaultConfig()
		return c, saveConfiguration(c)
	}

	data, readError := ioutil.ReadFile(file)

	if readError != nil {
		c := defaultConfig()
		return c, saveConfiguration(c)
	}

	parseError = json.Unmarshal(data, c)
	if parseError != nil {
		return
	}

	if c.ConfigVersion < currentConfigVersion {
		c = defaultConfig()
		return c, nil
	}

	return
}

func saveConfiguration(config *Configuration) (err error) {
	bytes, err := json.Marshal(config)
	if err != nil {
		return
	}

	file, err := ConfigFile()

	if err != nil {
		return
	}
	err = ioutil.WriteFile(file, bytes, filePermissions)

	return
}
<|endoftext|>


<commit_before>package cancel

import "context"

// Canceller is a simple wrapper for a cancellable context which makes the associated context.CancelFunc more easily
// accessible.
type Canceller struct {
	context.Context
	Cancel context.CancelFunc
}

// New returns a new canceller with the parent context.
func New(ctx context.Context) *Canceller {
	ctx, cancel := context.WithCancel(ctx)
	return &Canceller{
		Context: ctx,
		Cancel:  cancel,
	}
}
<commit_msg>shared/cancel: Use multiline import syntax.<commit_after>package cancel

import (
	"context"
)

// Canceller is a simple wrapper for a cancellable context which makes the associated context.CancelFunc more easily
// accessible.
type Canceller struct {
	context.Context
	Cancel context.CancelFunc
}

// New returns a new canceller with the parent context.
func New(ctx context.Context) *Canceller {
	ctx, cancel := context.WithCancel(ctx)
	return &Canceller{
		Context: ctx,
		Cancel:  cancel,
	}
}
<|endoftext|>


<commit_before>package levant

import (
	"fmt"
	"strings"
	"time"

	nomad "github.com/hashicorp/nomad/api"
	nomadStructs "github.com/hashicorp/nomad/nomad/structs"
	"github.com/jrasell/levant/logging"
)

type nomadClient struct {
	nomad *nomad.Client
}

// NomadClient is an interface to the Nomad API and deployment functions.
type NomadClient interface {
	// Deploy triggers a register of the job resulting in a Nomad deployment which
	// is monitored to determine the eventual state.
	Deploy(*nomad.Job, int, bool) bool
}

// NewNomadClient is used to create a new client to interact with Nomad.
func NewNomadClient(addr string) (NomadClient, error) {
	config := nomad.DefaultConfig()

	if addr != "" {
		config.Address = addr
	}

	c, err := nomad.NewClient(config)
	if err != nil {
		return nil, err
	}

	return &nomadClient{nomad: c}, nil
}

// Deploy triggers a register of the job resulting in a Nomad deployment which
// is monitored to determine the eventual state.
func (c *nomadClient) Deploy(job *nomad.Job, autoPromote int, forceCount bool) (success bool) {

	// Validate the job to check it is syntactically correct.
	if _, _, err := c.nomad.Jobs().Validate(job, nil); err != nil {
		logging.Error("levant/deploy: job validation failed: %v", err)
		return
	}

	// If job.Type isn't set we can't continue
	if job.Type == nil {
		logging.Error("levant/deploy: Nomad job `type` is not set, should be set to `service`")
		return
	}

	if !forceCount {
		logging.Debug("levant/deploy: running dynamic job count updater for job %s", *job.Name)
		if err := c.dynamicGroupCountUpdater(job); err != nil {
			return
		}
	}

	// Check that the job has at least 1 TaskGroup with count > 0 (GH-16)
	TGCount := 0
	for _, group := range job.TaskGroups {
		TGCount += *group.Count
	}
	if TGCount == 0 {
		logging.Error("levant/deploy: all TaskGroups have a count of 0, nothing to do")
		return
	}

	logging.Info("levant/deploy: triggering a deployment of job %s", *job.Name)

	eval, _, err := c.nomad.Jobs().Register(job, nil)
	if err != nil {
		logging.Error("levant/deploy: unable to register job %s with Nomad: %v", *job.Name, err)
		return
	}

	// GH-50: batch job types do not return an evaluation upon registration.
	if eval == nil && *job.Type == nomadStructs.JobTypeBatch {
		logging.Debug("levant/deploy: job type %s does not create evaluations", nomadStructs.JobTypeBatch)
		return true
	}

	// Trigger the evaluationInspector to identify any potential errors in the
	// Nomad evaluation run. As far as I can tell from testing; a single alloc
	// failure in an evaluation means no allocs will be placed so we exit here.
	err = c.evaluationInspector(&eval.EvalID)
	if err != nil {
		logging.Error("levant/deploy: %v", err)
		return
	}

	switch *job.Type {
	case nomadStructs.JobTypeService:
		logging.Info("levant/deploy: beginning deployment watcher for job %s", *job.Name)

		// Get the deploymentID from the evaluationID so that we can watch the
		// deployment for end status.
		depID, err := c.getDeploymentID(eval.EvalID)
		if err != nil {
			logging.Error("levant/deploy: unable to get info of evaluation %s: %v", eval.EvalID, err)
			return
		}

		// Get the success of the deployment.
		success = c.deploymentWatcher(depID, autoPromote)

		// If the deployment has not been successful; check whether the job is
		// configured to auto-revert so that this can be tracked.
		if !success {
			dep, _, err := c.nomad.Deployments().Info(depID, nil)
			if err != nil {
				logging.Error("levant/deploy: unable to query deployment %s for auto-revert check: %v",
					dep.ID, err)
				break
			}
			c.checkAutoRevert(dep)
		}

	default:
		logging.Debug("levant/deploy: job type %s does not support Nomad deployment model", *job.Type)
		success = true
	}

	return
}

func (c *nomadClient) evaluationInspector(evalID *string) error {

	for {
		evalInfo, _, err := c.nomad.Evaluations().Info(*evalID, nil)
		if err != nil {
			return err
		}

		switch evalInfo.Status {
		case nomadStructs.EvalStatusComplete, nomadStructs.EvalStatusFailed, nomadStructs.EvalStatusCancelled:
			if len(evalInfo.FailedTGAllocs) == 0 {
				logging.Info("levant/deploy: evaluation %s finished successfully", *evalID)
				return nil
			}

			var class, dimension []string

			for group, metrics := range evalInfo.FailedTGAllocs {

				// Iterate the classes and dimensions to generate lists of each failure.
				for c := range metrics.ClassExhausted {
					class = append(class, c)
				}
				for d := range metrics.DimensionExhausted {
					dimension = append(dimension, d)
				}

				logging.Error("levant/deploy: task group %s failed to place %v allocs, failed on %v and exhausted %v",
					group, metrics.CoalescedFailures+1, class, dimension)
			}

			return fmt.Errorf("evaluation %v finished with status %s but failed to place allocations",
				*evalID, evalInfo.Status)

		default:
			time.Sleep(1 * time.Second)
			continue
		}
	}
}

func (c *nomadClient) deploymentWatcher(depID string, autoPromote int) (success bool) {

	var canaryChan chan interface{}
	deploymentChan := make(chan interface{})

	t := time.Now()
	wt := time.Duration(5 * time.Second)

	// Setup the canaryChan and launch the autoPromote go routine if autoPromote
	// has been enabled.
	if autoPromote > 0 {
		canaryChan = make(chan interface{})
		go c.canaryAutoPromote(depID, autoPromote, canaryChan, deploymentChan)
	}

	q := &nomad.QueryOptions{WaitIndex: 1, AllowStale: true, WaitTime: wt}

	for {

		dep, meta, err := c.nomad.Deployments().Info(depID, q)
		logging.Debug("levant/deploy: deployment %v running for %.2fs", depID, time.Since(t).Seconds())

		// Listen for the deploymentChan closing which indicates Levant should exit
		// the deployment watcher.
		select {
		case <-deploymentChan:
			return false
		default:
			break
		}

		if err != nil {
			logging.Error("levant/deploy: unable to get info of deployment %s: %v", depID, err)
			return
		}

		if meta.LastIndex <= q.WaitIndex {
			continue
		}

		q.WaitIndex = meta.LastIndex

		cont, err := c.checkDeploymentStatus(dep, canaryChan)
		if err != nil {
			return false
		}

		if cont {
			continue
		} else {
			return true
		}
	}
}

func (c *nomadClient) checkDeploymentStatus(dep *nomad.Deployment, shutdownChan chan interface{}) (bool, error) {

	switch dep.Status {
	case nomadStructs.DeploymentStatusSuccessful:
		logging.Info("levant/deploy: deployment %v has completed successfully", dep.ID)
		return false, nil
	case nomadStructs.DeploymentStatusRunning:
		return true, nil
	default:
		if shutdownChan != nil {
			logging.Debug("levant/deploy: deployment %v meaning canary auto promote will shutdown", dep.Status)
			close(shutdownChan)
		}

		logging.Error("levant/deploy: deployment %v has status %s", dep.ID, dep.Status)

		// Launch the failure inspector.
		c.checkFailedDeployment(&dep.ID)

		return false, fmt.Errorf("deployment failed")
	}
}

// canaryAutoPromote handles Levant's canary-auto-promote functionality.
func (c *nomadClient) canaryAutoPromote(depID string, waitTime int, shutdownChan, deploymentChan chan interface{}) {

	// Setup the AutoPromote timer.
	autoPromote := time.After(time.Duration(waitTime) * time.Second)

	for {
		select {
		case <-autoPromote:
			logging.Info("levant/deploy: auto-promote period %vs has been reached for deployment %s",
				waitTime, depID)

			// Check the deployment is healthy before promoting.
			if healthy := c.checkCanaryDeploymentHealth(depID); !healthy {
				logging.Error("levant/deploy: the canary deployment %s has unhealthy allocations, unable to promote", depID)
				close(deploymentChan)
				return
			}

			logging.Info("levant/deploy: triggering auto promote of deployment %s", depID)

			// Promote the deployment.
			_, _, err := c.nomad.Deployments().PromoteAll(depID, nil)
			if err != nil {
				logging.Error("levant/deploy: unable to promote deployment %s: %v", depID, err)
				close(deploymentChan)
				return
			}

		case <-shutdownChan:
			logging.Info("levant/deploy: canary auto promote has been shutdown")
			return
		}
	}
}

// checkCanaryDeploymentHealth is used to check the health status of each
// task-group within a canary deployment.
func (c *nomadClient) checkCanaryDeploymentHealth(depID string) (healthy bool) {

	var unhealthy int

	dep, _, err := c.nomad.Deployments().Info(depID, &nomad.QueryOptions{AllowStale: true})
	if err != nil {
		logging.Error("levant/deploy: unable to query deployment %s for health: %v", depID, err)
		return
	}

	// Itertate each task in the deployment to determine is health status. If an
	// unhealthy task is found, incrament the unhealthy counter.
	for taskName, taskInfo := range dep.TaskGroups {
		if taskInfo.DesiredCanaries != taskInfo.HealthyAllocs {
			logging.Error("levant/deploy: task %s has unhealthy allocations in deployment %s", taskName, depID)
			unhealthy++
		}
	}

	// If zero unhealthy tasks were found, continue with the auto promotion.
	if unhealthy == 0 {
		logging.Debug("levant/deploy: deployment %s has 0 unhealthy allocations", depID)
		healthy = true
	}

	return
}

// getDeploymentID finds the Nomad deploymentID associated to a Nomad
// evaluationID. This is only needed as sometimes Nomad initially returns eval
// info with an empty deploymentID; and a retry is required in order to get the
// updated response from Nomad.
func (c *nomadClient) getDeploymentID(evalID string) (depID string, err error) {

	var evalInfo *nomad.Evaluation

	for {
		if evalInfo, _, err = c.nomad.Evaluations().Info(evalID, nil); err != nil {
			return
		}

		if evalInfo.DeploymentID == "" {
			logging.Debug("levant/deploy: Nomad returned an empty deployment for evaluation %v; retrying", evalID)
			time.Sleep(2 * time.Second)
			continue
		} else {
			break
		}
	}

	return evalInfo.DeploymentID, nil
}

// dynamicGroupCountUpdater takes the templated and rendered job and updates the
// group counts based on the currently deployed job; if its running.
func (c *nomadClient) dynamicGroupCountUpdater(job *nomad.Job) error {

	// Gather information about the current state, if any, of the job on the
	// Nomad cluster.
	rJob, _, err := c.nomad.Jobs().Info(*job.Name, &nomad.QueryOptions{})

	// This is a hack due to GH-1849; we check the error string for 404 which
	// indicates the job is not running, not that there was an error in the API
	// call.
	if err != nil && strings.Contains(err.Error(), "404") {
		logging.Info("levant/deploy: job %s not running, using template file group counts", *job.Name)
		return nil
	} else if err != nil {
		logging.Error("levant/deploy: unable to perform job evaluation: %v", err)
		return err
	}

	// Iterate the templated job and the Nomad returned job and update group count
	// based on matches.
	for _, rGroup := range rJob.TaskGroups {
		for _, group := range job.TaskGroups {
			if *rGroup.Name == *group.Name {
				logging.Info("levant/deploy: using dynamic count %v for job %s and group %s",
					*rGroup.Count, *job.Name, *group.Name)
				group.Count = rGroup.Count
			}
		}
	}
	return nil
}
<commit_msg>Fix issue where system jobs cause panic.<commit_after>package levant

import (
	"fmt"
	"strings"
	"time"

	nomad "github.com/hashicorp/nomad/api"
	nomadStructs "github.com/hashicorp/nomad/nomad/structs"
	"github.com/jrasell/levant/logging"
)

type nomadClient struct {
	nomad *nomad.Client
}

// NomadClient is an interface to the Nomad API and deployment functions.
type NomadClient interface {
	// Deploy triggers a register of the job resulting in a Nomad deployment which
	// is monitored to determine the eventual state.
	Deploy(*nomad.Job, int, bool) bool
}

// NewNomadClient is used to create a new client to interact with Nomad.
func NewNomadClient(addr string) (NomadClient, error) {
	config := nomad.DefaultConfig()

	if addr != "" {
		config.Address = addr
	}

	c, err := nomad.NewClient(config)
	if err != nil {
		return nil, err
	}

	return &nomadClient{nomad: c}, nil
}

// Deploy triggers a register of the job resulting in a Nomad deployment which
// is monitored to determine the eventual state.
func (c *nomadClient) Deploy(job *nomad.Job, autoPromote int, forceCount bool) (success bool) {

	// Validate the job to check it is syntactically correct.
	if _, _, err := c.nomad.Jobs().Validate(job, nil); err != nil {
		logging.Error("levant/deploy: job validation failed: %v", err)
		return
	}

	// If job.Type isn't set we can't continue
	if job.Type == nil {
		logging.Error("levant/deploy: Nomad job `type` is not set, should be set to `service`")
		return
	}

	if !forceCount {
		logging.Debug("levant/deploy: running dynamic job count updater for job %s", *job.Name)
		if err := c.dynamicGroupCountUpdater(job); err != nil {
			return
		}
	}

	// Check that the job has at least 1 TaskGroup with count > 0 (GH-16) if the
	// job is not a System job. Systems jobs do not define counts so cannot be
	// checked.
	if *job.Type != nomadStructs.JobTypeSystem {
		tgCount := 0
		for _, group := range job.TaskGroups {
			tgCount += *group.Count
		}
		if tgCount == 0 {
			logging.Error("levant/deploy: all TaskGroups have a count of 0, nothing to do")
			return
		}
	}

	logging.Info("levant/deploy: triggering a deployment of job %s", *job.Name)

	eval, _, err := c.nomad.Jobs().Register(job, nil)
	if err != nil {
		logging.Error("levant/deploy: unable to register job %s with Nomad: %v", *job.Name, err)
		return
	}

	// GH-50: batch job types do not return an evaluation upon registration.
	if eval == nil && *job.Type == nomadStructs.JobTypeBatch {
		logging.Debug("levant/deploy: job type %s does not create evaluations", nomadStructs.JobTypeBatch)
		return true
	}

	// Trigger the evaluationInspector to identify any potential errors in the
	// Nomad evaluation run. As far as I can tell from testing; a single alloc
	// failure in an evaluation means no allocs will be placed so we exit here.
	err = c.evaluationInspector(&eval.EvalID)
	if err != nil {
		logging.Error("levant/deploy: %v", err)
		return
	}

	switch *job.Type {
	case nomadStructs.JobTypeService:
		logging.Info("levant/deploy: beginning deployment watcher for job %s", *job.Name)

		// Get the deploymentID from the evaluationID so that we can watch the
		// deployment for end status.
		depID, err := c.getDeploymentID(eval.EvalID)
		if err != nil {
			logging.Error("levant/deploy: unable to get info of evaluation %s: %v", eval.EvalID, err)
			return
		}

		// Get the success of the deployment.
		success = c.deploymentWatcher(depID, autoPromote)

		// If the deployment has not been successful; check whether the job is
		// configured to auto-revert so that this can be tracked.
		if !success {
			dep, _, err := c.nomad.Deployments().Info(depID, nil)
			if err != nil {
				logging.Error("levant/deploy: unable to query deployment %s for auto-revert check: %v",
					dep.ID, err)
				break
			}
			c.checkAutoRevert(dep)
		}

	default:
		logging.Debug("levant/deploy: job type %s does not support Nomad deployment model", *job.Type)
		success = true
	}

	return
}

func (c *nomadClient) evaluationInspector(evalID *string) error {

	for {
		evalInfo, _, err := c.nomad.Evaluations().Info(*evalID, nil)
		if err != nil {
			return err
		}

		switch evalInfo.Status {
		case nomadStructs.EvalStatusComplete, nomadStructs.EvalStatusFailed, nomadStructs.EvalStatusCancelled:
			if len(evalInfo.FailedTGAllocs) == 0 {
				logging.Info("levant/deploy: evaluation %s finished successfully", *evalID)
				return nil
			}

			var class, dimension []string

			for group, metrics := range evalInfo.FailedTGAllocs {

				// Iterate the classes and dimensions to generate lists of each failure.
				for c := range metrics.ClassExhausted {
					class = append(class, c)
				}
				for d := range metrics.DimensionExhausted {
					dimension = append(dimension, d)
				}

				logging.Error("levant/deploy: task group %s failed to place %v allocs, failed on %v and exhausted %v",
					group, metrics.CoalescedFailures+1, class, dimension)
			}

			return fmt.Errorf("evaluation %v finished with status %s but failed to place allocations",
				*evalID, evalInfo.Status)

		default:
			time.Sleep(1 * time.Second)
			continue
		}
	}
}

func (c *nomadClient) deploymentWatcher(depID string, autoPromote int) (success bool) {

	var canaryChan chan interface{}
	deploymentChan := make(chan interface{})

	t := time.Now()
	wt := time.Duration(5 * time.Second)

	// Setup the canaryChan and launch the autoPromote go routine if autoPromote
	// has been enabled.
	if autoPromote > 0 {
		canaryChan = make(chan interface{})
		go c.canaryAutoPromote(depID, autoPromote, canaryChan, deploymentChan)
	}

	q := &nomad.QueryOptions{WaitIndex: 1, AllowStale: true, WaitTime: wt}

	for {

		dep, meta, err := c.nomad.Deployments().Info(depID, q)
		logging.Debug("levant/deploy: deployment %v running for %.2fs", depID, time.Since(t).Seconds())

		// Listen for the deploymentChan closing which indicates Levant should exit
		// the deployment watcher.
		select {
		case <-deploymentChan:
			return false
		default:
			break
		}

		if err != nil {
			logging.Error("levant/deploy: unable to get info of deployment %s: %v", depID, err)
			return
		}

		if meta.LastIndex <= q.WaitIndex {
			continue
		}

		q.WaitIndex = meta.LastIndex

		cont, err := c.checkDeploymentStatus(dep, canaryChan)
		if err != nil {
			return false
		}

		if cont {
			continue
		} else {
			return true
		}
	}
}

func (c *nomadClient) checkDeploymentStatus(dep *nomad.Deployment, shutdownChan chan interface{}) (bool, error) {

	switch dep.Status {
	case nomadStructs.DeploymentStatusSuccessful:
		logging.Info("levant/deploy: deployment %v has completed successfully", dep.ID)
		return false, nil
	case nomadStructs.DeploymentStatusRunning:
		return true, nil
	default:
		if shutdownChan != nil {
			logging.Debug("levant/deploy: deployment %v meaning canary auto promote will shutdown", dep.Status)
			close(shutdownChan)
		}

		logging.Error("levant/deploy: deployment %v has status %s", dep.ID, dep.Status)

		// Launch the failure inspector.
		c.checkFailedDeployment(&dep.ID)

		return false, fmt.Errorf("deployment failed")
	}
}

// canaryAutoPromote handles Levant's canary-auto-promote functionality.
func (c *nomadClient) canaryAutoPromote(depID string, waitTime int, shutdownChan, deploymentChan chan interface{}) {

	// Setup the AutoPromote timer.
	autoPromote := time.After(time.Duration(waitTime) * time.Second)

	for {
		select {
		case <-autoPromote:
			logging.Info("levant/deploy: auto-promote period %vs has been reached for deployment %s",
				waitTime, depID)

			// Check the deployment is healthy before promoting.
			if healthy := c.checkCanaryDeploymentHealth(depID); !healthy {
				logging.Error("levant/deploy: the canary deployment %s has unhealthy allocations, unable to promote", depID)
				close(deploymentChan)
				return
			}

			logging.Info("levant/deploy: triggering auto promote of deployment %s", depID)

			// Promote the deployment.
			_, _, err := c.nomad.Deployments().PromoteAll(depID, nil)
			if err != nil {
				logging.Error("levant/deploy: unable to promote deployment %s: %v", depID, err)
				close(deploymentChan)
				return
			}

		case <-shutdownChan:
			logging.Info("levant/deploy: canary auto promote has been shutdown")
			return
		}
	}
}

// checkCanaryDeploymentHealth is used to check the health status of each
// task-group within a canary deployment.
func (c *nomadClient) checkCanaryDeploymentHealth(depID string) (healthy bool) {

	var unhealthy int

	dep, _, err := c.nomad.Deployments().Info(depID, &nomad.QueryOptions{AllowStale: true})
	if err != nil {
		logging.Error("levant/deploy: unable to query deployment %s for health: %v", depID, err)
		return
	}

	// Itertate each task in the deployment to determine is health status. If an
	// unhealthy task is found, incrament the unhealthy counter.
	for taskName, taskInfo := range dep.TaskGroups {
		if taskInfo.DesiredCanaries != taskInfo.HealthyAllocs {
			logging.Error("levant/deploy: task %s has unhealthy allocations in deployment %s", taskName, depID)
			unhealthy++
		}
	}

	// If zero unhealthy tasks were found, continue with the auto promotion.
	if unhealthy == 0 {
		logging.Debug("levant/deploy: deployment %s has 0 unhealthy allocations", depID)
		healthy = true
	}

	return
}

// getDeploymentID finds the Nomad deploymentID associated to a Nomad
// evaluationID. This is only needed as sometimes Nomad initially returns eval
// info with an empty deploymentID; and a retry is required in order to get the
// updated response from Nomad.
func (c *nomadClient) getDeploymentID(evalID string) (depID string, err error) {

	var evalInfo *nomad.Evaluation

	for {
		if evalInfo, _, err = c.nomad.Evaluations().Info(evalID, nil); err != nil {
			return
		}

		if evalInfo.DeploymentID == "" {
			logging.Debug("levant/deploy: Nomad returned an empty deployment for evaluation %v; retrying", evalID)
			time.Sleep(2 * time.Second)
			continue
		} else {
			break
		}
	}

	return evalInfo.DeploymentID, nil
}

// dynamicGroupCountUpdater takes the templated and rendered job and updates the
// group counts based on the currently deployed job; if its running.
func (c *nomadClient) dynamicGroupCountUpdater(job *nomad.Job) error {

	// Gather information about the current state, if any, of the job on the
	// Nomad cluster.
	rJob, _, err := c.nomad.Jobs().Info(*job.Name, &nomad.QueryOptions{})

	// This is a hack due to GH-1849; we check the error string for 404 which
	// indicates the job is not running, not that there was an error in the API
	// call.
	if err != nil && strings.Contains(err.Error(), "404") {
		logging.Info("levant/deploy: job %s not running, using template file group counts", *job.Name)
		return nil
	} else if err != nil {
		logging.Error("levant/deploy: unable to perform job evaluation: %v", err)
		return err
	}

	// Iterate the templated job and the Nomad returned job and update group count
	// based on matches.
	for _, rGroup := range rJob.TaskGroups {
		for _, group := range job.TaskGroups {
			if *rGroup.Name == *group.Name {
				logging.Info("levant/deploy: using dynamic count %v for job %s and group %s",
					*rGroup.Count, *job.Name, *group.Name)
				group.Count = rGroup.Count
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go 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 logopt

import (
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

const srcCode = `package x
type pair struct {a,b int}
func bar(y *pair) *int {
	return &y.b
}
var a []int
func foo(w, z *pair) *int {
	if *bar(w) > 0 {
		return bar(z)
	}
	if a[1] > 0 {
		a = a[:2]
	}
	return &a[0]
}

// address taking prevents closure inlining
func n() int {
	foo := func() int { return 1 }
	bar := &foo
	x := (*bar)() + foo()
	return x
}
`

func want(t *testing.T, out string, desired string) {
	// On Windows, Unicode escapes in the JSON output end up "normalized" elsewhere to /u....,
	// so "normalize" what we're looking for to match that.
	s := strings.ReplaceAll(desired, string(os.PathSeparator), "/")
	if !strings.Contains(out, s) {
		t.Errorf("did not see phrase %s in \n%s", s, out)
	}
}

func wantN(t *testing.T, out string, desired string, n int) {
	if strings.Count(out, desired) != n {
		t.Errorf("expected exactly %d occurences of %s in \n%s", n, desired, out)
	}
}

func TestPathStuff(t *testing.T) {
	sep := string(filepath.Separator)
	if path, whine := parseLogPath("file:///c:foo"); path != "c:foo" || whine != "" { // good path
		t.Errorf("path='%s', whine='%s'", path, whine)
	}
	if path, whine := parseLogPath("file:///foo"); path != sep+"foo" || whine != "" { // good path
		t.Errorf("path='%s', whine='%s'", path, whine)
	}
	if path, whine := parseLogPath("foo"); path != "" || whine == "" { // BAD path
		t.Errorf("path='%s', whine='%s'", path, whine)
	}
	if sep == "\\" { // On WINDOWS ONLY
		if path, whine := parseLogPath("C:/foo"); path != "C:\\foo" || whine != "" { // good path
			t.Errorf("path='%s', whine='%s'", path, whine)
		}
		if path, whine := parseLogPath("c:foo"); path != "" || whine == "" { // BAD path
			t.Errorf("path='%s', whine='%s'", path, whine)
		}
		if path, whine := parseLogPath("/foo"); path != "" || whine == "" { // BAD path
			t.Errorf("path='%s', whine='%s'", path, whine)
		}
	} else { // ON UNIX ONLY
		if path, whine := parseLogPath("/foo"); path != sep+"foo" || whine != "" { // good path
			t.Errorf("path='%s', whine='%s'", path, whine)
		}
	}
}

func TestLogOpt(t *testing.T) {
	t.Parallel()

	testenv.MustHaveGoBuild(t)

	dir, err := ioutil.TempDir("", "TestLogOpt")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	dir = fixSlash(dir) // Normalize the directory name as much as possible, for Windows testing
	src := filepath.Join(dir, "file.go")
	if err := ioutil.WriteFile(src, []byte(srcCode), 0644); err != nil {
		t.Fatal(err)
	}

	outfile := filepath.Join(dir, "file.o")

	t.Run("JSON_fails", func(t *testing.T) {
		// Test malformed flag
		out, err := testLogOpt(t, "-json=foo", src, outfile)
		if err == nil {
			t.Error("-json=foo succeeded unexpectedly")
		}
		want(t, out, "option should be")
		want(t, out, "number")

		// Test a version number that is currently unsupported (and should remain unsupported for a while)
		out, err = testLogOpt(t, "-json=9,foo", src, outfile)
		if err == nil {
			t.Error("-json=0,foo succeeded unexpectedly")
		}
		want(t, out, "version must be")

	})

	// replace d (dir)  with t ("tmpdir") and convert path separators to '/'
	normalize := func(out []byte, d, t string) string {
		s := string(out)
		s = strings.ReplaceAll(s, d, t)
		s = strings.ReplaceAll(s, string(os.PathSeparator), "/")
		return s
	}

	// Ensure that <128 byte copies are not reported and that 128-byte copies are.
	// Check at both 1 and 8-byte alignments.
	t.Run("Copy", func(t *testing.T) {
		const copyCode = `package x
func s128a1(x *[128]int8) [128]int8 {
	return *x
}
func s127a1(x *[127]int8) [127]int8 {
	return *x
}
func s16a8(x *[16]int64) [16]int64 {
	return *x
}
func s15a8(x *[15]int64) [15]int64 {
	return *x
}
`
		copy := filepath.Join(dir, "copy.go")
		if err := ioutil.WriteFile(copy, []byte(copyCode), 0644); err != nil {
			t.Fatal(err)
		}
		outcopy := filepath.Join(dir, "copy.o")

		// On not-amd64, test the host architecture and os
		arches := []string{runtime.GOARCH}
		goos0 := runtime.GOOS
		if runtime.GOARCH == "amd64" { // Test many things with "linux" (wasm will get "js")
			arches = []string{"arm", "arm64", "386", "amd64", "mips", "mips64", "ppc64le", "riscv64", "s390x", "wasm"}
			goos0 = "linux"
		}

		for _, arch := range arches {
			t.Run(arch, func(t *testing.T) {
				goos := goos0
				if arch == "wasm" {
					goos = "js"
				}
				_, err := testCopy(t, dir, arch, goos, copy, outcopy)
				if err != nil {
					t.Error("-json=0,file://log/opt should have succeeded")
				}
				logged, err := ioutil.ReadFile(filepath.Join(dir, "log", "opt", "x", "copy.json"))
				if err != nil {
					t.Error("-json=0,file://log/opt missing expected log file")
				}
				slogged := normalize(logged, string(uriIfy(dir)), string(uriIfy("tmpdir")))
				t.Logf("%s", slogged)
				want(t, slogged, `{"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":2}},"severity":3,"code":"copy","source":"go compiler","message":"128 bytes"}`)
				want(t, slogged, `{"range":{"start":{"line":9,"character":2},"end":{"line":9,"character":2}},"severity":3,"code":"copy","source":"go compiler","message":"128 bytes"}`)
				wantN(t, slogged, `"code":"copy"`, 2)
			})
		}
	})

	// Some architectures don't fault on nil dereference, so nilchecks are eliminated differently.
	// The N-way copy test also doesn't need to run N-ways N times.
	if runtime.GOARCH != "amd64" {
		return
	}

	t.Run("Success", func(t *testing.T) {
		// This test is supposed to succeed

		// Note 'file://' is the I-Know-What-I-Am-Doing way of specifying a file, also to deal with corner cases for Windows.
		_, err := testLogOptDir(t, dir, "-json=0,file://log/opt", src, outfile)
		if err != nil {
			t.Error("-json=0,file://log/opt should have succeeded")
		}
		logged, err := ioutil.ReadFile(filepath.Join(dir, "log", "opt", "x", "file.json"))
		if err != nil {
			t.Error("-json=0,file://log/opt missing expected log file")
		}
		// All this delicacy with uriIfy and filepath.Join is to get this test to work right on Windows.
		slogged := normalize(logged, string(uriIfy(dir)), string(uriIfy("tmpdir")))
		t.Logf("%s", slogged)
		// below shows proper nilcheck
		want(t, slogged, `{"range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}},"severity":3,"code":"nilcheck","source":"go compiler","message":"",`+
			`"relatedInformation":[{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"}]}`)
		want(t, slogged, `{"range":{"start":{"line":11,"character":6},"end":{"line":11,"character":6}},"severity":3,"code":"isInBounds","source":"go compiler","message":""}`)
		want(t, slogged, `{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":6}},"severity":3,"code":"canInlineFunction","source":"go compiler","message":"cost: 35"}`)
		// escape analysis explanation
		want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r2 with derefs=0",`+
			`"relatedInformation":[`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:    flow: y = z:"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:      from y := z (assign-pair)"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:    flow: ~R0 = y:"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:      from y.b (dot of pointer)"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:      from \u0026y.b (address-of)"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:      from ~R0 = \u003cN\u003e (assign-pair)"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow:    flow: ~r2 = ~R0:"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow:      from return (*int)(~R0) (return)"}]}`)
	})
}

func testLogOpt(t *testing.T, flag, src, outfile string) (string, error) {
	run := []string{testenv.GoToolPath(t), "tool", "compile", flag, "-o", outfile, src}
	t.Log(run)
	cmd := exec.Command(run[0], run[1:]...)
	out, err := cmd.CombinedOutput()
	t.Logf("%s", out)
	return string(out), err
}

func testLogOptDir(t *testing.T, dir, flag, src, outfile string) (string, error) {
	// Notice the specified import path "x"
	run := []string{testenv.GoToolPath(t), "tool", "compile", "-p", "x", flag, "-o", outfile, src}
	t.Log(run)
	cmd := exec.Command(run[0], run[1:]...)
	cmd.Dir = dir
	out, err := cmd.CombinedOutput()
	t.Logf("%s", out)
	return string(out), err
}

func testCopy(t *testing.T, dir, goarch, goos, src, outfile string) (string, error) {
	// Notice the specified import path "x"
	run := []string{testenv.GoToolPath(t), "tool", "compile", "-p", "x", "-json=0,file://log/opt", "-o", outfile, src}
	t.Log(run)
	cmd := exec.Command(run[0], run[1:]...)
	cmd.Dir = dir
	cmd.Env = append(os.Environ(), "GOARCH="+goarch, "GOOS="+goos)
	out, err := cmd.CombinedOutput()
	t.Logf("%s", out)
	return string(out), err
}
<commit_msg>cmd/compile: fix message typo<commit_after>// Copyright 2019 The Go 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 logopt

import (
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

const srcCode = `package x
type pair struct {a,b int}
func bar(y *pair) *int {
	return &y.b
}
var a []int
func foo(w, z *pair) *int {
	if *bar(w) > 0 {
		return bar(z)
	}
	if a[1] > 0 {
		a = a[:2]
	}
	return &a[0]
}

// address taking prevents closure inlining
func n() int {
	foo := func() int { return 1 }
	bar := &foo
	x := (*bar)() + foo()
	return x
}
`

func want(t *testing.T, out string, desired string) {
	// On Windows, Unicode escapes in the JSON output end up "normalized" elsewhere to /u....,
	// so "normalize" what we're looking for to match that.
	s := strings.ReplaceAll(desired, string(os.PathSeparator), "/")
	if !strings.Contains(out, s) {
		t.Errorf("did not see phrase %s in \n%s", s, out)
	}
}

func wantN(t *testing.T, out string, desired string, n int) {
	if strings.Count(out, desired) != n {
		t.Errorf("expected exactly %d occurrences of %s in \n%s", n, desired, out)
	}
}

func TestPathStuff(t *testing.T) {
	sep := string(filepath.Separator)
	if path, whine := parseLogPath("file:///c:foo"); path != "c:foo" || whine != "" { // good path
		t.Errorf("path='%s', whine='%s'", path, whine)
	}
	if path, whine := parseLogPath("file:///foo"); path != sep+"foo" || whine != "" { // good path
		t.Errorf("path='%s', whine='%s'", path, whine)
	}
	if path, whine := parseLogPath("foo"); path != "" || whine == "" { // BAD path
		t.Errorf("path='%s', whine='%s'", path, whine)
	}
	if sep == "\\" { // On WINDOWS ONLY
		if path, whine := parseLogPath("C:/foo"); path != "C:\\foo" || whine != "" { // good path
			t.Errorf("path='%s', whine='%s'", path, whine)
		}
		if path, whine := parseLogPath("c:foo"); path != "" || whine == "" { // BAD path
			t.Errorf("path='%s', whine='%s'", path, whine)
		}
		if path, whine := parseLogPath("/foo"); path != "" || whine == "" { // BAD path
			t.Errorf("path='%s', whine='%s'", path, whine)
		}
	} else { // ON UNIX ONLY
		if path, whine := parseLogPath("/foo"); path != sep+"foo" || whine != "" { // good path
			t.Errorf("path='%s', whine='%s'", path, whine)
		}
	}
}

func TestLogOpt(t *testing.T) {
	t.Parallel()

	testenv.MustHaveGoBuild(t)

	dir, err := ioutil.TempDir("", "TestLogOpt")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	dir = fixSlash(dir) // Normalize the directory name as much as possible, for Windows testing
	src := filepath.Join(dir, "file.go")
	if err := ioutil.WriteFile(src, []byte(srcCode), 0644); err != nil {
		t.Fatal(err)
	}

	outfile := filepath.Join(dir, "file.o")

	t.Run("JSON_fails", func(t *testing.T) {
		// Test malformed flag
		out, err := testLogOpt(t, "-json=foo", src, outfile)
		if err == nil {
			t.Error("-json=foo succeeded unexpectedly")
		}
		want(t, out, "option should be")
		want(t, out, "number")

		// Test a version number that is currently unsupported (and should remain unsupported for a while)
		out, err = testLogOpt(t, "-json=9,foo", src, outfile)
		if err == nil {
			t.Error("-json=0,foo succeeded unexpectedly")
		}
		want(t, out, "version must be")

	})

	// replace d (dir)  with t ("tmpdir") and convert path separators to '/'
	normalize := func(out []byte, d, t string) string {
		s := string(out)
		s = strings.ReplaceAll(s, d, t)
		s = strings.ReplaceAll(s, string(os.PathSeparator), "/")
		return s
	}

	// Ensure that <128 byte copies are not reported and that 128-byte copies are.
	// Check at both 1 and 8-byte alignments.
	t.Run("Copy", func(t *testing.T) {
		const copyCode = `package x
func s128a1(x *[128]int8) [128]int8 {
	return *x
}
func s127a1(x *[127]int8) [127]int8 {
	return *x
}
func s16a8(x *[16]int64) [16]int64 {
	return *x
}
func s15a8(x *[15]int64) [15]int64 {
	return *x
}
`
		copy := filepath.Join(dir, "copy.go")
		if err := ioutil.WriteFile(copy, []byte(copyCode), 0644); err != nil {
			t.Fatal(err)
		}
		outcopy := filepath.Join(dir, "copy.o")

		// On not-amd64, test the host architecture and os
		arches := []string{runtime.GOARCH}
		goos0 := runtime.GOOS
		if runtime.GOARCH == "amd64" { // Test many things with "linux" (wasm will get "js")
			arches = []string{"arm", "arm64", "386", "amd64", "mips", "mips64", "ppc64le", "riscv64", "s390x", "wasm"}
			goos0 = "linux"
		}

		for _, arch := range arches {
			t.Run(arch, func(t *testing.T) {
				goos := goos0
				if arch == "wasm" {
					goos = "js"
				}
				_, err := testCopy(t, dir, arch, goos, copy, outcopy)
				if err != nil {
					t.Error("-json=0,file://log/opt should have succeeded")
				}
				logged, err := ioutil.ReadFile(filepath.Join(dir, "log", "opt", "x", "copy.json"))
				if err != nil {
					t.Error("-json=0,file://log/opt missing expected log file")
				}
				slogged := normalize(logged, string(uriIfy(dir)), string(uriIfy("tmpdir")))
				t.Logf("%s", slogged)
				want(t, slogged, `{"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":2}},"severity":3,"code":"copy","source":"go compiler","message":"128 bytes"}`)
				want(t, slogged, `{"range":{"start":{"line":9,"character":2},"end":{"line":9,"character":2}},"severity":3,"code":"copy","source":"go compiler","message":"128 bytes"}`)
				wantN(t, slogged, `"code":"copy"`, 2)
			})
		}
	})

	// Some architectures don't fault on nil dereference, so nilchecks are eliminated differently.
	// The N-way copy test also doesn't need to run N-ways N times.
	if runtime.GOARCH != "amd64" {
		return
	}

	t.Run("Success", func(t *testing.T) {
		// This test is supposed to succeed

		// Note 'file://' is the I-Know-What-I-Am-Doing way of specifying a file, also to deal with corner cases for Windows.
		_, err := testLogOptDir(t, dir, "-json=0,file://log/opt", src, outfile)
		if err != nil {
			t.Error("-json=0,file://log/opt should have succeeded")
		}
		logged, err := ioutil.ReadFile(filepath.Join(dir, "log", "opt", "x", "file.json"))
		if err != nil {
			t.Error("-json=0,file://log/opt missing expected log file")
		}
		// All this delicacy with uriIfy and filepath.Join is to get this test to work right on Windows.
		slogged := normalize(logged, string(uriIfy(dir)), string(uriIfy("tmpdir")))
		t.Logf("%s", slogged)
		// below shows proper nilcheck
		want(t, slogged, `{"range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}},"severity":3,"code":"nilcheck","source":"go compiler","message":"",`+
			`"relatedInformation":[{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"}]}`)
		want(t, slogged, `{"range":{"start":{"line":11,"character":6},"end":{"line":11,"character":6}},"severity":3,"code":"isInBounds","source":"go compiler","message":""}`)
		want(t, slogged, `{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":6}},"severity":3,"code":"canInlineFunction","source":"go compiler","message":"cost: 35"}`)
		// escape analysis explanation
		want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r2 with derefs=0",`+
			`"relatedInformation":[`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:    flow: y = z:"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:      from y := z (assign-pair)"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:    flow: ~R0 = y:"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:      from y.b (dot of pointer)"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:      from \u0026y.b (address-of)"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow:      from ~R0 = \u003cN\u003e (assign-pair)"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow:    flow: ~r2 = ~R0:"},`+
			`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow:      from return (*int)(~R0) (return)"}]}`)
	})
}

func testLogOpt(t *testing.T, flag, src, outfile string) (string, error) {
	run := []string{testenv.GoToolPath(t), "tool", "compile", flag, "-o", outfile, src}
	t.Log(run)
	cmd := exec.Command(run[0], run[1:]...)
	out, err := cmd.CombinedOutput()
	t.Logf("%s", out)
	return string(out), err
}

func testLogOptDir(t *testing.T, dir, flag, src, outfile string) (string, error) {
	// Notice the specified import path "x"
	run := []string{testenv.GoToolPath(t), "tool", "compile", "-p", "x", flag, "-o", outfile, src}
	t.Log(run)
	cmd := exec.Command(run[0], run[1:]...)
	cmd.Dir = dir
	out, err := cmd.CombinedOutput()
	t.Logf("%s", out)
	return string(out), err
}

func testCopy(t *testing.T, dir, goarch, goos, src, outfile string) (string, error) {
	// Notice the specified import path "x"
	run := []string{testenv.GoToolPath(t), "tool", "compile", "-p", "x", "-json=0,file://log/opt", "-o", outfile, src}
	t.Log(run)
	cmd := exec.Command(run[0], run[1:]...)
	cmd.Dir = dir
	cmd.Env = append(os.Environ(), "GOARCH="+goarch, "GOOS="+goos)
	out, err := cmd.CombinedOutput()
	t.Logf("%s", out)
	return string(out), err
}
<|endoftext|>


<commit_before>package lfs

import (
	"bytes"
	"io/ioutil"
	"testing"

	"github.com/github/git-lfs/vendor/_nuts/github.com/technoweenie/assert"
)

func TestWriterWithCallback(t *testing.T) {
	called := 0
	calledRead := make([]int64, 0, 2)

	reader := &CallbackReader{
		TotalSize: 5,
		Reader:    bytes.NewBufferString("BOOYA"),
		C: func(total int64, read int64, current int) error {
			called += 1
			calledRead = append(calledRead, read)
			assert.Equal(t, 5, int(total))
			return nil
		},
	}

	readBuf := make([]byte, 3)
	n, err := reader.Read(readBuf)
	assert.Equal(t, nil, err)
	assert.Equal(t, "BOO", string(readBuf[0:n]))

	n, err = reader.Read(readBuf)
	assert.Equal(t, nil, err)
	assert.Equal(t, "YA", string(readBuf[0:n]))

	assert.Equal(t, 2, called)
	assert.Equal(t, 2, len(calledRead))
	assert.Equal(t, 3, int(calledRead[0]))
	assert.Equal(t, 5, int(calledRead[1]))
}

func TestCopyWithCallback(t *testing.T) {
	buf := bytes.NewBufferString("BOOYA")

	called := 0
	calledWritten := make([]int64, 0, 2)

	n, err := CopyWithCallback(ioutil.Discard, buf, 5, func(total int64, written int64, current int) error {
		called += 1
		calledWritten = append(calledWritten, written)
		assert.Equal(t, 5, int(total))
		return nil
	})
	assert.Equal(t, nil, err)
	assert.Equal(t, 5, int(n))

	assert.Equal(t, 1, called)
	assert.Equal(t, 1, len(calledWritten))
	assert.Equal(t, 5, int(calledWritten[0]))
}
<commit_msg>アアー アアアア アー<commit_after>package lfs

import (
	"bytes"
	"io/ioutil"
	"testing"

	"github.com/github/git-lfs/vendor/_nuts/github.com/technoweenie/assert"
)

func TestWriterWithCallback(t *testing.T) {
	called := 0
	calledRead := make([]int64, 0, 2)

	reader := &CallbackReader{
		TotalSize: 5,
		Reader:    bytes.NewBufferString("BOOYA"),
		C: func(total int64, read int64, current int) error {
			called += 1
			calledRead = append(calledRead, read)
			assert.Equal(t, 5, int(total))
			return nil
		},
	}

	readBuf := make([]byte, 3)
	n, err := reader.Read(readBuf)
	assert.Equal(t, nil, err)
	assert.Equal(t, "BOO", string(readBuf[0:n]))

	n, err = reader.Read(readBuf)
	assert.Equal(t, nil, err)
	assert.Equal(t, "YA", string(readBuf[0:n]))

	assert.Equal(t, 2, called)
	assert.Equal(t, 2, len(calledRead))
	assert.Equal(t, 3, int(calledRead[0]))
	assert.Equal(t, 5, int(calledRead[1]))
}

func TestCopyWithCallback(t *testing.T) {
	buf := bytes.NewBufferString("BOOYA")

	called := 0
	calledWritten := make([]int64, 0, 2)

	n, err := CopyWithCallback(ioutil.Discard, buf, 5, func(total int64, written int64, current int) error {
		called += 1
		calledWritten = append(calledWritten, written)
		assert.Equal(t, 5, int(total))
		return nil
	})
	assert.Equal(t, nil, err)
	assert.Equal(t, 5, int(n))

	assert.Equal(t, 1, called)
	assert.Equal(t, 1, len(calledWritten))
	assert.Equal(t, 5, int(calledWritten[0]))
}

func TestFilterIncludeExclude(t *testing.T) {

	// Inclusion
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, nil))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test/filename.dat"}, nil))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"blank", "something", "test/filename.dat", "foo"}, nil))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"blank", "something", "foo"}, nil))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test/notfilename.dat"}, nil))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test"}, nil))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test/*"}, nil))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"nottest"}, nil))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"nottest/*"}, nil))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test/fil*"}, nil))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test/g*"}, nil))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"tes*/*"}, nil))

	// Exclusion
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test/filename.dat"}))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"blank", "something", "test/filename.dat", "foo"}))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"blank", "something", "foo"}))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test/notfilename.dat"}))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test"}))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test/*"}))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"nottest"}))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"nottest/*"}))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test/fil*"}))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test/g*"}))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"tes*/*"}))

	// Both
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test/filename.dat"}, []string{"test/notfilename.dat"}))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test"}, []string{"test/filename.dat"}))
	assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test/*"}, []string{"test/notfile*"}))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test/*"}, []string{"test/file*"}))
	assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"another/*", "test/*"}, []string{"test/notfilename.dat", "test/filename.dat"}))

	if IsWindows() {
		// Extra tests because Windows git reports filenames with / separators
		// but we need to allow \ separators in include/exclude too
		// Can only test this ON Windows because of filepath behaviour
		// Inclusion
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, nil))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test\\filename.dat"}, nil))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"blank", "something", "test\\filename.dat", "foo"}, nil))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"blank", "something", "foo"}, nil))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test\\notfilename.dat"}, nil))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test"}, nil))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test\\*"}, nil))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"nottest"}, nil))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"nottest\\*"}, nil))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test\\fil*"}, nil))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test\\g*"}, nil))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"tes*\\*"}, nil))

		// Exclusion
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test\\filename.dat"}))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"blank", "something", "test\\filename.dat", "foo"}))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"blank", "something", "foo"}))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test\\notfilename.dat"}))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test"}))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test\\*"}))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"nottest"}))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"nottest\\*"}))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test\\fil*"}))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"test\\g*"}))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", nil, []string{"tes*\\*"}))

		// Both
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test\\filename.dat"}, []string{"test\\notfilename.dat"}))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test"}, []string{"test\\filename.dat"}))
		assert.Equal(t, true, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test\\*"}, []string{"test\\notfile*"}))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"test\\*"}, []string{"test\\file*"}))
		assert.Equal(t, false, FilenamePassesIncludeExcludeFilter("test/filename.dat", []string{"another\\*", "test\\*"}, []string{"test\\notfilename.dat", "test\\filename.dat"}))

	}
}
<|endoftext|>


<commit_before><commit_msg>Change metric name (#30)<commit_after><|endoftext|>


<commit_before>package gqt_test

import (
	"fmt"
	"net"
	"strings"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Networking Uniqueness", func() {
	var (
		client *runner.RunningGarden
	)

	BeforeEach(func() {
		client = runner.Start(config)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	It("should not allocate duplicate subnets", func() {
		for i := 0; i < 10; i++ {
			go func() {
				defer GinkgoRecover()

				create(client, 5)
			}()
		}

		Eventually(numContainers(client), "20s").Should(Equal(50))
		Expect(numBridges()).To(Equal(50))
	})
})

func create(client *runner.RunningGarden, n int) {
	for i := 0; i < n; i++ {
		_, err := client.Create(garden.ContainerSpec{})
		Expect(err).NotTo(HaveOccurred())
	}
}

func numContainers(client *runner.RunningGarden) func() int {
	return func() int {
		containers, err := client.Containers(nil)
		Expect(err).NotTo(HaveOccurred())
		return len(containers)
	}
}

func numBridges() int {
	intfs, err := net.Interfaces()
	Expect(err).NotTo(HaveOccurred())

	bridgeCount := 0

	for _, intf := range intfs {
		if strings.Contains(intf.Name, fmt.Sprintf("w%dbrdg", GinkgoParallelNode())) {
			bridgeCount++
		}
	}

	return bridgeCount
}
<commit_msg>Revert "Add unique net test"<commit_after><|endoftext|>


<commit_before>package standard

import (
	"io"
	"net"
	"net/http"

	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/logger"
)

type Response struct {
	config         *engine.Config
	response       http.ResponseWriter
	request        *http.Request
	header         engine.Header
	status         int
	size           int64
	committed      bool
	writer         io.Writer
	logger         logger.Logger
	body           []byte
	keepBody       bool
	responseWriter *responseWriter
}

func NewResponse(w http.ResponseWriter, r *http.Request, l logger.Logger) *Response {
	return &Response{
		response: w,
		request:  r,
		header:   &Header{Header: w.Header()},
		writer:   w,
		logger:   l,
	}
}

func (r *Response) Header() engine.Header {
	return r.header
}

func (r *Response) WriteHeader(code int) {
	if r.committed {
		r.logger.Warn("response already committed")
		return
	}
	r.status = code
	r.response.WriteHeader(code)
	r.committed = true
}

func (r *Response) KeepBody(on bool) {
	r.keepBody = on
}

func (r *Response) Write(b []byte) (n int, err error) {
	if !r.committed {
		if r.status == 0 {
			r.status = http.StatusOK
		}
		r.WriteHeader(r.status)
	}
	if r.keepBody {
		r.body = append(r.body, b...)
	}
	n, err = r.writer.Write(b)
	r.size += int64(n)
	return
}

func (r *Response) Status() int {
	return r.status
}

func (r *Response) Size() int64 {
	return r.size
}

func (r *Response) Committed() bool {
	return r.committed
}

func (r *Response) SetWriter(w io.Writer) {
	r.writer = w
}

func (r *Response) Writer() io.Writer {
	return r.writer
}

func (r *Response) Object() interface{} {
	return r.response
}

func (r *Response) Error(errMsg string, args ...int) {
	if len(args) > 0 {
		r.status = args[0]
	} else {
		r.status = http.StatusInternalServerError
	}
	r.Write(engine.Str2bytes(errMsg))
	r.WriteHeader(r.status)
}

func (r *Response) reset(w http.ResponseWriter, req *http.Request, h engine.Header) {
	r.response = w
	r.request = req
	r.header = h
	r.status = http.StatusOK
	r.size = 0
	r.committed = false
	r.writer = w
	r.body = nil
	r.keepBody = false
	r.responseWriter = &responseWriter{r}
}

func (r *Response) Hijack(fn func(net.Conn)) {
	conn, bufrw, err := r.response.(http.Hijacker).Hijack()
	if err != nil {
		r.logger.Error(err)
	}
	_ = bufrw
	fn(conn)
	conn.Close()
	r.committed = true
}

func (r *Response) Body() []byte {
	return r.body
}

func (r *Response) Redirect(url string, code int) {
	http.Redirect(r.response, r.request, url, code)
	r.committed = true
}

func (r *Response) NotFound() {
	http.Error(r.response, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	r.committed = true
}

func (r *Response) SetCookie(cookie *http.Cookie) {
	r.header.Add(engine.HeaderSetCookie, cookie.String())
}

func (r *Response) ServeFile(file string) {
	r.keepBody = false
	http.ServeFile(r.response, r.request, file)
	r.committed = true
}

func (r *Response) Stream(step func(io.Writer) bool) {
	w := r.response
	clientGone := w.(http.CloseNotifier).CloseNotify()
	for {
		select {
		case <-clientGone:
			return
		default:
			keepOpen := step(w)
			w.(http.Flusher).Flush()
			if !keepOpen {
				return
			}
		}
	}
}

func (r *Response) StdResponseWriter() http.ResponseWriter {
	return r.responseWriter
}

type responseWriter struct {
	response *Response
}

func (r *responseWriter) Header() http.Header {
	return r.response.header.(*Header).Header
}

func (r *responseWriter) Write(b []byte) (n int, err error) {
	return r.response.Write(b)
}

func (r *responseWriter) WriteHeader(code int) {
	r.response.WriteHeader(code)
}
<commit_msg>update<commit_after>package standard

import (
	"io"
	"net"
	"net/http"

	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/logger"
)

type Response struct {
	config         *engine.Config
	response       http.ResponseWriter
	request        *http.Request
	header         engine.Header
	status         int
	size           int64
	committed      bool
	writer         io.Writer
	logger         logger.Logger
	body           []byte
	keepBody       bool
	responseWriter *responseWriter
}

func NewResponse(w http.ResponseWriter, r *http.Request, l logger.Logger) *Response {
	return &Response{
		response: w,
		request:  r,
		header:   &Header{Header: w.Header()},
		writer:   w,
		logger:   l,
	}
}

func (r *Response) Header() engine.Header {
	return r.header
}

func (r *Response) WriteHeader(code int) {
	if r.committed {
		r.logger.Warn("response already committed")
		return
	}
	r.status = code
	r.response.WriteHeader(code)
	r.committed = true
}

func (r *Response) KeepBody(on bool) {
	r.keepBody = on
}

func (r *Response) Write(b []byte) (n int, err error) {
	if !r.committed {
		if r.status == 0 {
			r.status = http.StatusOK
		}
		r.WriteHeader(r.status)
	}
	if r.keepBody {
		r.body = append(r.body, b...)
	}
	n, err = r.writer.Write(b)
	r.size += int64(n)
	return
}

func (r *Response) Status() int {
	return r.status
}

func (r *Response) Size() int64 {
	return r.size
}

func (r *Response) Committed() bool {
	return r.committed
}

func (r *Response) SetWriter(w io.Writer) {
	r.writer = w
}

func (r *Response) Writer() io.Writer {
	return r.writer
}

func (r *Response) Object() interface{} {
	return r.response
}

func (r *Response) Error(errMsg string, args ...int) {
	if len(args) > 0 {
		r.status = args[0]
	} else {
		r.status = http.StatusInternalServerError
	}
	r.Write(engine.Str2bytes(errMsg))
	r.WriteHeader(r.status)
}

func (r *Response) reset(w http.ResponseWriter, req *http.Request, h engine.Header) {
	r.response = w
	r.request = req
	r.header = h
	r.status = http.StatusOK
	r.size = 0
	r.committed = false
	r.writer = w
	r.body = nil
	r.keepBody = false
	r.responseWriter = &responseWriter{r}
}

func (r *Response) Hijack(fn func(net.Conn)) {
	conn, bufrw, err := r.response.(http.Hijacker).Hijack()
	if err != nil {
		r.logger.Error(err)
	}
	_ = bufrw
	fn(conn)
	conn.Close()
	r.committed = true
}

func (r *Response) Body() []byte {
	return r.body
}

func (r *Response) Redirect(url string, code int) {
	http.Redirect(r.response, r.request, url, code)
	r.committed = true
}

func (r *Response) NotFound() {
	http.Error(r.response, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	r.committed = true
}

func (r *Response) SetCookie(cookie *http.Cookie) {
	r.header.Add(engine.HeaderSetCookie, cookie.String())
}

func (r *Response) ServeFile(file string) {
	r.keepBody = false
	http.ServeFile(r.response, r.request, file)
	r.committed = true
}

func (r *Response) Stream(step func(io.Writer) bool) {
	w := r.response
	clientGone := w.(http.CloseNotifier).CloseNotify()
	for {
		select {
		case <-clientGone:
			return
		default:
			keepOpen := step(w)
			w.(http.Flusher).Flush()
			if !keepOpen {
				return
			}
		}
	}
}

func (r *Response) StdResponseWriter() http.ResponseWriter {
	return r.responseWriter
}

type responseWriter struct {
	response *Response
}

func (r *responseWriter) StatusCode() int {
	if r.response.Status() == 0 {
		return http.StatusOK
	}
	return r.response.Status()
}

func (r *responseWriter) Header() http.Header {
	return r.response.header.(*Header).Header
}

func (r *responseWriter) Write(b []byte) (n int, err error) {
	return r.response.Write(b)
}

func (r *responseWriter) WriteHeader(code int) {
	r.response.WriteHeader(code)
}
<|endoftext|>


<commit_before>package main

import (
    "flag"
    "log"
    "os"
    "image/png"
    "errors"
    "functorama.com/demo/libgodelbrot"
)

type commandLine struct {
    iterateLimit uint
    divergeLimit float64
    width uint
    height uint
    filename string
    xOffset float64
    yOffset float64
    zoom float64
    mode string
    regionCollapse uint
}

func parseArguments(args *commandLine) {
    xOffset := real(libgodelbrot.MagicOffset)
    yOffset := imag(libgodelbrot.MagicOffset)

    flag.UintVar(&args.iterateLimit, "iterateLimit", 255, "Maximum number of iterations")
    flag.Float64Var(&args.divergeLimit, "divergeLimit", 4.0, "Limit where function is said to diverge to infinity")
    flag.UintVar(&args.width, "imageWidth", 800, "Width of output PNG")
    flag.UintVar(&args.height, "imageHeight", 600, "Height of output PNG")
    flag.StringVar(&args.filename, "filename", "mandelbrot.png", "Name of output PNG")
    flag.Float64Var(&args.xOffset, "realOffset", xOffset, "Leftmost position of complex plane projected onto PNG image")
    flag.Float64Var(&args.yOffset, "imagOffset", yOffset, "Topmost position of complex plane projected onto PNG image")
    flag.Float64Var(&args.zoom, "zoom", 1.0, "Look into the eyeball")
    flag.StringVar(&args.mode, "mode", "sequential", "Render mode")
    flag.UintVar(&args.regionCollapse, 2, "Pixel width of region at which sequential render is forced")
    flag.Parse()
}

func extractRenderParameters(args commandLine) (*libgodelbrot.RenderConfig, error) {
    if args.iterateLimit > 255 {
        return nil, errors.New("iterateLimit out of bounds (uint8)")
    }

    if args.divergeLimit <= 0.0 {
        return nil, errors.New("divergeLimit out of bounds (positive float64)")
    }

    if args.zoom <= 0.0 {
        return nil, errors.New("zoom out of bounds (positive float64)")
    }

    parameters := libgodelbrot.RenderConfig{
        IterateLimit: uint8(args.iterateLimit),
        DivergeLimit: args.divergeLimit,
        Width: args.width,
        Height: args.height,
        XOffset: args.xOffset,
        YOffset: args.yOffset,
        Zoom: args.zoom,
        RegionCollapse: args.regionCollapse,
    }
    config := parameters.Configure()
    return config, nil
}

func main() {
    args := commandLine{}
    parseArguments(&args)

    var renderer libgodelbrot.Renderer
    switch args.mode {
    case "sequential":
        renderer = libgodelbrot.SequentialRender
    case "region":
        renderer = libgodelbrot.RegionRender
    default:
        log.Fatal("Unknown renderer")
    }

    config, validationError := extractRenderParameters(args)
    if validationError != nil {
        log.Fatal(validationError)
    }

    // Redscale is the only palette we have available
    redscale := NewRedscalePalette(config.IterateLimit)

    image, renderError := renderer(config, redscale)
    if renderError != nil {
        log.Fatal(renderError)
    }

    file, fileError := os.Create(args.filename)

    if fileError != nil {
        log.Fatal(fileError)
    }
    defer file.Close()

    writeError := png.Encode(file, image)

    if writeError != nil {
        log.Fatal(writeError)
    }
}<commit_msg>Changed --filename argument to --output<commit_after>package main

import (
    "flag"
    "log"
    "os"
    "image/png"
    "errors"
    "functorama.com/demo/libgodelbrot"
)

type commandLine struct {
    iterateLimit uint
    divergeLimit float64
    width uint
    height uint
    filename string
    xOffset float64
    yOffset float64
    zoom float64
    mode string
    regionCollapse uint
}

func parseArguments(args *commandLine) {
    xOffset := real(libgodelbrot.MagicOffset)
    yOffset := imag(libgodelbrot.MagicOffset)

    flag.UintVar(&args.iterateLimit, "iterateLimit", 255, "Maximum number of iterations")
    flag.Float64Var(&args.divergeLimit, "divergeLimit", 4.0, "Limit where function is said to diverge to infinity")
    flag.UintVar(&args.width, "imageWidth", 800, "Width of output PNG")
    flag.UintVar(&args.height, "imageHeight", 600, "Height of output PNG")
    flag.StringVar(&args.filename, "output", "mandelbrot.png", "Name of output PNG")
    flag.Float64Var(&args.xOffset, "realOffset", xOffset, "Leftmost position of complex plane projected onto PNG image")
    flag.Float64Var(&args.yOffset, "imagOffset", yOffset, "Topmost position of complex plane projected onto PNG image")
    flag.Float64Var(&args.zoom, "zoom", 1.0, "Look into the eyeball")
    flag.StringVar(&args.mode, "mode", "sequential", "Render mode")
    flag.UintVar(&args.regionCollapse, 2, "Pixel width of region at which sequential render is forced")
    flag.Parse()
}

func extractRenderParameters(args commandLine) (*libgodelbrot.RenderConfig, error) {
    if args.iterateLimit > 255 {
        return nil, errors.New("iterateLimit out of bounds (uint8)")
    }

    if args.divergeLimit <= 0.0 {
        return nil, errors.New("divergeLimit out of bounds (positive float64)")
    }

    if args.zoom <= 0.0 {
        return nil, errors.New("zoom out of bounds (positive float64)")
    }

    parameters := libgodelbrot.RenderConfig{
        IterateLimit: uint8(args.iterateLimit),
        DivergeLimit: args.divergeLimit,
        Width: args.width,
        Height: args.height,
        XOffset: args.xOffset,
        YOffset: args.yOffset,
        Zoom: args.zoom,
        RegionCollapse: args.regionCollapse,
    }
    config := parameters.Configure()
    return config, nil
}

func main() {
    args := commandLine{}
    parseArguments(&args)

    var renderer libgodelbrot.Renderer
    switch args.mode {
    case "sequential":
        renderer = libgodelbrot.SequentialRender
    case "region":
        renderer = libgodelbrot.RegionRender
    default:
        log.Fatal("Unknown renderer")
    }

    config, validationError := extractRenderParameters(args)
    if validationError != nil {
        log.Fatal(validationError)
    }

    // Redscale is the only palette we have available
    redscale := NewRedscalePalette(config.IterateLimit)

    image, renderError := renderer(config, redscale)
    if renderError != nil {
        log.Fatal(renderError)
    }

    file, fileError := os.Create(args.filename)

    if fileError != nil {
        log.Fatal(fileError)
    }
    defer file.Close()

    writeError := png.Encode(file, image)

    if writeError != nil {
        log.Fatal(writeError)
    }
}<|endoftext|>


<commit_before>// Copyright ©2017 The gonum 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 path

import (
	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/internal/set"
)

// Dominators returns immediate dominators for all nodes in the flow graph
// g starting from the given root node.
func Dominators(root graph.Node, g graph.Directed) map[int64]graph.Node {
	// The algorithm used here is the Lengauer and Tarjan
	// algorithm described in https://doi.org/10.1145%2F357062.357071

	lt := lengauerTarjan{
		parent: make(map[int64]graph.Node),
		pred:   make(map[int64][]graph.Node),
		semi:   make(map[int64]int),
		vertex: make([]graph.Node, 1), // FIXME(kortschak): The current implementation requires non-zero node numbers.
		bucket: make(map[int64]set.Nodes),
		dom:    make(map[int64]graph.Node),

		ancestor: make(map[int64]graph.Node),
		label:    make(map[int64]graph.Node),
	}

	// step 1.
	lt.dfs(g, root)

	for i := lt.n; i >= 2; i-- {
		w := lt.vertex[i]
		wid := w.ID()
		// step 2.
		for _, v := range lt.pred[wid] {
			uid := lt.eval(v).ID()
			if lt.semi[uid] < lt.semi[wid] {
				lt.semi[wid] = lt.semi[uid]
			}
		}

		b, ok := lt.bucket[lt.vertex[lt.semi[wid]].ID()]
		if !ok {
			b = make(set.Nodes)
			lt.bucket[lt.vertex[lt.semi[wid]].ID()] = b
		}
		b.Add(w)
		lt.link(lt.parent[wid], w)

		// step 3.
		for _, v := range lt.bucket[lt.parent[wid].ID()] {
			vid := v.ID()
			lt.bucket[lt.parent[wid].ID()].Remove(v)
			u := lt.eval(v)
			if lt.semi[u.ID()] < lt.semi[vid] {
				lt.dom[vid] = u
			} else {
				lt.dom[vid] = lt.parent[wid]
			}
		}

	}

	// step 4.
	for i := 2; i <= lt.n; i++ {
		w := lt.vertex[i]
		wid := w.ID()
		if lt.dom[wid].ID() != lt.vertex[lt.semi[wid]].ID() {
			lt.dom[wid] = lt.dom[lt.dom[wid].ID()]
		}
	}
	delete(lt.dom, root.ID())

	return lt.dom
}

type lengauerTarjan struct {
	// The vertex which is the parent of vertex w
	// in the spanning tree generated by the search.
	parent map[int64]graph.Node

	// The set of vertices v such that (v, w) is an edge
	// of the graph.
	pred map[int64][]graph.Node

	// semi[w] is a number defined as follows:
	// (i)   Before vertex w is numbered, semi[w] = 0, false.
	// (ii)  After w is numbered but before its semidominator
	//       is computed, semi[w] is the number of w.
	// (iii) After the semidominator of w is computed, semi[w]
	//       is the number of the semidominator of w.
	semi map[int64]int

	// vertex[i] is the vertex whose number is i.
	vertex []graph.Node

	// bucket[w] is the set of vertices whose
	// semidominator is w.
	bucket map[int64]set.Nodes

	// dom[w] is vertex defined as follows:
	// (i)  After step 3, if the semidominator of w is its
	//      immediate dominator, then dom[w] is the immediate
	//      dominator of w. Otherwise dom[w] is a vertex v
	//      whose number is smaller than w and whose immediate
	//      dominator is also w's immediate dominator.
	// (ii) After step 4, dom[w] is the immediate dominator of w.
	dom map[int64]graph.Node

	// In general ancestor[v] = 0 only if v is a tree root
	// in the forest; otherwise ancestor[v] is an ancestor
	// of v in the forest.
	ancestor map[int64]graph.Node

	// Initially label[v] is v.
	label map[int64]graph.Node

	n int
}

func (lt *lengauerTarjan) dfs(g graph.Directed, v graph.Node) {
	vid := v.ID()
	lt.n++
	lt.semi[vid] = lt.n
	lt.label[vid] = v
	lt.vertex = append(lt.vertex, v)
	for _, w := range g.From(v) {
		wid := w.ID()
		if _, ok := lt.semi[wid]; !ok {
			lt.parent[wid] = v
			lt.dfs(g, w)
		}
		lt.pred[wid] = append(lt.pred[wid], v)
	}
}

func (lt *lengauerTarjan) compress(v graph.Node) {
	vid := v.ID()
	if _, ok := lt.ancestor[lt.ancestor[vid].ID()]; ok {
		lt.compress(lt.ancestor[vid])
		if lt.semi[lt.label[lt.ancestor[vid].ID()].ID()] < lt.semi[lt.label[vid].ID()] {
			lt.label[vid] = lt.label[lt.ancestor[vid].ID()]
		}
		lt.ancestor[vid] = lt.ancestor[lt.ancestor[vid].ID()]
	}
}

func (lt *lengauerTarjan) eval(v graph.Node) graph.Node {
	vid := v.ID()
	if _, ok := lt.ancestor[vid]; !ok {
		return v
	}
	lt.compress(v)
	return lt.label[vid]
}

func (lt *lengauerTarjan) link(v, w graph.Node) {
	lt.ancestor[w.ID()] = v
}
<commit_msg>graph/path: replace recursive dfs with iterative implementation<commit_after>// Copyright ©2017 The gonum 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 path

import (
	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/internal/set"
	"gonum.org/v1/gonum/graph/traverse"
)

// Dominators returns immediate dominators for all nodes in the flow graph
// g starting from the given root node.
func Dominators(root graph.Node, g graph.Directed) map[int64]graph.Node {
	// The algorithm used here is the Lengauer and Tarjan
	// algorithm described in https://doi.org/10.1145%2F357062.357071

	lt := lengauerTarjan{
		parent: make(map[int64]graph.Node),
		pred:   make(map[int64][]graph.Node),
		semi:   make(map[int64]int),
		vertex: make([]graph.Node, 1), // FIXME(kortschak): The current implementation requires non-zero node numbers.
		bucket: make(map[int64]set.Nodes),
		dom:    make(map[int64]graph.Node),

		ancestor: make(map[int64]graph.Node),
		label:    make(map[int64]graph.Node),
	}

	// step 1.
	lt.dfs(g, root)

	for i := len(lt.vertex) - 1; i >= 2; i-- {
		w := lt.vertex[i]
		wid := w.ID()
		// step 2.
		for _, v := range lt.pred[wid] {
			uid := lt.eval(v).ID()
			if lt.semi[uid] < lt.semi[wid] {
				lt.semi[wid] = lt.semi[uid]
			}
		}

		b, ok := lt.bucket[lt.vertex[lt.semi[wid]].ID()]
		if !ok {
			b = make(set.Nodes)
			lt.bucket[lt.vertex[lt.semi[wid]].ID()] = b
		}
		b.Add(w)
		lt.link(lt.parent[wid], w)

		// step 3.
		for _, v := range lt.bucket[lt.parent[wid].ID()] {
			vid := v.ID()
			lt.bucket[lt.parent[wid].ID()].Remove(v)
			u := lt.eval(v)
			if lt.semi[u.ID()] < lt.semi[vid] {
				lt.dom[vid] = u
			} else {
				lt.dom[vid] = lt.parent[wid]
			}
		}

	}

	// step 4.
	for _, w := range lt.vertex[2:] {
		wid := w.ID()
		if lt.dom[wid].ID() != lt.vertex[lt.semi[wid]].ID() {
			lt.dom[wid] = lt.dom[lt.dom[wid].ID()]
		}
	}
	delete(lt.dom, root.ID())

	return lt.dom
}

type lengauerTarjan struct {
	// The vertex which is the parent of vertex w
	// in the spanning tree generated by the search.
	parent map[int64]graph.Node

	// The set of vertices v such that (v, w) is an edge
	// of the graph.
	pred map[int64][]graph.Node

	// semi[w] is a number defined as follows:
	// (i)   Before vertex w is numbered, semi[w] = 0, false.
	// (ii)  After w is numbered but before its semidominator
	//       is computed, semi[w] is the number of w.
	// (iii) After the semidominator of w is computed, semi[w]
	//       is the number of the semidominator of w.
	semi map[int64]int

	// vertex[i] is the vertex whose number is i.
	vertex []graph.Node

	// bucket[w] is the set of vertices whose
	// semidominator is w.
	bucket map[int64]set.Nodes

	// dom[w] is vertex defined as follows:
	// (i)  After step 3, if the semidominator of w is its
	//      immediate dominator, then dom[w] is the immediate
	//      dominator of w. Otherwise dom[w] is a vertex v
	//      whose number is smaller than w and whose immediate
	//      dominator is also w's immediate dominator.
	// (ii) After step 4, dom[w] is the immediate dominator of w.
	dom map[int64]graph.Node

	// In general ancestor[v] = 0 only if v is a tree root
	// in the forest; otherwise ancestor[v] is an ancestor
	// of v in the forest.
	ancestor map[int64]graph.Node

	// Initially label[v] is v.
	label map[int64]graph.Node
}

func (lt *lengauerTarjan) dfs(g graph.Directed, root graph.Node) {
	df := traverse.DepthFirst{EdgeFilter: func(e graph.Edge) bool {
		u := e.From()
		vid := e.To().ID()
		lt.pred[vid] = append(lt.pred[vid], u)
		if _, ok := lt.semi[vid]; ok {
			return false
		}
		lt.parent[vid] = u
		return true
	}}
	df.Walk(g, root, func(u graph.Node) bool {
		uid := u.ID()
		lt.label[uid] = u
		lt.semi[uid] = len(lt.vertex)
		lt.vertex = append(lt.vertex, u)
		return false
	})
}

func (lt *lengauerTarjan) compress(v graph.Node) {
	vid := v.ID()
	if _, ok := lt.ancestor[lt.ancestor[vid].ID()]; ok {
		lt.compress(lt.ancestor[vid])
		if lt.semi[lt.label[lt.ancestor[vid].ID()].ID()] < lt.semi[lt.label[vid].ID()] {
			lt.label[vid] = lt.label[lt.ancestor[vid].ID()]
		}
		lt.ancestor[vid] = lt.ancestor[lt.ancestor[vid].ID()]
	}
}

func (lt *lengauerTarjan) eval(v graph.Node) graph.Node {
	vid := v.ID()
	if _, ok := lt.ancestor[vid]; !ok {
		return v
	}
	lt.compress(v)
	return lt.label[vid]
}

func (lt *lengauerTarjan) link(v, w graph.Node) {
	lt.ancestor[w.ID()] = v
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/rackerlabs/libcarina"
	"github.com/rgbkrk/interlocarina/plugins"
	"github.com/rgbkrk/interlocarina/version"
)

func main() {
	app := cli.NewApp()
	app.Name = "interlock"
	app.Version = version.FullVersion()
	app.Author = "@rgbkrk"
	app.Email = ""
	app.Usage = "event driven docker plugins"
	app.Before = func(c *cli.Context) error {
		if c.GlobalBool("debug") {
			log.SetLevel(log.DebugLevel)
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "username",
			Usage: "carina username",
		},
		cli.StringFlag{
			Name:  "api-key",
			Usage: "carina API key",
		},
		cli.StringFlag{
			Name:  "clustername",
			Usage: "name of the swarm cluster",
		},
		cli.StringFlag{
			Name:  "endpoint",
			Value: libcarina.BetaEndpoint,
			Usage: "endpoint for carina",
		},
		cli.StringSliceFlag{
			Name:  "plugin, p",
			Usage: "enable plugin",
			Value: &cli.StringSlice{},
		},
		cli.BoolFlag{
			Name:  "debug, D",
			Usage: "enable debug",
		},
	}
	// base commands
	baseCommands := []cli.Command{
		{
			Name:   "start",
			Action: cmdStart,
		},
		{
			Name:   "list-plugins",
			Action: cmdListPlugins,
		},
		{
			Name:   "info",
			Action: cmdInfo,
		},
	}
	// plugin supplied commands
	baseCommands = append(baseCommands, plugins.GetCommands()...)

	app.Commands = baseCommands

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Alias apikey anyway.<commit_after>package main

import (
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/rackerlabs/libcarina"
	"github.com/rgbkrk/interlocarina/plugins"
	"github.com/rgbkrk/interlocarina/version"
)

func main() {
	app := cli.NewApp()
	app.Name = "interlock"
	app.Version = version.FullVersion()
	app.Author = "@rgbkrk"
	app.Email = ""
	app.Usage = "event driven docker plugins"
	app.Before = func(c *cli.Context) error {
		if c.GlobalBool("debug") {
			log.SetLevel(log.DebugLevel)
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "username",
			Usage: "carina username",
		},
		cli.StringFlag{
			Name:  "api-key, apikey",
			Usage: "carina API key",
		},
		cli.StringFlag{
			Name:  "clustername",
			Usage: "name of the swarm cluster",
		},
		cli.StringFlag{
			Name:  "endpoint",
			Value: libcarina.BetaEndpoint,
			Usage: "endpoint for carina",
		},
		cli.StringSliceFlag{
			Name:  "plugin, p",
			Usage: "enable plugin",
			Value: &cli.StringSlice{},
		},
		cli.BoolFlag{
			Name:  "debug, D",
			Usage: "enable debug",
		},
	}
	// base commands
	baseCommands := []cli.Command{
		{
			Name:   "start",
			Action: cmdStart,
		},
		{
			Name:   "list-plugins",
			Action: cmdListPlugins,
		},
		{
			Name:   "info",
			Action: cmdInfo,
		},
	}
	// plugin supplied commands
	baseCommands = append(baseCommands, plugins.GetCommands()...)

	app.Commands = baseCommands

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All Rights Reserved.
//
// 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 internal provides support for package cloud.
//
// Users should not import this package directly.
package internal

import (
	"fmt"
	"net/http"
)

const userAgent = "gcloud-golang/0.1"

// UATransport is an http.RoundTripper that appends
// Google Cloud client's user-agent to the original
// request's user-agent header.
type UATransport struct {
	// Base represents the actual http.RoundTripper
	// the requests will be delegated to.
	Base http.RoundTripper
}

// RoundTrip appends a user-agent to the existing user-agent
// header and delegates the request to the base http.RoundTripper.
func (t *UATransport) RoundTrip(req *http.Request) (*http.Response, error) {
	// TODO(jbd): Is it OK to mutate request? UATransport is internal only.
	ua := req.Header.Get("User-Agent")
	if ua == "" {
		ua = userAgent
	} else {
		ua = fmt.Sprintf("%s;%s", ua, userAgent)
	}
	req.Header.Set("User-Agent", ua)
	return t.Base.RoundTrip(req)
}
<commit_msg>storage: UATransport shouldn't mutate the request.<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// 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 internal provides support for package cloud.
//
// Users should not import this package directly.
package internal

import (
	"fmt"
	"net/http"
)

const userAgent = "gcloud-golang/0.1"

// UATransport is an http.RoundTripper that appends
// Google Cloud client's user-agent to the original
// request's user-agent header.
type UATransport struct {
	// Base represents the actual http.RoundTripper
	// the requests will be delegated to.
	Base http.RoundTripper
}

// RoundTrip appends a user-agent to the existing user-agent
// header and delegates the request to the base http.RoundTripper.
func (t *UATransport) RoundTrip(req *http.Request) (*http.Response, error) {
	req = cloneRequest(req)
	ua := req.Header.Get("User-Agent")
	if ua == "" {
		ua = userAgent
	} else {
		ua = fmt.Sprintf("%s;%s", ua, userAgent)
	}
	req.Header.Set("User-Agent", ua)
	return t.Base.RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}
<|endoftext|>