<commit_before>package worker

import (
	"context"
	"fmt"
	"path/filepath"
	"sort"
	"time"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/lager"
	"github.com/concourse/baggageclaim"
	"github.com/concourse/concourse/atc/creds"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/db/lock"
	"github.com/concourse/concourse/atc/metric"
)

const creatingContainerRetryDelay = 1 * time.Second

func NewContainerProvider(
	gardenClient garden.Client,
	volumeClient VolumeClient,
	dbWorker db.Worker,
	imageFactory ImageFactory,
	dbVolumeRepository db.VolumeRepository,
	dbTeamFactory db.TeamFactory,
	lockFactory lock.LockFactory,
) ContainerProvider {

	return &containerProvider{
		gardenClient:       gardenClient,
		volumeClient:       volumeClient,
		imageFactory:       imageFactory,
		dbVolumeRepository: dbVolumeRepository,
		dbTeamFactory:      dbTeamFactory,
		lockFactory:        lockFactory,
		httpProxyURL:       dbWorker.HTTPProxyURL(),
		httpsProxyURL:      dbWorker.HTTPSProxyURL(),
		noProxy:            dbWorker.NoProxy(),
		worker:             dbWorker,
	}
}

//go:generate counterfeiter . ContainerProvider

type ContainerProvider interface {
	FindCreatedContainerByHandle(
		logger lager.Logger,
		handle string,
		teamID int,
	) (Container, bool, error)

	FindOrCreateContainer(
		ctx context.Context,
		logger lager.Logger,
		owner db.ContainerOwner,
		delegate ImageFetchingDelegate,
		metadata db.ContainerMetadata,
		containerSpec ContainerSpec,
		workerSpec WorkerSpec,
		resourceTypes creds.VersionedResourceTypes,
		image Image,
	) (Container, error)
}

// TODO: Remove the ImageFactory from the containerProvider.
// Currently, the imageFactory is only needed to create a garden
// worker in createGardenContainer. Creating a garden worker here
// is cyclical because the garden worker contains a containerProvider.
// There is an ongoing refactor that is attempting to fix this.
type containerProvider struct {
	gardenClient       garden.Client
	volumeClient       VolumeClient
	imageFactory       ImageFactory
	dbVolumeRepository db.VolumeRepository
	dbTeamFactory      db.TeamFactory

	lockFactory lock.LockFactory

	worker        db.Worker
	httpProxyURL  string
	httpsProxyURL string
	noProxy       string
}

// If a created container exists, a garden.Container must also exist
// so this method will find it, create the corresponding worker.Container
// and return it.
// If no created container exists, FindOrCreateContainer will go through
// the container creation flow i.e. find or create a CreatingContainer,
// create the garden.Container and then the CreatedContainer
func (p *containerProvider) FindOrCreateContainer(
	ctx context.Context,
	logger lager.Logger,
	owner db.ContainerOwner,
	delegate ImageFetchingDelegate,
	metadata db.ContainerMetadata,
	containerSpec ContainerSpec,
	workerSpec WorkerSpec,
	resourceTypes creds.VersionedResourceTypes,
	image Image,
) (Container, error) {
	var (
		gardenContainer   garden.Container
		createdContainer  db.CreatedContainer
		creatingContainer db.CreatingContainer
		err               error
	)

	for {
		creatingContainer, createdContainer, err = p.worker.FindContainerOnWorker(
			owner,
		)
		if err != nil {
			logger.Error("failed-to-find-container-in-db", err)
			return nil, err
		}

		if createdContainer != nil {
			logger = logger.WithData(lager.Data{"container": createdContainer.Handle()})

			logger.Debug("found-created-container-in-db")

			gardenContainer, err = p.gardenClient.Lookup(createdContainer.Handle())
			if err != nil {
				logger.Error("failed-to-lookup-created-container-in-garden", err)
				return nil, err
			}

			return p.constructGardenWorkerContainer(
				logger,
				createdContainer,
				gardenContainer,
			)
		}

		if creatingContainer == nil {
			logger.Debug("creating-container-in-db")

			creatingContainer, err = p.worker.CreateContainer(
				owner,
				metadata,
			)
			if err != nil {
				logger.Error("failed-to-create-container-in-db", err)
				return nil, err
			}

			logger = logger.WithData(lager.Data{"container": creatingContainer.Handle()})
			logger.Debug("created-creating-container-in-db")
		} else {
			logger = logger.WithData(lager.Data{"container": creatingContainer.Handle()})
			logger.Debug("found-creating-container-in-db")
		}

		gardenContainer, err = p.gardenClient.Lookup(creatingContainer.Handle())
		if err != nil {
			if _, ok := err.(garden.ContainerNotFoundError); !ok {
				logger.Error("failed-to-lookup-creating-container-in-garden", err)
				return nil, err
			}
		}

		if gardenContainer == nil {
			containerLock, acquired, err := p.lockFactory.Acquire(logger, lock.NewContainerCreatingLockID(creatingContainer.ID()))
			if err != nil {
				logger.Error("failed-to-acquire-container-creating-lock", err)
				return nil, err
			}

			if !acquired {
				time.Sleep(creatingContainerRetryDelay)
				continue
			}

			defer containerLock.Release()

			logger.Debug("fetching-image")

			fetchedImage, err := image.FetchForContainer(ctx, logger, creatingContainer)
			if err != nil {
				creatingContainer.Failed()
				logger.Error("failed-to-fetch-image-for-container", err)
				return nil, err
			}

			logger.Debug("creating-container-in-garden")

			gardenContainer, err = p.createGardenContainer(
				logger,
				creatingContainer,
				containerSpec,
				fetchedImage,
			)
			if err != nil {
				_, failedErr := creatingContainer.Failed()
				if failedErr != nil {
					logger.Error("failed-to-mark-container-as-failed", err)
				}
				metric.FailedContainers.Inc()

				logger.Error("failed-to-create-container-in-garden", err)
				return nil, err
			}

			metric.ContainersCreated.Inc()

			logger.Debug("created-container-in-garden")
		} else {
			logger.Debug("found-created-container-in-garden")
		}

		createdContainer, err = creatingContainer.Created()
		if err != nil {
			logger.Error("failed-to-mark-container-as-created", err)

			_ = p.gardenClient.Destroy(creatingContainer.Handle())

			return nil, err
		}

		logger.Debug("created-container-in-db")

		return p.constructGardenWorkerContainer(
			logger,
			createdContainer,
			gardenContainer,
		)
	}
}

func (p *containerProvider) FindCreatedContainerByHandle(
	logger lager.Logger,
	handle string,
	teamID int,
) (Container, bool, error) {
	gardenContainer, err := p.gardenClient.Lookup(handle)
	if err != nil {
		if _, ok := err.(garden.ContainerNotFoundError); ok {
			logger.Info("container-not-found")
			return nil, false, nil
		}

		logger.Error("failed-to-lookup-on-garden", err)
		return nil, false, err
	}

	createdContainer, found, err := p.dbTeamFactory.GetByID(teamID).FindCreatedContainerByHandle(handle)
	if err != nil {
		logger.Error("failed-to-lookup-in-db", err)
		return nil, false, err
	}

	if !found {
		return nil, false, nil
	}

	createdVolumes, err := p.dbVolumeRepository.FindVolumesForContainer(createdContainer)
	if err != nil {
		return nil, false, err
	}

	container, err := newGardenWorkerContainer(
		logger,
		gardenContainer,
		createdContainer,
		createdVolumes,
		p.gardenClient,
		p.volumeClient,
		p.worker.Name(),
	)

	if err != nil {
		logger.Error("failed-to-construct-container", err)
		return nil, false, err
	}

	return container, true, nil
}

func (p *containerProvider) constructGardenWorkerContainer(
	logger lager.Logger,
	createdContainer db.CreatedContainer,
	gardenContainer garden.Container,
) (Container, error) {
	createdVolumes, err := p.dbVolumeRepository.FindVolumesForContainer(createdContainer)
	if err != nil {
		logger.Error("failed-to-find-container-volumes", err)
		return nil, err
	}

	return newGardenWorkerContainer(
		logger,
		gardenContainer,
		createdContainer,
		createdVolumes,
		p.gardenClient,
		p.volumeClient,
		p.worker.Name(),
	)
}

func (p *containerProvider) createGardenContainer(
	logger lager.Logger,
	creatingContainer db.CreatingContainer,
	spec ContainerSpec,
	fetchedImage FetchedImage,
) (garden.Container, error) {
	var volumeMounts []VolumeMount
	var ioVolumeMounts []VolumeMount

	scratchVolume, err := p.volumeClient.FindOrCreateVolumeForContainer(
		logger,
		VolumeSpec{
			Strategy:   baggageclaim.EmptyStrategy{},
			Privileged: fetchedImage.Privileged,
		},
		creatingContainer,
		spec.TeamID,
		"/scratch",
	)
	if err != nil {
		return nil, err
	}

	volumeMounts = append(volumeMounts, VolumeMount{
		Volume:    scratchVolume,
		MountPath: "/scratch",
	})

	hasSpecDirInInputs := anyMountTo(spec.Dir, getDestinationPathsFromInputs(spec.Inputs))
	hasSpecDirInOutputs := anyMountTo(spec.Dir, getDestinationPathsFromOutputs(spec.Outputs))

	if spec.Dir != "" && !hasSpecDirInOutputs && !hasSpecDirInInputs {
		workdirVolume, volumeErr := p.volumeClient.FindOrCreateVolumeForContainer(
			logger,
			VolumeSpec{
				Strategy:   baggageclaim.EmptyStrategy{},
				Privileged: fetchedImage.Privileged,
			},
			creatingContainer,
			spec.TeamID,
			spec.Dir,
		)
		if volumeErr != nil {
			return nil, volumeErr
		}

		volumeMounts = append(volumeMounts, VolumeMount{
			Volume:    workdirVolume,
			MountPath: spec.Dir,
		})
	}

	worker := NewGardenWorker(
		p.gardenClient,
		p,
		p.volumeClient,
		p.imageFactory,
		p.worker,
		0,
	)

	inputDestinationPaths := make(map[string]bool)

	for _, inputSource := range spec.Inputs {
		var inputVolume Volume

		localVolume, found, err := inputSource.Source().VolumeOn(logger, worker)
		if err != nil {
			return nil, err
		}

		cleanedInputPath := filepath.Clean(inputSource.DestinationPath())

		if found {
			inputVolume, err = p.volumeClient.FindOrCreateCOWVolumeForContainer(
				logger,
				VolumeSpec{
					Strategy:   localVolume.COWStrategy(),
					Privileged: fetchedImage.Privileged,
				},
				creatingContainer,
				localVolume,
				spec.TeamID,
				cleanedInputPath,
			)
			if err != nil {
				return nil, err
			}
		} else {
			inputVolume, err = p.volumeClient.FindOrCreateVolumeForContainer(
				logger,
				VolumeSpec{
					Strategy:   baggageclaim.EmptyStrategy{},
					Privileged: fetchedImage.Privileged,
				},
				creatingContainer,
				spec.TeamID,
				cleanedInputPath,
			)
			if err != nil {
				return nil, err
			}

			destData := lager.Data{
				"dest-volume": inputVolume.Handle(),
				"dest-worker": inputVolume.WorkerName(),
			}
			err = inputSource.Source().StreamTo(logger.Session("stream-to", destData), inputVolume)
			if err != nil {
				return nil, err
			}
		}

		ioVolumeMounts = append(ioVolumeMounts, VolumeMount{
			Volume:    inputVolume,
			MountPath: cleanedInputPath,
		})

		inputDestinationPaths[cleanedInputPath] = true
	}

	for _, outputPath := range spec.Outputs {
		cleanedOutputPath := filepath.Clean(outputPath)

		// reuse volume if output path is the same as input
		if inputDestinationPaths[cleanedOutputPath] {
			continue
		}

		outVolume, volumeErr := p.volumeClient.FindOrCreateVolumeForContainer(
			logger,
			VolumeSpec{
				Strategy:   baggageclaim.EmptyStrategy{},
				Privileged: fetchedImage.Privileged,
			},
			creatingContainer,
			spec.TeamID,
			cleanedOutputPath,
		)
		if volumeErr != nil {
			return nil, volumeErr
		}

		ioVolumeMounts = append(ioVolumeMounts, VolumeMount{
			Volume:    outVolume,
			MountPath: cleanedOutputPath,
		})
	}
	bindMounts := []garden.BindMount{}

	for _, mount := range spec.BindMounts {
		bindMount, found, mountErr := mount.VolumeOn(worker)
		if mountErr != nil {
			return nil, mountErr
		}
		if found {
			bindMounts = append(bindMounts, bindMount)
		}
	}

	sort.Sort(byMountPath(ioVolumeMounts))
	volumeMounts = append(volumeMounts, ioVolumeMounts...)

	for _, mount := range volumeMounts {
		bindMounts = append(bindMounts, garden.BindMount{
			SrcPath: mount.Volume.Path(),
			DstPath: mount.MountPath,
			Mode:    garden.BindMountModeRW,
		})
	}

	gardenProperties := garden.Properties{}

	if spec.User != "" {
		gardenProperties[userPropertyName] = spec.User
	} else {
		gardenProperties[userPropertyName] = fetchedImage.Metadata.User
	}

	env := append(fetchedImage.Metadata.Env, spec.Env...)

	if p.httpProxyURL != "" {
		env = append(env, fmt.Sprintf("http_proxy=%s", p.httpProxyURL))
	}

	if p.httpsProxyURL != "" {
		env = append(env, fmt.Sprintf("https_proxy=%s", p.httpsProxyURL))
	}

	if p.noProxy != "" {
		env = append(env, fmt.Sprintf("no_proxy=%s", p.noProxy))
	}

	return p.gardenClient.Create(garden.ContainerSpec{
		Handle:     creatingContainer.Handle(),
		RootFSPath: fetchedImage.URL,
		Privileged: fetchedImage.Privileged,
		BindMounts: bindMounts,
		Limits:     spec.Limits.ToGardenLimits(),
		Env:        env,
		Properties: gardenProperties,
	})
}

func getDestinationPathsFromInputs(inputs []InputSource) []string {
	destinationPaths := make([]string, len(inputs))

	for idx, input := range inputs {
		destinationPaths[idx] = input.DestinationPath()
	}

	return destinationPaths
}

func getDestinationPathsFromOutputs(outputs OutputPaths) []string {
	var (
		idx              = 0
		destinationPaths = make([]string, len(outputs))
	)

	for _, destinationPath := range outputs {
		destinationPaths[idx] = destinationPath
		idx++
	}

	return destinationPaths
}

func anyMountTo(path string, destinationPaths []string) bool {
	for _, destinationPath := range destinationPaths {
		if filepath.Clean(destinationPath) == filepath.Clean(path) {
			return true
		}
	}

	return false
}
<commit_msg>atc/worker/: Add log to check if container lock is used<commit_after>package worker

import (
	"context"
	"fmt"
	"path/filepath"
	"sort"
	"time"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/lager"
	"github.com/concourse/baggageclaim"
	"github.com/concourse/concourse/atc/creds"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/db/lock"
	"github.com/concourse/concourse/atc/metric"
)

const creatingContainerRetryDelay = 1 * time.Second

func NewContainerProvider(
	gardenClient garden.Client,
	volumeClient VolumeClient,
	dbWorker db.Worker,
	imageFactory ImageFactory,
	dbVolumeRepository db.VolumeRepository,
	dbTeamFactory db.TeamFactory,
	lockFactory lock.LockFactory,
) ContainerProvider {

	return &containerProvider{
		gardenClient:       gardenClient,
		volumeClient:       volumeClient,
		imageFactory:       imageFactory,
		dbVolumeRepository: dbVolumeRepository,
		dbTeamFactory:      dbTeamFactory,
		lockFactory:        lockFactory,
		httpProxyURL:       dbWorker.HTTPProxyURL(),
		httpsProxyURL:      dbWorker.HTTPSProxyURL(),
		noProxy:            dbWorker.NoProxy(),
		worker:             dbWorker,
	}
}

//go:generate counterfeiter . ContainerProvider

type ContainerProvider interface {
	FindCreatedContainerByHandle(
		logger lager.Logger,
		handle string,
		teamID int,
	) (Container, bool, error)

	FindOrCreateContainer(
		ctx context.Context,
		logger lager.Logger,
		owner db.ContainerOwner,
		delegate ImageFetchingDelegate,
		metadata db.ContainerMetadata,
		containerSpec ContainerSpec,
		workerSpec WorkerSpec,
		resourceTypes creds.VersionedResourceTypes,
		image Image,
	) (Container, error)
}

// TODO: Remove the ImageFactory from the containerProvider.
// Currently, the imageFactory is only needed to create a garden
// worker in createGardenContainer. Creating a garden worker here
// is cyclical because the garden worker contains a containerProvider.
// There is an ongoing refactor that is attempting to fix this.
type containerProvider struct {
	gardenClient       garden.Client
	volumeClient       VolumeClient
	imageFactory       ImageFactory
	dbVolumeRepository db.VolumeRepository
	dbTeamFactory      db.TeamFactory

	lockFactory lock.LockFactory

	worker        db.Worker
	httpProxyURL  string
	httpsProxyURL string
	noProxy       string
}

// If a created container exists, a garden.Container must also exist
// so this method will find it, create the corresponding worker.Container
// and return it.
// If no created container exists, FindOrCreateContainer will go through
// the container creation flow i.e. find or create a CreatingContainer,
// create the garden.Container and then the CreatedContainer
func (p *containerProvider) FindOrCreateContainer(
	ctx context.Context,
	logger lager.Logger,
	owner db.ContainerOwner,
	delegate ImageFetchingDelegate,
	metadata db.ContainerMetadata,
	containerSpec ContainerSpec,
	workerSpec WorkerSpec,
	resourceTypes creds.VersionedResourceTypes,
	image Image,
) (Container, error) {
	var (
		gardenContainer   garden.Container
		createdContainer  db.CreatedContainer
		creatingContainer db.CreatingContainer
		err               error
	)

	for {
		creatingContainer, createdContainer, err = p.worker.FindContainerOnWorker(
			owner,
		)
		if err != nil {
			logger.Error("failed-to-find-container-in-db", err)
			return nil, err
		}

		if createdContainer != nil {
			logger = logger.WithData(lager.Data{"container": createdContainer.Handle()})

			logger.Debug("found-created-container-in-db")

			gardenContainer, err = p.gardenClient.Lookup(createdContainer.Handle())
			if err != nil {
				logger.Error("failed-to-lookup-created-container-in-garden", err)
				return nil, err
			}

			return p.constructGardenWorkerContainer(
				logger,
				createdContainer,
				gardenContainer,
			)
		}

		if creatingContainer == nil {
			logger.Debug("creating-container-in-db")

			creatingContainer, err = p.worker.CreateContainer(
				owner,
				metadata,
			)
			if err != nil {
				logger.Error("failed-to-create-container-in-db", err)
				return nil, err
			}

			logger = logger.WithData(lager.Data{"container": creatingContainer.Handle()})
			logger.Debug("created-creating-container-in-db")
		} else {
			logger = logger.WithData(lager.Data{"container": creatingContainer.Handle()})
			logger.Debug("found-creating-container-in-db")
		}

		gardenContainer, err = p.gardenClient.Lookup(creatingContainer.Handle())
		if err != nil {
			if _, ok := err.(garden.ContainerNotFoundError); !ok {
				logger.Error("failed-to-lookup-creating-container-in-garden", err)
				return nil, err
			}
		}

		if gardenContainer == nil {
			containerLock, acquired, err := p.lockFactory.Acquire(logger, lock.NewContainerCreatingLockID(creatingContainer.ID()))
			if err != nil {
				logger.Error("failed-to-acquire-container-creating-lock", err)
				return nil, err
			}

			if !acquired {
				time.Sleep(creatingContainerRetryDelay)
				logger.Debug(fmt.Sprintf("did-not-acquire-creating-container-lock-%d", creatingContainer.ID()))
				continue
			}

			defer containerLock.Release()

			logger.Debug("fetching-image")

			fetchedImage, err := image.FetchForContainer(ctx, logger, creatingContainer)
			if err != nil {
				creatingContainer.Failed()
				logger.Error("failed-to-fetch-image-for-container", err)
				return nil, err
			}

			logger.Debug("creating-container-in-garden")

			gardenContainer, err = p.createGardenContainer(
				logger,
				creatingContainer,
				containerSpec,
				fetchedImage,
			)
			if err != nil {
				_, failedErr := creatingContainer.Failed()
				if failedErr != nil {
					logger.Error("failed-to-mark-container-as-failed", err)
				}
				metric.FailedContainers.Inc()

				logger.Error("failed-to-create-container-in-garden", err)
				return nil, err
			}

			metric.ContainersCreated.Inc()

			logger.Debug("created-container-in-garden")
		} else {
			logger.Debug("found-created-container-in-garden")
		}

		createdContainer, err = creatingContainer.Created()
		if err != nil {
			logger.Error("failed-to-mark-container-as-created", err)

			_ = p.gardenClient.Destroy(creatingContainer.Handle())

			return nil, err
		}

		logger.Debug("created-container-in-db")

		return p.constructGardenWorkerContainer(
			logger,
			createdContainer,
			gardenContainer,
		)
	}
}

func (p *containerProvider) FindCreatedContainerByHandle(
	logger lager.Logger,
	handle string,
	teamID int,
) (Container, bool, error) {
	gardenContainer, err := p.gardenClient.Lookup(handle)
	if err != nil {
		if _, ok := err.(garden.ContainerNotFoundError); ok {
			logger.Info("container-not-found")
			return nil, false, nil
		}

		logger.Error("failed-to-lookup-on-garden", err)
		return nil, false, err
	}

	createdContainer, found, err := p.dbTeamFactory.GetByID(teamID).FindCreatedContainerByHandle(handle)
	if err != nil {
		logger.Error("failed-to-lookup-in-db", err)
		return nil, false, err
	}

	if !found {
		return nil, false, nil
	}

	createdVolumes, err := p.dbVolumeRepository.FindVolumesForContainer(createdContainer)
	if err != nil {
		return nil, false, err
	}

	container, err := newGardenWorkerContainer(
		logger,
		gardenContainer,
		createdContainer,
		createdVolumes,
		p.gardenClient,
		p.volumeClient,
		p.worker.Name(),
	)

	if err != nil {
		logger.Error("failed-to-construct-container", err)
		return nil, false, err
	}

	return container, true, nil
}

func (p *containerProvider) constructGardenWorkerContainer(
	logger lager.Logger,
	createdContainer db.CreatedContainer,
	gardenContainer garden.Container,
) (Container, error) {
	createdVolumes, err := p.dbVolumeRepository.FindVolumesForContainer(createdContainer)
	if err != nil {
		logger.Error("failed-to-find-container-volumes", err)
		return nil, err
	}

	return newGardenWorkerContainer(
		logger,
		gardenContainer,
		createdContainer,
		createdVolumes,
		p.gardenClient,
		p.volumeClient,
		p.worker.Name(),
	)
}

func (p *containerProvider) createGardenContainer(
	logger lager.Logger,
	creatingContainer db.CreatingContainer,
	spec ContainerSpec,
	fetchedImage FetchedImage,
) (garden.Container, error) {
	var volumeMounts []VolumeMount
	var ioVolumeMounts []VolumeMount

	scratchVolume, err := p.volumeClient.FindOrCreateVolumeForContainer(
		logger,
		VolumeSpec{
			Strategy:   baggageclaim.EmptyStrategy{},
			Privileged: fetchedImage.Privileged,
		},
		creatingContainer,
		spec.TeamID,
		"/scratch",
	)
	if err != nil {
		return nil, err
	}

	volumeMounts = append(volumeMounts, VolumeMount{
		Volume:    scratchVolume,
		MountPath: "/scratch",
	})

	hasSpecDirInInputs := anyMountTo(spec.Dir, getDestinationPathsFromInputs(spec.Inputs))
	hasSpecDirInOutputs := anyMountTo(spec.Dir, getDestinationPathsFromOutputs(spec.Outputs))

	if spec.Dir != "" && !hasSpecDirInOutputs && !hasSpecDirInInputs {
		workdirVolume, volumeErr := p.volumeClient.FindOrCreateVolumeForContainer(
			logger,
			VolumeSpec{
				Strategy:   baggageclaim.EmptyStrategy{},
				Privileged: fetchedImage.Privileged,
			},
			creatingContainer,
			spec.TeamID,
			spec.Dir,
		)
		if volumeErr != nil {
			return nil, volumeErr
		}

		volumeMounts = append(volumeMounts, VolumeMount{
			Volume:    workdirVolume,
			MountPath: spec.Dir,
		})
	}

	worker := NewGardenWorker(
		p.gardenClient,
		p,
		p.volumeClient,
		p.imageFactory,
		p.worker,
		0,
	)

	inputDestinationPaths := make(map[string]bool)

	for _, inputSource := range spec.Inputs {
		var inputVolume Volume

		localVolume, found, err := inputSource.Source().VolumeOn(logger, worker)
		if err != nil {
			return nil, err
		}

		cleanedInputPath := filepath.Clean(inputSource.DestinationPath())

		if found {
			inputVolume, err = p.volumeClient.FindOrCreateCOWVolumeForContainer(
				logger,
				VolumeSpec{
					Strategy:   localVolume.COWStrategy(),
					Privileged: fetchedImage.Privileged,
				},
				creatingContainer,
				localVolume,
				spec.TeamID,
				cleanedInputPath,
			)
			if err != nil {
				return nil, err
			}
		} else {
			inputVolume, err = p.volumeClient.FindOrCreateVolumeForContainer(
				logger,
				VolumeSpec{
					Strategy:   baggageclaim.EmptyStrategy{},
					Privileged: fetchedImage.Privileged,
				},
				creatingContainer,
				spec.TeamID,
				cleanedInputPath,
			)
			if err != nil {
				return nil, err
			}

			destData := lager.Data{
				"dest-volume": inputVolume.Handle(),
				"dest-worker": inputVolume.WorkerName(),
			}
			err = inputSource.Source().StreamTo(logger.Session("stream-to", destData), inputVolume)
			if err != nil {
				return nil, err
			}
		}

		ioVolumeMounts = append(ioVolumeMounts, VolumeMount{
			Volume:    inputVolume,
			MountPath: cleanedInputPath,
		})

		inputDestinationPaths[cleanedInputPath] = true
	}

	for _, outputPath := range spec.Outputs {
		cleanedOutputPath := filepath.Clean(outputPath)

		// reuse volume if output path is the same as input
		if inputDestinationPaths[cleanedOutputPath] {
			continue
		}

		outVolume, volumeErr := p.volumeClient.FindOrCreateVolumeForContainer(
			logger,
			VolumeSpec{
				Strategy:   baggageclaim.EmptyStrategy{},
				Privileged: fetchedImage.Privileged,
			},
			creatingContainer,
			spec.TeamID,
			cleanedOutputPath,
		)
		if volumeErr != nil {
			return nil, volumeErr
		}

		ioVolumeMounts = append(ioVolumeMounts, VolumeMount{
			Volume:    outVolume,
			MountPath: cleanedOutputPath,
		})
	}
	bindMounts := []garden.BindMount{}

	for _, mount := range spec.BindMounts {
		bindMount, found, mountErr := mount.VolumeOn(worker)
		if mountErr != nil {
			return nil, mountErr
		}
		if found {
			bindMounts = append(bindMounts, bindMount)
		}
	}

	sort.Sort(byMountPath(ioVolumeMounts))
	volumeMounts = append(volumeMounts, ioVolumeMounts...)

	for _, mount := range volumeMounts {
		bindMounts = append(bindMounts, garden.BindMount{
			SrcPath: mount.Volume.Path(),
			DstPath: mount.MountPath,
			Mode:    garden.BindMountModeRW,
		})
	}

	gardenProperties := garden.Properties{}

	if spec.User != "" {
		gardenProperties[userPropertyName] = spec.User
	} else {
		gardenProperties[userPropertyName] = fetchedImage.Metadata.User
	}

	env := append(fetchedImage.Metadata.Env, spec.Env...)

	if p.httpProxyURL != "" {
		env = append(env, fmt.Sprintf("http_proxy=%s", p.httpProxyURL))
	}

	if p.httpsProxyURL != "" {
		env = append(env, fmt.Sprintf("https_proxy=%s", p.httpsProxyURL))
	}

	if p.noProxy != "" {
		env = append(env, fmt.Sprintf("no_proxy=%s", p.noProxy))
	}

	return p.gardenClient.Create(garden.ContainerSpec{
		Handle:     creatingContainer.Handle(),
		RootFSPath: fetchedImage.URL,
		Privileged: fetchedImage.Privileged,
		BindMounts: bindMounts,
		Limits:     spec.Limits.ToGardenLimits(),
		Env:        env,
		Properties: gardenProperties,
	})
}

func getDestinationPathsFromInputs(inputs []InputSource) []string {
	destinationPaths := make([]string, len(inputs))

	for idx, input := range inputs {
		destinationPaths[idx] = input.DestinationPath()
	}

	return destinationPaths
}

func getDestinationPathsFromOutputs(outputs OutputPaths) []string {
	var (
		idx              = 0
		destinationPaths = make([]string, len(outputs))
	)

	for _, destinationPath := range outputs {
		destinationPaths[idx] = destinationPath
		idx++
	}

	return destinationPaths
}

func anyMountTo(path string, destinationPaths []string) bool {
	for _, destinationPath := range destinationPaths {
		if filepath.Clean(destinationPath) == filepath.Clean(path) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package db

import (
	"errors"
	"time"
)

// Proposal represents the db schema of a proposal
type Proposal struct {
	ID           int64
	UserID       int64
	Title        string
	Description  string
	Activities   string
	Contact      string
	Recipient    string
	Recipient2   string
	Value        uint64
	Starts       time.Time
	FinishedDate time.Time
	Votes        uint64
	Moderated    bool
	StartTrigger bool
}

// LoadProposalByID loads a proposal by ID from the database
func (context *PollyContext) LoadProposalByID(id int64) (Proposal, error) {
	proposal := Proposal{}
	if id < 1 {
		return proposal, ErrInvalidID
	}

	err := context.QueryRow("SELECT id, userid, title, description, activities, contact, recipient, recipient2, value, starts, votes, moderated, started, finisheddate FROM proposals WHERE id = $1", id).Scan(&proposal.ID, &proposal.UserID, &proposal.Title, &proposal.Description, &proposal.Activities, &proposal.Contact, &proposal.Recipient, &proposal.Recipient2, &proposal.Value, &proposal.Starts, &proposal.Votes, &proposal.Moderated, &proposal.StartTrigger, &proposal.FinishedDate)
	return proposal, err
}

// GetProposalByID returns a proposal by ID from the cache
func (context *PollyContext) GetProposalByID(id int64) (Proposal, error) {
	proposal := Proposal{}
	proposalCache, err := proposalsCache.Value(id, context)
	if err != nil {
		return proposal, err
	}

	proposal = *proposalCache.Data().(*Proposal)
	return proposal, nil
}

// LoadAllProposals loads all proposals from the database
func (context *PollyContext) LoadAllProposals() ([]Proposal, error) {
	proposals := []Proposal{}

	rows, err := context.Query("SELECT id, userid, title, description, activities, contact, recipient, recipient2, value, starts, votes, moderated, started, finisheddate FROM proposals ORDER BY starts ASC")
	if err != nil {
		return proposals, err
	}

	defer rows.Close()
	for rows.Next() {
		proposal := Proposal{}
		err = rows.Scan(&proposal.ID, &proposal.UserID, &proposal.Title, &proposal.Description, &proposal.Activities, &proposal.Contact, &proposal.Recipient, &proposal.Recipient2, &proposal.Value, &proposal.Starts, &proposal.Votes, &proposal.Moderated, &proposal.StartTrigger, &proposal.FinishedDate)
		if err != nil {
			return proposals, err
		}

		proposals = append(proposals, proposal)
	}

	return proposals, err
}

// Update a proposal in the database
func (proposal *Proposal) Update(context *PollyContext) error {
	_, err := context.Exec("UPDATE proposals SET title = $1, description = $2, activities = $3, contact = $4, recipient = $5, recipient2 = $6, value = $7, starts = $8, moderated = $9, started = $10, finisheddate = $11 WHERE id = $12", proposal.Title, proposal.Description, proposal.Activities, proposal.Contact, proposal.Recipient, proposal.Recipient2, proposal.Value, proposal.Starts, proposal.Moderated, proposal.StartTrigger, proposal.FinishedDate, proposal.ID)
	if err != nil {
		panic(err)
	}

	proposalsCache.Delete(proposal.ID)
	return err
}

// Save a proposal to the database
func (proposal *Proposal) Save(context *PollyContext) error {
	if proposal.Value > uint64(context.Config.App.Proposals.MaxGrantValue) {
		return errors.New("Grant value is too high")
	}

	if proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) {
		if proposal.Value > uint64(context.SmallGrantMaxValue(uint(proposal.Ends(context).Month()))) {
			return errors.New("Proposal value is too high for this polling period")
		}

		if proposal.Starts.Before(time.Now()) {
			return errors.New("Invalid start date")
		}
	} else {
		largeGrantStartMonth := ((int(proposal.Starts.Month()) + int(context.Config.App.Proposals.StartMonth)) % int(context.Config.App.Proposals.GrantIntervalMonths)) + int(proposal.Starts.Month())
		startDate := time.Date(proposal.Starts.Year(), time.Month(largeGrantStartMonth), 1, 0, 0, 0, 0, time.UTC).AddDate(0, 1, -1)
		proposal.Starts = time.Date(startDate.Year(), startDate.Month(), startDate.Day(), 12, 0, 0, 0, time.UTC)

		if proposal.Starts.Before(time.Now()) {
			return errors.New("Invalid start date")
		}
	}

	err := context.QueryRow("INSERT INTO proposals (userid, title, description, activities, contact, recipient, recipient2, value, starts, finisheddate) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id", proposal.UserID, proposal.Title, proposal.Description, proposal.Activities, proposal.Contact, proposal.Recipient, proposal.Recipient2, proposal.Value, proposal.Starts, proposal.FinishedDate).Scan(&proposal.ID)
	proposalsCache.Delete(proposal.ID)
	return err
}

// Started returns true if a proposal has started
func (proposal *Proposal) Started(context *PollyContext) bool {
	return proposal.Starts.Before(time.Now())
}

// Ends returns when this proposal ends
func (proposal *Proposal) Ends(context *PollyContext) time.Time {
	return proposal.Starts.AddDate(0, 0, int(context.Config.App.Proposals.SmallGrantVoteRuntimeDays))
}

// Ended returns true if a proposal either ended or got rejected by votes
func (proposal *Proposal) Ended(context *PollyContext) bool {
	return proposal.Ends(context).Before(time.Now()) ||
		(proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) &&
			proposal.Votes >= uint64(context.Config.App.Proposals.SmallGrantVoteThreshold))
}

// Accepted returns true if a proposal has finished and was accepted by poll
func (proposal *Proposal) Accepted(context *PollyContext) bool {
	return proposal.Ended(context) &&
		(proposal.Value >= uint64(context.Config.App.Proposals.SmallGrantValueThreshold) ||
			(proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) &&
				proposal.Votes < uint64(context.Config.App.Proposals.SmallGrantVoteThreshold)))
}

// Vote marks a vote for a proposal
func (proposal *Proposal) Vote(context *PollyContext, user User) (Vote, error) {
	vote := Vote{
		UserID:     user.ID,
		ProposalID: proposal.ID,
		Vote:       true,
	}
	err := vote.Save(context)
	if err != nil {
		return Vote{}, err
	}

	err = context.QueryRow("UPDATE proposals SET votes=votes+1 WHERE id = $1 RETURNING votes", proposal.ID).Scan(&proposal.Votes)
	proposalsCache.Delete(proposal.ID)
	return vote, err
}
<commit_msg>Keep proposals running regardless of their rejection state<commit_after>package db

import (
	"errors"
	"time"
)

// Proposal represents the db schema of a proposal
type Proposal struct {
	ID           int64
	UserID       int64
	Title        string
	Description  string
	Activities   string
	Contact      string
	Recipient    string
	Recipient2   string
	Value        uint64
	Starts       time.Time
	FinishedDate time.Time
	Votes        uint64
	Moderated    bool
	StartTrigger bool
}

// LoadProposalByID loads a proposal by ID from the database
func (context *PollyContext) LoadProposalByID(id int64) (Proposal, error) {
	proposal := Proposal{}
	if id < 1 {
		return proposal, ErrInvalidID
	}

	err := context.QueryRow("SELECT id, userid, title, description, activities, contact, recipient, recipient2, value, starts, votes, moderated, started, finisheddate FROM proposals WHERE id = $1", id).Scan(&proposal.ID, &proposal.UserID, &proposal.Title, &proposal.Description, &proposal.Activities, &proposal.Contact, &proposal.Recipient, &proposal.Recipient2, &proposal.Value, &proposal.Starts, &proposal.Votes, &proposal.Moderated, &proposal.StartTrigger, &proposal.FinishedDate)
	return proposal, err
}

// GetProposalByID returns a proposal by ID from the cache
func (context *PollyContext) GetProposalByID(id int64) (Proposal, error) {
	proposal := Proposal{}
	proposalCache, err := proposalsCache.Value(id, context)
	if err != nil {
		return proposal, err
	}

	proposal = *proposalCache.Data().(*Proposal)
	return proposal, nil
}

// LoadAllProposals loads all proposals from the database
func (context *PollyContext) LoadAllProposals() ([]Proposal, error) {
	proposals := []Proposal{}

	rows, err := context.Query("SELECT id, userid, title, description, activities, contact, recipient, recipient2, value, starts, votes, moderated, started, finisheddate FROM proposals ORDER BY starts ASC")
	if err != nil {
		return proposals, err
	}

	defer rows.Close()
	for rows.Next() {
		proposal := Proposal{}
		err = rows.Scan(&proposal.ID, &proposal.UserID, &proposal.Title, &proposal.Description, &proposal.Activities, &proposal.Contact, &proposal.Recipient, &proposal.Recipient2, &proposal.Value, &proposal.Starts, &proposal.Votes, &proposal.Moderated, &proposal.StartTrigger, &proposal.FinishedDate)
		if err != nil {
			return proposals, err
		}

		proposals = append(proposals, proposal)
	}

	return proposals, err
}

// Update a proposal in the database
func (proposal *Proposal) Update(context *PollyContext) error {
	_, err := context.Exec("UPDATE proposals SET title = $1, description = $2, activities = $3, contact = $4, recipient = $5, recipient2 = $6, value = $7, starts = $8, moderated = $9, started = $10, finisheddate = $11 WHERE id = $12", proposal.Title, proposal.Description, proposal.Activities, proposal.Contact, proposal.Recipient, proposal.Recipient2, proposal.Value, proposal.Starts, proposal.Moderated, proposal.StartTrigger, proposal.FinishedDate, proposal.ID)
	if err != nil {
		panic(err)
	}

	proposalsCache.Delete(proposal.ID)
	return err
}

// Save a proposal to the database
func (proposal *Proposal) Save(context *PollyContext) error {
	if proposal.Value > uint64(context.Config.App.Proposals.MaxGrantValue) {
		return errors.New("Grant value is too high")
	}

	if proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) {
		if proposal.Value > uint64(context.SmallGrantMaxValue(uint(proposal.Ends(context).Month()))) {
			return errors.New("Proposal value is too high for this polling period")
		}

		if proposal.Starts.Before(time.Now()) {
			return errors.New("Invalid start date")
		}
	} else {
		largeGrantStartMonth := ((int(proposal.Starts.Month()) + int(context.Config.App.Proposals.StartMonth)) % int(context.Config.App.Proposals.GrantIntervalMonths)) + int(proposal.Starts.Month())
		startDate := time.Date(proposal.Starts.Year(), time.Month(largeGrantStartMonth), 1, 0, 0, 0, 0, time.UTC).AddDate(0, 1, -1)
		proposal.Starts = time.Date(startDate.Year(), startDate.Month(), startDate.Day(), 12, 0, 0, 0, time.UTC)

		if proposal.Starts.Before(time.Now()) {
			return errors.New("Invalid start date")
		}
	}

	err := context.QueryRow("INSERT INTO proposals (userid, title, description, activities, contact, recipient, recipient2, value, starts, finisheddate) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id", proposal.UserID, proposal.Title, proposal.Description, proposal.Activities, proposal.Contact, proposal.Recipient, proposal.Recipient2, proposal.Value, proposal.Starts, proposal.FinishedDate).Scan(&proposal.ID)
	proposalsCache.Delete(proposal.ID)
	return err
}

// Started returns true if a proposal has started
func (proposal *Proposal) Started(context *PollyContext) bool {
	return proposal.Starts.Before(time.Now())
}

// Ends returns when this proposal ends
func (proposal *Proposal) Ends(context *PollyContext) time.Time {
	return proposal.Starts.AddDate(0, 0, int(context.Config.App.Proposals.SmallGrantVoteRuntimeDays))
}

// Ended returns true if a proposal ended
func (proposal *Proposal) Ended(context *PollyContext) bool {
	return proposal.Ends(context).Before(time.Now())
}

// Accepted returns true if a proposal has finished and was accepted by poll
func (proposal *Proposal) Accepted(context *PollyContext) bool {
	return proposal.Ended(context) &&
		(proposal.Value >= uint64(context.Config.App.Proposals.SmallGrantValueThreshold) ||
			(proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) &&
				proposal.Votes < uint64(context.Config.App.Proposals.SmallGrantVoteThreshold)))
}

// Vote marks a vote for a proposal
func (proposal *Proposal) Vote(context *PollyContext, user User) (Vote, error) {
	vote := Vote{
		UserID:     user.ID,
		ProposalID: proposal.ID,
		Vote:       true,
	}
	err := vote.Save(context)
	if err != nil {
		return Vote{}, err
	}

	err = context.QueryRow("UPDATE proposals SET votes=votes+1 WHERE id = $1 RETURNING votes", proposal.ID).Scan(&proposal.Votes)
	proposalsCache.Delete(proposal.ID)
	return vote, err
}
<|endoftext|>


<commit_before>// Copyright 2015 realglobe, Inc.
//
// 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 proxy

import (
	"bufio"
	"bytes"
	"github.com/realglobe-Inc/go-lib/erro"
	"io"
	"io/ioutil"
	"os"
)

// ボディを読みながら保存しとく機構
type buffer struct {
	base io.ReadCloser

	last bool

	memMax int
	memW   *bytes.Buffer
	memR   *bytes.Reader

	filePref string
	file     *os.File
	fileW    *bufio.Writer
	fileR    *bufio.Reader
}

func newBuffer(base io.ReadCloser, memMax int, filePref string) *buffer {
	return &buffer{
		base:     base,
		memMax:   memMax,
		memW:     &bytes.Buffer{},
		filePref: filePref,
	}
}

func (this *buffer) Read(p []byte) (n int, err error) {
	n = 0
	if this.memR != nil {
		m, err := io.ReadFull(this.memR, p[n:])
		switch err {
		case nil:
			// バッファが埋まった。
			return n + m, nil
		case io.EOF, io.ErrUnexpectedEOF:
			// メモリを読み切った。
			n += m
			this.memR = nil
		default:
			// 読み込みエラー。
			return 0, erro.Wrap(err)
		}
	}
	if this.fileR != nil {
		m, err := io.ReadFull(this.fileR, p[n:])
		switch err {
		case nil:
			// バッファが埋まった。
			return n + m, nil
		case io.EOF, io.ErrUnexpectedEOF:
			// ファイルを読み切った。
			n += m
			this.fileR = nil
		default:
			// 読み込みエラー。
			return 0, erro.Wrap(err)
		}
	}
	if this.base != nil {
		m, err := io.ReadFull(this.base, p[n:])
		switch err {
		case nil:
			// バッファが埋まった。
			if err := this.save(p[n : n+m]); err != nil {
				return 0, erro.Wrap(err)
			}
			return n + m, nil
		case io.EOF, io.ErrUnexpectedEOF:
			// ボディを読み切った。
			if err := this.save(p[n : n+m]); err != nil {
				return 0, erro.Wrap(err)
			}
			n += m
			this.base.Close()
			this.base = nil
		default:
			// 読み込みエラー。
			return 0, erro.Wrap(err)
		}
	}

	return n, io.EOF
}

// 貯める。
func (this *buffer) save(data []byte) (err error) {
	if this.fileW == nil {
		// メモリに貯められるだけ貯める。
		if remSize := this.memMax - this.memW.Len(); remSize > 0 {
			toSave := data
			if len(toSave) > remSize {
				toSave = data[:remSize]
			}
			saveLen, _ := this.memW.Write(toSave)
			data = data[saveLen:]
		}
		if len(data) == 0 {
			return nil
		}

		if this.file == nil {
			this.file, err = ioutil.TempFile("", this.filePref)
			if err != nil {
				return erro.Wrap(err)
			}
		}
		this.fileW = bufio.NewWriter(this.file)
	}

	// ファイルに貯める。
	if _, err := this.fileW.Write(data); err != nil {
		return erro.Wrap(err)
	}
	return nil
}

func (this *buffer) Close() error {
	return nil
}

// 最後の 1 回。
func (this *buffer) lastRollback() error {
	this.setLast()
	return this.rollback()
}

// また頭から読めるようにする。
func (this *buffer) rollback() error {
	if this.last {
		return erro.New("last stage")
	}
	if this.file != nil {
		if this.fileW != nil {
			if err := this.fileW.Flush(); err != nil {
				return erro.Wrap(err)
			}
			this.fileW = nil
		}
		if _, err := this.file.Seek(0, 0); err != nil {
			return erro.Wrap(err)
		}
		this.fileR = bufio.NewReader(this.file)
	}
	if this.memW != nil {
		this.memR = bytes.NewReader(this.memW.Bytes())
	}
	return nil
}

// もう貯めないようにする。
func (this *buffer) setLast() {
	this.last = true
}

// 廃棄する。
func (this *buffer) dispose() {
	if this.file != nil {
		this.file.Close()
		os.Remove(this.file.Name())
	}
	if this.base != nil {
		this.base.Close()
	}
}
<commit_msg>リクエストボディ用バッファをロールバックできていないところがあったので修正<commit_after>// Copyright 2015 realglobe, Inc.
//
// 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 proxy

import (
	"bufio"
	"bytes"
	"github.com/realglobe-Inc/go-lib/erro"
	"io"
	"io/ioutil"
	"os"
)

// ボディを読みながら保存しとく機構
type buffer struct {
	base io.ReadCloser

	last bool

	memMax int
	memW   *bytes.Buffer
	memR   *bytes.Reader

	filePref string
	file     *os.File
	fileW    *bufio.Writer
	fileR    *bufio.Reader
}

func newBuffer(base io.ReadCloser, memMax int, filePref string) *buffer {
	return &buffer{
		base:     base,
		memMax:   memMax,
		memW:     &bytes.Buffer{},
		filePref: filePref,
	}
}

func (this *buffer) Read(p []byte) (n int, err error) {
	n = 0
	if this.memR != nil {
		m, err := io.ReadFull(this.memR, p[n:])
		switch err {
		case nil:
			// バッファが埋まった。
			return n + m, nil
		case io.EOF, io.ErrUnexpectedEOF:
			// メモリを読み切った。
			n += m
			this.memR = nil
		default:
			// 読み込みエラー。
			return 0, erro.Wrap(err)
		}
	}
	if this.fileR != nil {
		m, err := io.ReadFull(this.fileR, p[n:])
		switch err {
		case nil:
			// バッファが埋まった。
			return n + m, nil
		case io.EOF, io.ErrUnexpectedEOF:
			// ファイルを読み切った。
			n += m
			this.fileR = nil
		default:
			// 読み込みエラー。
			return 0, erro.Wrap(err)
		}
	}
	if this.base != nil {
		m, err := io.ReadFull(this.base, p[n:])
		switch err {
		case nil:
			// バッファが埋まった。
			if err := this.save(p[n : n+m]); err != nil {
				return 0, erro.Wrap(err)
			}
			return n + m, nil
		case io.EOF, io.ErrUnexpectedEOF:
			// ボディを読み切った。
			if err := this.save(p[n : n+m]); err != nil {
				return 0, erro.Wrap(err)
			}
			n += m
			this.base.Close()
			this.base = nil
		default:
			// 読み込みエラー。
			return 0, erro.Wrap(err)
		}
	}

	return n, io.EOF
}

// 貯める。
func (this *buffer) save(data []byte) (err error) {
	if this.fileW == nil {
		// メモリに貯められるだけ貯める。
		if remSize := this.memMax - this.memW.Len(); remSize > 0 {
			toSave := data
			if len(toSave) > remSize {
				toSave = data[:remSize]
			}
			saveLen, _ := this.memW.Write(toSave)
			data = data[saveLen:]
		}
		if len(data) == 0 {
			return nil
		}

		if this.file == nil {
			this.file, err = ioutil.TempFile("", this.filePref)
			if err != nil {
				return erro.Wrap(err)
			}
		}
		this.fileW = bufio.NewWriter(this.file)
	}

	// ファイルに貯める。
	if _, err := this.fileW.Write(data); err != nil {
		return erro.Wrap(err)
	}
	return nil
}

func (this *buffer) Close() error {
	return nil
}

// 最後の 1 回。
func (this *buffer) lastRollback() error {
	if err := this.rollback(); err != nil {
		return erro.Wrap(err)
	}
	this.setLast()
	return nil
}

// また頭から読めるようにする。
func (this *buffer) rollback() error {
	if this.last {
		return erro.New("last stage")
	}
	if this.file != nil {
		if this.fileW != nil {
			if err := this.fileW.Flush(); err != nil {
				return erro.Wrap(err)
			}
			this.fileW = nil
		}
		if _, err := this.file.Seek(0, 0); err != nil {
			return erro.Wrap(err)
		}
		this.fileR = bufio.NewReader(this.file)
	}
	if this.memW != nil {
		this.memR = bytes.NewReader(this.memW.Bytes())
	}
	return nil
}

// もう貯めないようにする。
func (this *buffer) setLast() {
	this.last = true
}

// 廃棄する。
func (this *buffer) dispose() {
	if this.file != nil {
		this.file.Close()
		os.Remove(this.file.Name())
	}
	if this.base != nil {
		this.base.Close()
	}
}
<|endoftext|>


<commit_before>package user_printer

import (
	"fmt"

	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/plugin/models"
)

type UserPrinter interface {
	PrintUsers(org models.Organization, space models.Space, username string)
}

type SpaceUsersPluginPrinter struct {
	UserPrinter
	UsersMap    map[string]plugin_models.GetSpaceUsers_Model
	UserLister  func(spaceGuid string, role string) ([]models.UserFields, error)
	Roles       []string
	PluginModel *[]plugin_models.GetSpaceUsers_Model
}

type SpaceUsersUiPrinter struct {
	UserPrinter
	Ui               terminal.UI
	UserLister       func(spaceGuid string, role string) ([]models.UserFields, error)
	Roles            []string
	RoleDisplayNames map[string]string
}

type OrgUsersPluginPrinter struct {
	UserPrinter
	UsersMap    map[string]plugin_models.GetOrgUsers_Model
	Roles       []string
	UserLister  func(orgGuid string, role string) ([]models.UserFields, error)
	PluginModel *[]plugin_models.GetOrgUsers_Model
}

type OrgUsersUiPrinter struct {
	UserPrinter
	Roles            []string
	RoleDisplayNames map[string]string
	UserLister       func(orgGuid string, role string) ([]models.UserFields, error)
	Ui               terminal.UI
}

func (p *OrgUsersPluginPrinter) PrintUsers(org models.Organization, _ models.Space, username string) {
	for _, role := range p.Roles {
		users, _ := p.UserLister(org.Guid, role)
		for _, user := range users {
			u, found := p.UsersMap[user.Username]
			if found {
				u.Roles = append(u.Roles, role)
				p.UsersMap[user.Username] = u
			} else {
				u = plugin_models.GetOrgUsers_Model{}
				u.Username = user.Username
				u.Guid = user.Guid
				u.IsAdmin = user.IsAdmin
				u.Roles = make([]string, 1)
				u.Roles[0] = role
				p.UsersMap[user.Username] = u
			}
		}
	}
	for _, v := range p.UsersMap {
		*(p.PluginModel) = append(*(p.PluginModel), v)
	}
}

func (p *OrgUsersUiPrinter) PrintUsers(org models.Organization, _ models.Space, username string) {
	for _, role := range p.Roles {
		displayName := p.RoleDisplayNames[role]
		users, apiErr := p.UserLister(org.Guid, role)

		p.Ui.Say("")
		p.Ui.Say("%s", terminal.HeaderColor(displayName))

		if len(users) == 0 {
			p.Ui.Say(fmt.Sprintf("  "+T("No %s found"), displayName))
			continue
		}

		for _, user := range users {
			p.Ui.Say("  %s", user.Username)
		}

		if apiErr != nil {
			p.Ui.Failed(T("Failed fetching org-users for role {{.OrgRoleToDisplayName}}.\n{{.Error}}",
				map[string]interface{}{
					"Error":                apiErr.Error(),
					"OrgRoleToDisplayName": displayName,
				}))
			return
		}
	}
}

func (p *SpaceUsersPluginPrinter) PrintUsers(_ models.Organization, space models.Space, _ string) {
	for _, role := range p.Roles {
		users, _ := p.UserLister(space.Guid, role)
		for _, user := range users {
			u, found := p.UsersMap[user.Username]
			if found {
				u.Roles = append(u.Roles, role)
			} else {
				u = plugin_models.GetSpaceUsers_Model{}
				u.Username = user.Username
				u.Guid = user.Guid
				u.IsAdmin = user.IsAdmin
				u.Roles = make([]string, 1)
				u.Roles[0] = role
			}
			p.UsersMap[user.Username] = u
		}
	}
	for _, v := range p.UsersMap {
		*(p.PluginModel) = append(*(p.PluginModel), v)
	}
}

func (p *SpaceUsersUiPrinter) PrintUsers(org models.Organization, space models.Space, username string) {
	p.Ui.Say(T("Getting users in org {{.TargetOrg}} / space {{.TargetSpace}} as {{.CurrentUser}}",
		map[string]interface{}{
			"TargetOrg":   terminal.EntityNameColor(org.Name),
			"TargetSpace": terminal.EntityNameColor(space.Name),
			"CurrentUser": terminal.EntityNameColor(username),
		}))

	for _, role := range p.Roles {
		displayName := p.RoleDisplayNames[role]
		users, err := p.UserLister(space.Guid, role)
		if err != nil {
			p.Ui.Failed(T("Failed fetching space-users for role {{.SpaceRoleToDisplayName}}.\n{{.Error}}",
				map[string]interface{}{
					"Error":                  err.Error(),
					"SpaceRoleToDisplayName": displayName,
				}))
			return
		}
		p.Ui.Say("")
		p.Ui.Say("%s", terminal.HeaderColor(displayName))

		if len(users) == 0 {
			p.Ui.Say(fmt.Sprintf("  "+T("No %s found"), displayName))
		} else {
			for _, user := range users {
				p.Ui.Say("  %s", user.Username)
			}
		}
	}
}
<commit_msg>Make plugin PrintUsers almost identical<commit_after>package user_printer

import (
	"fmt"

	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/plugin/models"
)

type UserPrinter interface {
	PrintUsers(org models.Organization, space models.Space, username string)
}

type SpaceUsersPluginPrinter struct {
	UserPrinter
	UsersMap    map[string]plugin_models.GetSpaceUsers_Model
	UserLister  func(spaceGuid string, role string) ([]models.UserFields, error)
	Roles       []string
	PluginModel *[]plugin_models.GetSpaceUsers_Model
}

type SpaceUsersUiPrinter struct {
	UserPrinter
	Ui               terminal.UI
	UserLister       func(spaceGuid string, role string) ([]models.UserFields, error)
	Roles            []string
	RoleDisplayNames map[string]string
}

type OrgUsersPluginPrinter struct {
	UserPrinter
	UsersMap    map[string]plugin_models.GetOrgUsers_Model
	Roles       []string
	UserLister  func(orgGuid string, role string) ([]models.UserFields, error)
	PluginModel *[]plugin_models.GetOrgUsers_Model
}

type OrgUsersUiPrinter struct {
	UserPrinter
	Roles            []string
	RoleDisplayNames map[string]string
	UserLister       func(orgGuid string, role string) ([]models.UserFields, error)
	Ui               terminal.UI
}

func (p *OrgUsersPluginPrinter) PrintUsers(org models.Organization, _ models.Space, username string) {
	for _, role := range p.Roles {
		users, _ := p.UserLister(org.Guid, role)
		for _, user := range users {
			u, found := p.UsersMap[user.Username]
			if found {
				u.Roles = append(u.Roles, role)
			} else {
				u = plugin_models.GetOrgUsers_Model{}
				u.Username = user.Username
				u.Guid = user.Guid
				u.IsAdmin = user.IsAdmin
				u.Roles = make([]string, 1)
				u.Roles[0] = role
			}
			p.UsersMap[user.Username] = u
		}
	}
	for _, v := range p.UsersMap {
		*(p.PluginModel) = append(*(p.PluginModel), v)
	}
}

func (p *OrgUsersUiPrinter) PrintUsers(org models.Organization, _ models.Space, username string) {
	for _, role := range p.Roles {
		displayName := p.RoleDisplayNames[role]
		users, apiErr := p.UserLister(org.Guid, role)

		p.Ui.Say("")
		p.Ui.Say("%s", terminal.HeaderColor(displayName))

		if len(users) == 0 {
			p.Ui.Say(fmt.Sprintf("  "+T("No %s found"), displayName))
			continue
		}

		for _, user := range users {
			p.Ui.Say("  %s", user.Username)
		}

		if apiErr != nil {
			p.Ui.Failed(T("Failed fetching org-users for role {{.OrgRoleToDisplayName}}.\n{{.Error}}",
				map[string]interface{}{
					"Error":                apiErr.Error(),
					"OrgRoleToDisplayName": displayName,
				}))
			return
		}
	}
}

func (p *SpaceUsersPluginPrinter) PrintUsers(_ models.Organization, space models.Space, _ string) {
	for _, role := range p.Roles {
		users, _ := p.UserLister(space.Guid, role)
		for _, user := range users {
			u, found := p.UsersMap[user.Username]
			if found {
				u.Roles = append(u.Roles, role)
			} else {
				u = plugin_models.GetSpaceUsers_Model{}
				u.Username = user.Username
				u.Guid = user.Guid
				u.IsAdmin = user.IsAdmin
				u.Roles = make([]string, 1)
				u.Roles[0] = role
			}
			p.UsersMap[user.Username] = u
		}
	}
	for _, v := range p.UsersMap {
		*(p.PluginModel) = append(*(p.PluginModel), v)
	}
}

func (p *SpaceUsersUiPrinter) PrintUsers(org models.Organization, space models.Space, username string) {
	p.Ui.Say(T("Getting users in org {{.TargetOrg}} / space {{.TargetSpace}} as {{.CurrentUser}}",
		map[string]interface{}{
			"TargetOrg":   terminal.EntityNameColor(org.Name),
			"TargetSpace": terminal.EntityNameColor(space.Name),
			"CurrentUser": terminal.EntityNameColor(username),
		}))

	for _, role := range p.Roles {
		displayName := p.RoleDisplayNames[role]
		users, err := p.UserLister(space.Guid, role)
		if err != nil {
			p.Ui.Failed(T("Failed fetching space-users for role {{.SpaceRoleToDisplayName}}.\n{{.Error}}",
				map[string]interface{}{
					"Error":                  err.Error(),
					"SpaceRoleToDisplayName": displayName,
				}))
			return
		}
		p.Ui.Say("")
		p.Ui.Say("%s", terminal.HeaderColor(displayName))

		if len(users) == 0 {
			p.Ui.Say(fmt.Sprintf("  "+T("No %s found"), displayName))
		} else {
			for _, user := range users {
				p.Ui.Say("  %s", user.Username)
			}
		}
	}
}
<|endoftext|>


<commit_before>package pgtype_test

import (
	"testing"

	version "github.com/hashicorp/go-version"
	"github.com/jackc/pgx/pgtype"
	"github.com/jackc/pgx/pgtype/testutil"
)

func TestLineTranscode(t *testing.T) {
	conn := testutil.MustConnectPgx(t)
	serverVersion, err := version.NewVersion(conn.RuntimeParams["server_version"])
	if err != nil {
		t.Fatalf("cannot get server version: %v", err)
	}
	testutil.MustClose(t, conn)

	minVersion := version.Must(version.NewVersion("9.4"))

	if serverVersion.LessThan(minVersion) {
		t.Skipf("Skipping line test for server version %v", serverVersion)
	}

	testutil.TestSuccessfulTranscode(t, "line", []interface{}{
		&pgtype.Line{
			A: 1.23, B: 4.56, C: 7.89,
			Status: pgtype.Present,
		},
		&pgtype.Line{
			A: -1.23, B: -4.56, C: -7.89,
			Status: pgtype.Present,
		},
		&pgtype.Line{Status: pgtype.Null},
	})
}
<commit_msg>Skip test based on missing line type<commit_after>package pgtype_test

import (
	"testing"

	"github.com/jackc/pgx/pgtype"
	"github.com/jackc/pgx/pgtype/testutil"
)

func TestLineTranscode(t *testing.T) {
	conn := testutil.MustConnectPgx(t)
	if _, ok := conn.ConnInfo.DataTypeForName("line"); !ok {
		t.Skip("Skipping due to no line type")
	}

	testutil.TestSuccessfulTranscode(t, "line", []interface{}{
		&pgtype.Line{
			A: 1.23, B: 4.56, C: 7.89,
			Status: pgtype.Present,
		},
		&pgtype.Line{
			A: -1.23, B: -4.56, C: -7.89,
			Status: pgtype.Present,
		},
		&pgtype.Line{Status: pgtype.Null},
	})
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/boltdb/bolt"
)

// DBConf provides methods for reading and writing configs from a database
type DBConf struct {
	fileName string
	db       *bolt.DB
}

const (
	// CurrentBucketName is the name of the bucket that holds the current
	// verison of the config
	CurrentBucketName = "current"

	// CurrentVersionHash is the "hash" name of the current config
	CurrentVersionHash   = "current"
	appConfigBucketName  = "app"
	userConfigBucketName = "user"
)

var (
	// BucketNames holds the names of the buckets used to store versioned configs
	BucketNames = []string{
		"app",
		"user",
	}
)

func (d *DBConf) initAdminUser() error {
	adminExists := false
	err := d.db.Update(func(t *bolt.Tx) error {
		b, err := t.CreateBucketIfNotExists([]byte(userConfigBucketName))
		if err != nil {
			return err
		}

		// the admin user is always user_id 0
		buff := b.Get([]byte("admin"))

		if len(buff) != 0 {
			adminExists = true
		}

		return nil
	})

	// if the admin already exists, no need to do anything
	if adminExists {
		return nil
	}

	if err != nil {
		return err
	}

	// if no user is found, we must created one
	u := NewUser("admin", "admin", "admin", ADMIN)
	logrus.Info("Adding default admin user. user: admin passs: admin")
	// add the user to the database
	return d.PutUser(u)
}

func (d *DBConf) init() {

	// get the schema, and apply it before moving on
	s := GetSchemaFromDb(d.db)

	// bootstrap
	if s.Version == First {
		logrus.Info("Bootstrapping config db")
		err := s.Apply(d.db)
		if err != nil {
			logrus.Errorf("Unable to bootstrap config %s", err.Error())
		}

	}

	for LatestSchema().Greater(GetSchemaFromDb(d.db)) {
		logrus.Infof("Upgrading config db version from %s to %s", s.Version, LatestSchema().Version)
		err := LatestSchema().Apply(d.db)
		if err != nil {
			logrus.Errorf("Unable to apply schema version %s to config db %s", LatestSchema().Version, err.Error())
		} else {
			s = LatestSchema()
		}
	}

	logrus.Infof("Using db config version %s", s.Version)

	err := d.initAdminUser()
	if err != nil {
		logrus.Errorf("Unable init admin user: %s", err)
	}
}

func createIfNotExists(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		f, err := os.Create(path)
		f.Close()
		return err
	}

	return nil
}

func (d *DBConf) initDB() error {
	createIfNotExists(d.fileName)
	db, err := bolt.Open(d.fileName, 0600, nil)
	d.db = db
	d.init()

	return err
}

func (d *DBConf) encode(i interface{}) ([]byte, error) {
	return json.Marshal(i)
}

func (d *DBConf) decode(buff []byte, i interface{}) error {
	return json.Unmarshal(buff, i)
}

// Snapshot represents a bangarang config at a given point in time
type Snapshot struct {
	Hash            string     `json:"hash"`
	Timestamp       time.Time  `json:"time_stamp"`
	App             *AppConfig `json:"app"`
	CreatorId       uint16     `json:"creator_id"` // the User.Id of who created this snapshot
	CreatorName     string     `json:"creator_name"`
	CreatorUserName string     `json:"creator_user_name"`
}

func newSnapshot(ac *AppConfig, creator *User) *Snapshot {
	return &Snapshot{
		Timestamp:       time.Now(),
		App:             ac,
		CreatorName:     creator.Name,
		CreatorUserName: creator.UserName,
		Hash:            fmt.Sprintf("%x", HashConfig(ac)),
	}
}

//  GetUserByUserName
func (d *DBConf) GetUserByUserName(name string) (*User, error) {

	// get all the users
	users, err := d.ListUsers()
	if err != nil {
		return nil, err
	}

	// for every user, check to see if it has the user name we are looking for
	for _, u := range users {
		if u.UserName == name {
			return u, nil
		}
	}

	return nil, fmt.Errorf("Unable to find users with name %s", name)
}

//  GetUser by their User.Id
func (d *DBConf) GetUser(name string) (*User, error) {
	var buff []byte

	err := d.db.View(func(t *bolt.Tx) error {
		b := t.Bucket([]byte(userConfigBucketName))
		buff = b.Get([]byte(name))
		return nil
	})

	if err != nil {
		return nil, err
	}

	// if the found buffer is of len 0, then the user's record was not found
	if len(buff) == 0 {
		return nil, fmt.Errorf("User: %s not found", name)
	}

	// unmarshal the user
	u := &User{}
	err = d.decode(buff, u)

	return u, err
}

// PutUser inserts the user into the db
func (d *DBConf) PutUser(u *User) error {

	// encode the user
	buff, err := d.encode(u)
	if err != nil {
		return err
	}

	// write the user to the db
	err = d.db.Update(func(t *bolt.Tx) error {
		b := t.Bucket([]byte(userConfigBucketName))
		return b.Put([]byte(u.UserName), buff)
	})

	return err
}

// DeleteUser by the User.Id
func (d *DBConf) DeleteUser(name string) error {
	return d.db.Update(func(t *bolt.Tx) error {
		b := t.Bucket([]byte(userConfigBucketName))
		return b.Delete([]byte(name))
	})
}

// ListUsers fetches all known users
func (d *DBConf) ListUsers() ([]*User, error) {
	var u []*User

	err := d.db.View(func(t *bolt.Tx) error {
		b := t.Bucket([]byte(userConfigBucketName))
		u = make([]*User, 0, b.Stats().KeyN)

		// for every key/value decode the user and append it to the user list
		return b.ForEach(func(key, val []byte) error {
			x := &User{}
			err := d.decode(val, x)
			if err != nil {
				return err
			}

			// add the user to the list
			u = append(u, x)
			return nil
		})

	})
	return u, err
}

func (d *DBConf) getVersion(version string) (*AppConfig, error) {
	logrus.Infof("Loading config version %s", version)

	var buff []byte
	err := d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(appConfigBucketName))
		if b == nil {
			var err error
			b, err = tx.CreateBucket([]byte(appConfigBucketName))
			if err != nil {
				return err
			}
		}
		buff = b.Get([]byte(version))
		return nil
	})

	if err != nil {
		return nil, err
	}

	// decode the snapshot
	s := &Snapshot{
		App: NewDefaultConfig(),
	}
	s.App.provider = d

	// if the buffer is of zero size, then the config was not found
	if len(buff) == 0 {
		logrus.Infof("No config found for version %s in db, using defaults", version)
		return s.App, nil
	}

	err = d.decode(buff, s)
	if err != nil {
		return nil, err
	}

	s.App.provider = d

	return s.App, nil
}

// GetCurrent loads the current version of the config
func (d *DBConf) GetCurrent() (*AppConfig, error) {
	return d.getVersion(CurrentVersionHash)
}

// GetConfig get the config file which has the hash of given version
func (d *DBConf) GetConfig(versionHash string) (*AppConfig, error) {
	return d.getVersion(versionHash)
}

func (d *DBConf) ListRawSnapshots() []json.RawMessage {
	raw := []json.RawMessage{}
	err := d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(appConfigBucketName))
		return b.ForEach(func(k, v []byte) error {
			raw = append(raw, json.RawMessage(v))
			return nil
		})
	})

	if err != nil {
		logrus.Error(err)
	}

	return raw

}

func (d *DBConf) ListSnapshots() []*Snapshot {
	snaps := []*Snapshot{}
	err := d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(appConfigBucketName))
		return b.ForEach(func(k, v []byte) error {
			s := &Snapshot{}
			err := d.decode(v, s)
			if err != nil {
				return err
			}
			s.Hash = string(k)
			snaps = append(snaps, s)
			return nil
		})
	})
	if err != nil {
		logrus.Error(err)
	}
	return snaps
}

// PutConfig writes the given config to the database and returns
// the new hash and an error
func (d *DBConf) PutConfig(a *AppConfig, u *User) (string, error) {

	// check to see if the given user has write permissions
	if u.Permissions < WRITE {
		return "", InsufficientPermissions(WRITE, u.Permissions)
	}
	err := d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(appConfigBucketName))
		oldBuff := b.Get([]byte(CurrentVersionHash))
		if len(oldBuff) > 0 {
			old := newSnapshot(NewDefaultConfig(), u)
			err := d.decode(oldBuff, old)
			if err != nil {
				log.Println(err)
				return err
			}

			// write the old snapshot at it's hash
			err = b.Put([]byte(old.Hash), oldBuff)
			if err != nil {
				log.Println(err)
				return err
			}
		}

		// write the new snapshot to disk
		newBuff, err := d.encode(newSnapshot(a, u))
		if err != nil {
			log.Println(err)
			return err
		}

		return b.Put([]byte(CurrentVersionHash), newBuff)
	})

	if err != nil {
		log.Println(err)
		return "", err
	}

	return string(a.Hash), nil
}
<commit_msg>updated logging<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/boltdb/bolt"
)

// DBConf provides methods for reading and writing configs from a database
type DBConf struct {
	fileName string
	db       *bolt.DB
}

const (
	// CurrentBucketName is the name of the bucket that holds the current
	// verison of the config
	CurrentBucketName = "current"

	// CurrentVersionHash is the "hash" name of the current config
	CurrentVersionHash   = "current"
	appConfigBucketName  = "app"
	userConfigBucketName = "user"
)

var (
	// BucketNames holds the names of the buckets used to store versioned configs
	BucketNames = []string{
		"app",
		"user",
	}
)

func (d *DBConf) initAdminUser() error {
	adminExists := false
	err := d.db.Update(func(t *bolt.Tx) error {
		b, err := t.CreateBucketIfNotExists([]byte(userConfigBucketName))
		if err != nil {
			return err
		}

		// the admin user is always user_id 0
		buff := b.Get([]byte("admin"))

		if len(buff) != 0 {
			adminExists = true
		}

		return nil
	})

	// if the admin already exists, no need to do anything
	if adminExists {
		return nil
	}

	if err != nil {
		return err
	}

	// if no user is found, we must created one
	u := NewUser("admin", "admin", "admin", ADMIN)
	logrus.Info("Adding default admin user. user: admin passs: admin")
	// add the user to the database
	return d.PutUser(u)
}

func (d *DBConf) init() {

	// get the schema, and apply it before moving on
	s := GetSchemaFromDb(d.db)

	// bootstrap
	if s.Version == First {
		logrus.Info("Bootstrapping config db")
		err := s.Apply(d.db)
		if err != nil {
			logrus.Errorf("Unable to bootstrap config %s", err.Error())
		}

	}

	for LatestSchema().Greater(GetSchemaFromDb(d.db)) {
		logrus.Infof("Upgrading config db version from %s to %s", s.Version, LatestSchema().Version)
		err := LatestSchema().Apply(d.db)
		if err != nil {
			logrus.Errorf("Unable to apply schema version %s to config db %s", LatestSchema().Version, err.Error())
		} else {
			s = LatestSchema()
		}
	}

	logrus.Infof("Using db config version %s", s.Version)

	err := d.initAdminUser()
	if err != nil {
		logrus.Errorf("Unable init admin user: %s", err)
	}
}

func createIfNotExists(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		f, err := os.Create(path)
		f.Close()
		return err
	}

	return nil
}

func (d *DBConf) initDB() error {
	createIfNotExists(d.fileName)
	db, err := bolt.Open(d.fileName, 0600, nil)
	d.db = db
	d.init()

	return err
}

func (d *DBConf) encode(i interface{}) ([]byte, error) {
	return json.Marshal(i)
}

func (d *DBConf) decode(buff []byte, i interface{}) error {
	return json.Unmarshal(buff, i)
}

// Snapshot represents a bangarang config at a given point in time
type Snapshot struct {
	Hash            string     `json:"hash"`
	Timestamp       time.Time  `json:"time_stamp"`
	App             *AppConfig `json:"app"`
	CreatorId       uint16     `json:"creator_id"` // the User.Id of who created this snapshot
	CreatorName     string     `json:"creator_name"`
	CreatorUserName string     `json:"creator_user_name"`
}

func newSnapshot(ac *AppConfig, creator *User) *Snapshot {
	return &Snapshot{
		Timestamp:       time.Now(),
		App:             ac,
		CreatorName:     creator.Name,
		CreatorUserName: creator.UserName,
		Hash:            fmt.Sprintf("%x", HashConfig(ac)),
	}
}

//  GetUserByUserName
func (d *DBConf) GetUserByUserName(name string) (*User, error) {

	// get all the users
	users, err := d.ListUsers()
	if err != nil {
		return nil, err
	}

	// for every user, check to see if it has the user name we are looking for
	for _, u := range users {
		if u.UserName == name {
			return u, nil
		}
	}

	return nil, fmt.Errorf("Unable to find users with name %s", name)
}

//  GetUser by their User.Id
func (d *DBConf) GetUser(name string) (*User, error) {
	var buff []byte

	err := d.db.View(func(t *bolt.Tx) error {
		b := t.Bucket([]byte(userConfigBucketName))
		buff = b.Get([]byte(name))
		return nil
	})

	if err != nil {
		return nil, err
	}

	// if the found buffer is of len 0, then the user's record was not found
	if len(buff) == 0 {
		return nil, fmt.Errorf("User: %s not found", name)
	}

	// unmarshal the user
	u := &User{}
	err = d.decode(buff, u)

	return u, err
}

// PutUser inserts the user into the db
func (d *DBConf) PutUser(u *User) error {

	// encode the user
	buff, err := d.encode(u)
	if err != nil {
		return err
	}

	// write the user to the db
	err = d.db.Update(func(t *bolt.Tx) error {
		b := t.Bucket([]byte(userConfigBucketName))
		return b.Put([]byte(u.UserName), buff)
	})

	return err
}

// DeleteUser by the User.Id
func (d *DBConf) DeleteUser(name string) error {
	return d.db.Update(func(t *bolt.Tx) error {
		b := t.Bucket([]byte(userConfigBucketName))
		return b.Delete([]byte(name))
	})
}

// ListUsers fetches all known users
func (d *DBConf) ListUsers() ([]*User, error) {
	var u []*User

	err := d.db.View(func(t *bolt.Tx) error {
		b := t.Bucket([]byte(userConfigBucketName))
		u = make([]*User, 0, b.Stats().KeyN)

		// for every key/value decode the user and append it to the user list
		return b.ForEach(func(key, val []byte) error {
			x := &User{}
			err := d.decode(val, x)
			if err != nil {
				return err
			}

			// add the user to the list
			u = append(u, x)
			return nil
		})

	})
	return u, err
}

func (d *DBConf) getVersion(version string) (*AppConfig, error) {
	logrus.Infof("Loading config version %s", version)

	var buff []byte
	err := d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(appConfigBucketName))
		if b == nil {
			var err error
			b, err = tx.CreateBucket([]byte(appConfigBucketName))
			if err != nil {
				return err
			}
		}
		buff = b.Get([]byte(version))
		return nil
	})

	if err != nil {
		return nil, err
	}

	// decode the snapshot
	s := &Snapshot{
		App: NewDefaultConfig(),
	}
	s.App.provider = d

	// if the buffer is of zero size, then the config was not found
	if len(buff) == 0 {
		logrus.Infof("No config found for version %s in db, using defaults", version)
		return s.App, nil
	}

	err = d.decode(buff, s)
	if err != nil {
		return nil, err
	}

	s.App.provider = d

	return s.App, nil
}

// GetCurrent loads the current version of the config
func (d *DBConf) GetCurrent() (*AppConfig, error) {
	return d.getVersion(CurrentVersionHash)
}

// GetConfig get the config file which has the hash of given version
func (d *DBConf) GetConfig(versionHash string) (*AppConfig, error) {
	return d.getVersion(versionHash)
}

func (d *DBConf) ListSnapshots() []*Snapshot {
	snaps := []*Snapshot{}
	err := d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(appConfigBucketName))
		return b.ForEach(func(k, v []byte) error {
			s := &Snapshot{}
			err := d.decode(v, s)
			if err != nil {
				return err
			}
			s.Hash = string(k)
			snaps = append(snaps, s)
			return nil
		})
	})
	if err != nil {
		logrus.Error(err)
	}
	return snaps
}

// PutConfig writes the given config to the database and returns
// the new hash and an error
func (d *DBConf) PutConfig(a *AppConfig, u *User) (string, error) {

	// check to see if the given user has write permissions
	if u.Permissions < WRITE {
		return "", InsufficientPermissions(WRITE, u.Permissions)
	}
	err := d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(appConfigBucketName))
		oldBuff := b.Get([]byte(CurrentVersionHash))
		if len(oldBuff) > 0 {
			old := newSnapshot(NewDefaultConfig(), u)
			err := d.decode(oldBuff, old)
			if err != nil {
				logrus.Error("Unable to decode snapshot")
				logrus.Error(err)
				return err
			}

			// write the old snapshot at it's hash
			err = b.Put([]byte(old.Hash), oldBuff)
			if err != nil {
				logrus.Error(err)
				return err
			}
		}

		// write the new snapshot to disk
		newBuff, err := d.encode(newSnapshot(a, u))
		if err != nil {
			logrus.Error(err)
			return err
		}

		return b.Put([]byte(CurrentVersionHash), newBuff)
	})

	if err != nil {
		logrus.Error(err)
		return "", err
	}

	return string(a.Hash), nil
}
<|endoftext|>


<commit_before>package js

import (
	"github.com/loadimpact/speedboat/runner"
	"github.com/robertkrimen/otto"
	"net/http"
	"time"
)

type JSRunner struct {
	BaseVM *otto.Otto
	Script *otto.Script

	httpClient *http.Client
}

func New() (r *JSRunner, err error) {
	r = &JSRunner{}

	// Create a base VM
	r.BaseVM = otto.New()

	// Bridge basic functions
	r.BaseVM.Set("sleep", jsSleepFactory(time.Sleep))

	// Use a single HTTP client for this
	r.httpClient = &http.Client{
		Transport: &http.Transport{
			DisableKeepAlives: true,
		},
	}
	r.BaseVM.Set("get", jsHTTPGetFactory(func(url string) (*http.Response, error) {
		return r.httpClient.Get(url)
	}))

	return r, nil
}

func (r *JSRunner) Load(filename, src string) (err error) {
	r.Script, err = r.BaseVM.Compile(filename, src)
	return err
}

func (r *JSRunner) RunVU(stop <-chan interface{}) <-chan interface{} {
	out := make(chan interface{})

	go func() {
		defer close(out)

	runLoop:
		for {
			select {
			case <-stop:
				break runLoop
			default:
				vm := r.BaseVM.Copy()
				for res := range r.RunIteration(vm) {
					out <- res
				}
			}
		}
	}()

	return out
}

func (r *JSRunner) RunIteration(vm *otto.Otto) <-chan interface{} {
	out := make(chan interface{})

	go func() {
		defer close(out)
		defer func() {
			if err := recover(); err != nil {
				out <- runner.NewError(err.(JSError))
			}
		}()

		// Log has to be bridged here, as it needs a reference to the channel
		vm.Set("log", jsLogFactory(func(text string) {
			out <- runner.NewLogEntry(text)
		}))

		startTime := time.Now()
		_, err := vm.Run(r.Script)
		duration := time.Since(startTime)

		if err != nil {
			out <- runner.NewError(err)
		}

		out <- runner.NewMetric(startTime, duration)
	}()

	return out
}
<commit_msg>This makes more sense here<commit_after>package js

import (
	"github.com/loadimpact/speedboat/runner"
	"github.com/robertkrimen/otto"
	"net/http"
	"time"
)

type JSRunner struct {
	BaseVM *otto.Otto
	Script *otto.Script

	httpClient *http.Client
}

func New() (r *JSRunner, err error) {
	r = &JSRunner{}

	// Create a base VM
	r.BaseVM = otto.New()

	// Bridge basic functions
	r.BaseVM.Set("sleep", jsSleepFactory(time.Sleep))

	// Use a single HTTP client for this
	r.httpClient = &http.Client{
		Transport: &http.Transport{
			DisableKeepAlives: true,
		},
	}
	r.BaseVM.Set("get", jsHTTPGetFactory(func(url string) (*http.Response, error) {
		return r.httpClient.Get(url)
	}))

	return r, nil
}

func (r *JSRunner) Load(filename, src string) (err error) {
	r.Script, err = r.BaseVM.Compile(filename, src)
	return err
}

func (r *JSRunner) RunVU(stop <-chan interface{}) <-chan interface{} {
	out := make(chan interface{})

	go func() {
		defer close(out)

	runLoop:
		for {
			select {
			case <-stop:
				break runLoop
			default:
				for res := range r.RunIteration() {
					out <- res
				}
			}
		}
	}()

	return out
}

func (r *JSRunner) RunIteration() <-chan interface{} {
	out := make(chan interface{})

	go func() {
		defer close(out)
		defer func() {
			if err := recover(); err != nil {
				out <- runner.NewError(err.(JSError))
			}
		}()

		// Make a copy of the base VM
		vm := r.BaseVM.Copy()

		// Log has to be bridged here, as it needs a reference to the channel
		vm.Set("log", jsLogFactory(func(text string) {
			out <- runner.NewLogEntry(text)
		}))

		startTime := time.Now()
		_, err := vm.Run(r.Script)
		duration := time.Since(startTime)

		if err != nil {
			out <- runner.NewError(err)
		}

		out <- runner.NewMetric(startTime, duration)
	}()

	return out
}
<|endoftext|>


<commit_before>package ldap

import (
	"github.com/nmcclain/ldap"
	"net"
	"time"
)

func (p *ldapconfig) probe() error {
	timeout := time.Duration(p.probefreq) * time.Second
	timeout = timeout / time.Duration(len(p.hostnames))
	for _, hostname := range p.hostnames {
		hostnamePort := hostname + ":636"
		start := time.Now()
		conn, err := ldap.DialTLSDialer("tcp", hostnamePort,
			nil, &net.Dialer{Timeout: timeout})
		if err != nil {
			p.healthy = false
			continue
		}
		defer conn.Close()
		err = conn.Bind(p.bindDN, p.bindPassword)
		latency := time.Since(start)
		p.ldapLatencyDistribution.Add(latency)
		if err != nil {
			p.healthy = false
		} else {
			p.healthy = true
			break
		}
	}
	return nil
}
<commit_msg>rewrite to use gopkg library<commit_after>package ldap

import (
	"crypto/tls"
	"gopkg.in/ldap.v2"
	"net"
	"time"
)

func (p *ldapconfig) probe() error {
	timeout := time.Duration(p.probefreq) * time.Second
	timeout = timeout / time.Duration(len(p.hostnames))
	p.healthy = false
	for _, hostname := range p.hostnames {
		hostnamePort := hostname + ":636"
		start := time.Now()

		// timeouts must be speficied both at the network layer and at the LDAP layer
		tlsConn, err := tls.DialWithDialer(&net.Dialer{Timeout: timeout},
			"tcp", hostnamePort, &tls.Config{ServerName: hostname})
		if err != nil {
			continue
		}
		// we dont close the tls connection directly  close defer to the new ldap connection
		conn := ldap.NewConn(tlsConn, true)
		defer conn.Close()
		conn.SetTimeout(timeout)
		conn.Start()

		err = conn.Bind(p.bindDN, p.bindPassword)
		latency := time.Since(start)
		p.ldapLatencyDistribution.Add(latency)
		if err != nil {
			continue
		}
		p.healthy = true
		break

	}
	return nil
}
<|endoftext|>


<commit_before>/*
This is free and unencumbered software released into the public domain. For more
information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
*/
package gedcom

import (
	"io"
	"testing"
)

type example struct {
	input []byte
	level int
	tag   string
	value string
	xref  string
}

var examples = []example{
	{[]byte("1 SEX F\n"), 1, `SEX`, `F`, ""},
	{[]byte(" 1 SEX F\n"), 1, `SEX`, `F`, ""},
	{[]byte("  \r\n\t 1 SEX F\n"), 1, `SEX`, `F`, ""},
	{[]byte("  \r\n\t 1     SEX      F\n"), 1, `SEX`, `F`, ""},
	{[]byte("1 SEX F\r"), 1, `SEX`, `F`, ""},
	{[]byte("1 SEX F \r"), 1, `SEX`, `F `, ""},
	{[]byte("0 HEAD\r"), 0, `HEAD`, ``, ""},
	{[]byte("0 @OTHER@ SUBM\n"), 0, `SUBM`, ``, "@OTHER@"},
}

func TestNextTagFound(t *testing.T) {
	s := &scanner{}
	for _, ex := range examples {
		s.reset()
		offset, err := s.nextTag(ex.input)

		if err != nil {
			t.Fatalf(`nextTag for "%s" returned error "%v", expected no error`, ex.input, err)
		}

		if offset == 0 {
			t.Fatalf(`nextTag for "%s" did not find tag, expected it to find`, ex.input)
		}

		if s.level != ex.level {
			t.Errorf(`nextTag for "%s" returned level %d, expected %d`, ex.input, s.level, ex.level)
		}

		if string(s.tag) != ex.tag {
			t.Errorf(`nextTag for "%s" returned tag "%s", expected "%s"`, ex.input, s.tag, ex.tag)
		}

		if string(s.value) != ex.value {
			t.Errorf(`nextTag for "%s" returned value "%s", expected "%s"`, ex.input, s.value, ex.value)
		}

		if string(s.xref) != ex.xref {
			t.Errorf(`nextTag for "%s" returned xref "%s", expected "%s"`, ex.input, s.xref, ex.xref)
		}

	}

}

var examplesNot = [][]byte{
	[]byte("1 SEX F"),
	[]byte(" 1 SEX F "),
}

func TestNextTagNotFound(t *testing.T) {
	s := &scanner{}
	for _, ex := range examplesNot {
		s.reset()
		_, err := s.nextTag(ex)

		if err != io.EOF {
			t.Fatalf(`nextTag for "%s" returned unexpected error "%v", expected io.EOF`, ex, err)
		}

	}

}
<commit_msg>Fixed non-compiling tests<commit_after>/*
This is free and unencumbered software released into the public domain. For more
information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
*/
package gedcom

import (
	"io"
	"testing"
)

type example struct {
	input []byte
	level int
	tag   string
	value string
	xref  string
}

var examples = []example{
	{[]byte("1 SEX F\n"), 1, `SEX`, `F`, ""},
	{[]byte(" 1 SEX F\n"), 1, `SEX`, `F`, ""},
	{[]byte("  \r\n\t 1 SEX F\n"), 1, `SEX`, `F`, ""},
	{[]byte("  \r\n\t 1     SEX      F\n"), 1, `SEX`, `F`, ""},
	{[]byte("1 SEX F\r"), 1, `SEX`, `F`, ""},
	{[]byte("1 SEX F \r"), 1, `SEX`, `F `, ""},
	{[]byte("0 HEAD\r"), 0, `HEAD`, ``, ""},
	{[]byte("0 @OTHER@ SUBM\n"), 0, `SUBM`, ``, "OTHER"},
}

func TestNextTagFound(t *testing.T) {
	s := &scanner{}
	for _, ex := range examples {
		s.reset()
		offset, err := s.nextTag(ex.input)

		if err != nil {
			t.Fatalf(`nextTag for "%s" returned error "%v", expected no error`, ex.input, err)
		}

		if offset == 0 {
			t.Fatalf(`nextTag for "%s" did not find tag, expected it to find`, ex.input)
		}

		if s.level != ex.level {
			t.Errorf(`nextTag for "%s" returned level %d, expected %d`, ex.input, s.level, ex.level)
		}

		if string(s.tag) != ex.tag {
			t.Errorf(`nextTag for "%s" returned tag "%s", expected "%s"`, ex.input, s.tag, ex.tag)
		}

		if string(s.value) != ex.value {
			t.Errorf(`nextTag for "%s" returned value "%s", expected "%s"`, ex.input, s.value, ex.value)
		}

		if string(s.xref) != ex.xref {
			t.Errorf(`nextTag for "%s" returned xref "%s", expected "%s"`, ex.input, s.xref, ex.xref)
		}

	}

}

var examplesNot = [][]byte{
	[]byte("1 SEX F"),
	[]byte(" 1 SEX F "),
}

func TestNextTagNotFound(t *testing.T) {
	s := &scanner{}
	for _, ex := range examplesNot {
		s.reset()
		_, err := s.nextTag(ex)

		if err != io.EOF {
			t.Fatalf(`nextTag for "%s" returned unexpected error "%v", expected io.EOF`, ex, err)
		}

	}

}
<|endoftext|>