<commit_before>package scheduler

import (
	"os"
	"time"

	"github.com/concourse/atc"
	"github.com/concourse/atc/db"
	"github.com/pivotal-golang/lager"
)

//go:generate counterfeiter . Locker
type Locker interface {
	AcquireWriteLockImmediately(lock []db.NamedLock) (db.Lock, error)
	AcquireReadLock(lock []db.NamedLock) (db.Lock, error)
}

//go:generate counterfeiter . BuildScheduler
type BuildScheduler interface {
	TryNextPendingBuild(atc.JobConfig, atc.ResourceConfigs) error
	BuildLatestInputs(atc.JobConfig, atc.ResourceConfigs) error

	TrackInFlightBuilds() error
}

//go:generate counterfeiter . ConfigDB
type ConfigDB interface {
	GetConfig() (atc.Config, error)
}

type Runner struct {
	Logger lager.Logger

	Locker   Locker
	ConfigDB ConfigDB

	Scheduler BuildScheduler

	Noop bool

	Interval time.Duration
}

func (runner *Runner) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	close(ready)

	if runner.Interval == 0 {
		panic("unconfigured scheduler interval")
	}

	runner.Logger.Info("start", lager.Data{
		"inverval": runner.Interval.String(),
	})

	defer runner.Logger.Info("done")

dance:
	for {
		runner.tick(runner.Logger.Session("tick"))

		select {
		case <-time.After(runner.Interval):
		case <-signals:
			break dance
		}
	}

	return nil
}

func (runner *Runner) tick(logger lager.Logger) {
	logger.Info("start")
	defer logger.Info("done")

	config, err := runner.ConfigDB.GetConfig()
	if err != nil {
		logger.Error("failed-to-get-config", err)
		return
	}

	err = runner.Scheduler.TrackInFlightBuilds()
	if err != nil {
		logger.Error("failed-to-track-in-flight-builds", err)
	}

	if runner.Noop {
		return
	}

	for _, job := range config.Jobs {
		lock, err := runner.Locker.AcquireWriteLockImmediately([]db.NamedLock{db.JobSchedulingLock(job.Name)})
		if err != nil {
			continue
		}

		logger.Debug("scheduling", lager.Data{
			"job": job.Name,
		})

		err = runner.Scheduler.TryNextPendingBuild(job, config.Resources)
		if err != nil {
			logger.Error("failed-to-try-next-pending-build", err)
		}

		err = runner.Scheduler.BuildLatestInputs(job, config.Resources)
		if err != nil {
			logger.Error("failed-to-build-from-latest-inputs", err)
		}

		lock.Release()
	}
}
<commit_msg>scheduler: nix logging, refactor<commit_after>package scheduler

import (
	"os"
	"time"

	"github.com/concourse/atc"
	"github.com/concourse/atc/db"
	"github.com/pivotal-golang/lager"
)

//go:generate counterfeiter . Locker
type Locker interface {
	AcquireWriteLockImmediately(lock []db.NamedLock) (db.Lock, error)
	AcquireReadLock(lock []db.NamedLock) (db.Lock, error)
}

//go:generate counterfeiter . BuildScheduler
type BuildScheduler interface {
	TryNextPendingBuild(atc.JobConfig, atc.ResourceConfigs) error
	BuildLatestInputs(atc.JobConfig, atc.ResourceConfigs) error

	TrackInFlightBuilds() error
}

//go:generate counterfeiter . ConfigDB
type ConfigDB interface {
	GetConfig() (atc.Config, error)
}

type Runner struct {
	Logger lager.Logger

	Locker   Locker
	ConfigDB ConfigDB

	Scheduler BuildScheduler

	Noop bool

	Interval time.Duration
}

func (runner *Runner) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	close(ready)

	if runner.Interval == 0 {
		panic("unconfigured scheduler interval")
	}

	runner.Logger.Info("start", lager.Data{
		"inverval": runner.Interval.String(),
	})

	defer runner.Logger.Info("done")

dance:
	for {
		runner.tick(runner.Logger.Session("tick"))

		select {
		case <-time.After(runner.Interval):
		case <-signals:
			break dance
		}
	}

	return nil
}

func (runner *Runner) tick(logger lager.Logger) {
	logger.Info("start")
	defer logger.Info("done")

	config, err := runner.ConfigDB.GetConfig()
	if err != nil {
		logger.Error("failed-to-get-config", err)
		return
	}

	err = runner.Scheduler.TrackInFlightBuilds()
	if err != nil {
		logger.Error("failed-to-track-in-flight-builds", err)
	}

	if runner.Noop {
		return
	}

	for _, job := range config.Jobs {
		lock, err := runner.Locker.AcquireWriteLockImmediately([]db.NamedLock{db.JobSchedulingLock(job.Name)})
		if err != nil {
			continue
		}

		runner.schedule(job, config.Resources, logger.Session("scheduling", lager.Data{
			"job": job.Name,
		}))

		lock.Release()
	}
}

func (runner *Runner) schedule(job atc.JobConfig, resources atc.ResourceConfigs, logger lager.Logger) {
	err := runner.Scheduler.TryNextPendingBuild(job, resources)
	if err != nil {
		logger.Error("failed-to-try-next-pending-build", err)
	}

	err = runner.Scheduler.BuildLatestInputs(job, resources)
	if err != nil {
		logger.Error("failed-to-build-from-latest-inputs", err)
	}
}
<|endoftext|>


<commit_before>// This files contains schema for Paris TraceRoute tests.
package schema

import (
	"time"

	"github.com/m-lab/annotation-service/api"
)

// TODO(dev): use mixed case Go variable names throughout

type ParisTracerouteHop struct {
	Protocol         string            `json:"protocal,string"`
	Src_ip           string            `json:"src_ip,string"`
	Src_af           int32             `json:"src_af,int32"`
	Dest_ip          string            `json:"dest_ip,string"`
	Dest_af          int32             `json:"dest_af,int32"`
	Src_hostname     string            `json:"src_hostname,string"`
	Dest_hostname    string            `json:"dest_hostname,string"`
	Rtt              []float64         `json:"rtt,[]float64"`
	Src_geolocation  api.GeolocationIP `json:"src_geolocation"`
	Dest_geolocation api.GeolocationIP `json:"dest_geolocation"`
}

type MLabConnectionSpecification struct {
	Server_ip          string            `json:"server_ip,string"`
	Server_af          int32             `json:"server_af,int32"`
	Client_ip          string            `json:"client_ip,string"`
	Client_af          int32             `json:"client_af,int32"`
	Data_direction     int32             `json:"data_direction,int32"`
	Server_geolocation api.GeolocationIP `json:"server_geolocation"`
	Client_geolocation api.GeolocationIP `json:"client_geolocation"`
}

type PT struct {
	TestID               string                      `json:"test_id,string" bigquery:"test_id"`
	Project              int32                       `json:"project,int32" bigquery:"project"`
	TaskFilename         string                      `json:"task_filename,string" bigquery:"task_filename"`
	ParseTime            time.Time                   `json:"parse_time" bigquery:"parse_time"`
	ParserVersion        string                      `json:"parser_version,string" bigquery:"parser_version"`
	LogTime              int64                       `json:"log_time,int64" bigquery:"log_time"`
	Connection_spec      MLabConnectionSpecification `json:"connection_spec"`
	Paris_traceroute_hop ParisTracerouteHop          `json:"paris_traceroute_hop"`
	Type                 int32                       `json:"type,int32"`
}
<commit_msg>Add new schema for PT (#688)<commit_after>// This files contains schema for Paris TraceRoute tests.
package schema

import (
	"time"

	"github.com/m-lab/annotation-service/api"
)

// TODO: The following schema will be deprecated soon.

type ParisTracerouteHop struct {
	Protocol         string            `json:"protocal,string"`
	Src_ip           string            `json:"src_ip,string"`
	Src_af           int32             `json:"src_af,int32"`
	Dest_ip          string            `json:"dest_ip,string"`
	Dest_af          int32             `json:"dest_af,int32"`
	Src_hostname     string            `json:"src_hostname,string"`
	Dest_hostname    string            `json:"dest_hostname,string"`
	Rtt              []float64         `json:"rtt,[]float64"`
	Src_geolocation  api.GeolocationIP `json:"src_geolocation"`
	Dest_geolocation api.GeolocationIP `json:"dest_geolocation"`
}

type MLabConnectionSpecification struct {
	Server_ip          string            `json:"server_ip,string"`
	Server_af          int32             `json:"server_af,int32"`
	Client_ip          string            `json:"client_ip,string"`
	Client_af          int32             `json:"client_af,int32"`
	Data_direction     int32             `json:"data_direction,int32"`
	Server_geolocation api.GeolocationIP `json:"server_geolocation"`
	Client_geolocation api.GeolocationIP `json:"client_geolocation"`
}

type PT struct {
	TestID               string                      `json:"test_id,string" bigquery:"test_id"`
	Project              int32                       `json:"project,int32" bigquery:"project"`
	TaskFilename         string                      `json:"task_filename,string" bigquery:"task_filename"`
	ParseTime            time.Time                   `json:"parse_time" bigquery:"parse_time"`
	ParserVersion        string                      `json:"parser_version,string" bigquery:"parser_version"`
	LogTime              int64                       `json:"log_time,int64" bigquery:"log_time"`
	Connection_spec      MLabConnectionSpecification `json:"connection_spec"`
	Paris_traceroute_hop ParisTracerouteHop          `json:"paris_traceroute_hop"`
	Type                 int32                       `json:"type,int32"`
}

// This will be new schema for traceroute tests.

type HopIP struct {
	Ip          string `json:"ip,string"`
	City        string `json:"city,string"`
	CountryCode string `json:"country_code,string"`
	Hostname    string `json:"hostname,string"`
}

type HopProbe struct {
	Flowid int64     `json:"flowid,int64"`
	Rtt    []float64 `json:"rtt"`
}

type HopLink struct {
	HopDstIp string     `json:"hop_dst_ip,string"`
	TTL      int64      `json:"ttl,int64"`
	Probes   []HopProbe `json:"probes"`
}

type ScamperHop struct {
	Source HopIP     `json:"source"`
	Linkc  int64     `json:"linkc,int64"`
	Links  []HopLink `json:"link"`
}

type PTTest struct {
	UUID           string       `json:"uuid,string" bigquery:"uuid"`
	Parseinfo      ParseInfo    `json:"parseinfo"`
	StartTime      int64        `json:"start_time,int64" bigquery:"start_time"`
	StopTime       int64        `json:"stop_time,int64" bigquery:"stop_time"`
	ScamperVersion string       `json:"scamper_version,string" bigquery:"scamper_version"`
	Source         ServerInfo   `json:"source"`
	Destination    ClientInfo   `json:"destination"`
	ProbeSize      int64        `json:"probe_size,int64"`
	ProbeC         int64        `json:"probec,int64"`
	Hop            []ScamperHop `json:"hop"`
}
<|endoftext|>


<commit_before>// Copyright 2016 The Linux Foundation
//
// 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 schema

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"regexp"

	digest "github.com/opencontainers/go-digest"
	"github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
	"github.com/xeipuuv/gojsonschema"
)

// Validator wraps a media type string identifier
// and implements validation against a JSON schema.
type Validator string

type validateFunc func(r io.Reader) error

var mapValidate = map[Validator]validateFunc{
	ValidatorMediaTypeImageConfig: validateConfig,
	ValidatorMediaTypeDescriptor:  validateDescriptor,
	ValidatorMediaTypeImageIndex:  validateIndex,
	ValidatorMediaTypeManifest:    validateManifest,
}

// ValidationError contains all the errors that happened during validation.
type ValidationError struct {
	Errs []error
}

func (e ValidationError) Error() string {
	return fmt.Sprintf("%v", e.Errs)
}

// Validate validates the given reader against the schema of the wrapped media type.
func (v Validator) Validate(src io.Reader) error {
	buf, err := ioutil.ReadAll(src)
	if err != nil {
		return errors.Wrap(err, "unable to read the document file")
	}

	if f, ok := mapValidate[v]; ok {
		if f == nil {
			return fmt.Errorf("internal error: mapValidate[%q] is nil", v)
		}
		err = f(bytes.NewReader(buf))
		if err != nil {
			return err
		}
	}

	sl := gojsonschema.NewReferenceLoaderFileSystem("file:///"+specs[v], fs)
	ml := gojsonschema.NewStringLoader(string(buf))

	result, err := gojsonschema.Validate(sl, ml)
	if err != nil {
		return errors.Wrapf(
			WrapSyntaxError(bytes.NewReader(buf), err),
			"schema %s: unable to validate", v)
	}

	if result.Valid() {
		return nil
	}

	errs := make([]error, 0, len(result.Errors()))
	for _, desc := range result.Errors() {
		errs = append(errs, fmt.Errorf("%s", desc))
	}

	return ValidationError{
		Errs: errs,
	}
}

type unimplemented string

func (v unimplemented) Validate(src io.Reader) error {
	return fmt.Errorf("%s: unimplemented", v)
}

func validateManifest(r io.Reader) error {
	header := v1.Manifest{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "manifest format mismatch")
	}

	if header.Config.MediaType != string(v1.MediaTypeImageConfig) {
		fmt.Printf("warning: config %s has an unknown media type: %s\n", header.Config.Digest, header.Config.MediaType)
	}

	for _, layer := range header.Layers {
		if layer.MediaType != string(v1.MediaTypeImageLayer) &&
			layer.MediaType != string(v1.MediaTypeImageLayerGzip) &&
			layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) &&
			layer.MediaType != string(v1.MediaTypeImageLayerNonDistributableGzip) {
			fmt.Printf("warning: layer %s has an unknown media type: %s\n", layer.Digest, layer.MediaType)
		}
	}
	return nil
}

func validateDescriptor(r io.Reader) error {
	header := v1.Descriptor{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "descriptor format mismatch")
	}

	err = header.Digest.Validate()
	if err == digest.ErrDigestUnsupported {
		// we ignore unsupported algorithms
		fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err)
		return nil
	}
	return err
}

func validateIndex(r io.Reader) error {
	header := v1.Index{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "manifestlist format mismatch")
	}

	for _, manifest := range header.Manifests {
		if manifest.MediaType != string(v1.MediaTypeImageManifest) {
			fmt.Printf("warning: manifest %s has an unknown media type: %s\n", manifest.Digest, manifest.MediaType)
		}
		if manifest.Platform != nil {
			checkPlatform(manifest.Platform.OS, manifest.Platform.Architecture)
		}

	}

	return nil
}

func validateConfig(r io.Reader) error {
	header := v1.Image{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "config format mismatch")
	}

	checkPlatform(header.OS, header.Architecture)

	envRegexp := regexp.MustCompile(`^[^=]+=.*$`)
	for _, e := range header.Config.Env {
		if !envRegexp.MatchString(e) {
			return errors.Errorf("unexpected env: %q", e)
		}
	}

	return nil
}

func checkPlatform(OS string, Architecture string) {
	validCombins := map[string][]string{
		"android":   {"arm"},
		"darwin":    {"386", "amd64", "arm", "arm64"},
		"dragonfly": {"amd64"},
		"freebsd":   {"386", "amd64", "arm"},
		"linux":     {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le", "s390x"},
		"netbsd":    {"386", "amd64", "arm"},
		"openbsd":   {"386", "amd64", "arm"},
		"plan9":     {"386", "amd64"},
		"solaris":   {"amd64"},
		"windows":   {"386", "amd64"}}
	for os, archs := range validCombins {
		if os == OS {
			for _, arch := range archs {
				if arch == Architecture {
					return
				}
			}
			fmt.Printf("warning: combination of %q and %q is invalid.", OS, Architecture)
		}
	}
	fmt.Printf("warning: operating system %q of the bundle is not supported yet.", OS)
}
<commit_msg>validate: change manifestlist to index<commit_after>// Copyright 2016 The Linux Foundation
//
// 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 schema

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"regexp"

	digest "github.com/opencontainers/go-digest"
	"github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
	"github.com/xeipuuv/gojsonschema"
)

// Validator wraps a media type string identifier
// and implements validation against a JSON schema.
type Validator string

type validateFunc func(r io.Reader) error

var mapValidate = map[Validator]validateFunc{
	ValidatorMediaTypeImageConfig: validateConfig,
	ValidatorMediaTypeDescriptor:  validateDescriptor,
	ValidatorMediaTypeImageIndex:  validateIndex,
	ValidatorMediaTypeManifest:    validateManifest,
}

// ValidationError contains all the errors that happened during validation.
type ValidationError struct {
	Errs []error
}

func (e ValidationError) Error() string {
	return fmt.Sprintf("%v", e.Errs)
}

// Validate validates the given reader against the schema of the wrapped media type.
func (v Validator) Validate(src io.Reader) error {
	buf, err := ioutil.ReadAll(src)
	if err != nil {
		return errors.Wrap(err, "unable to read the document file")
	}

	if f, ok := mapValidate[v]; ok {
		if f == nil {
			return fmt.Errorf("internal error: mapValidate[%q] is nil", v)
		}
		err = f(bytes.NewReader(buf))
		if err != nil {
			return err
		}
	}

	sl := gojsonschema.NewReferenceLoaderFileSystem("file:///"+specs[v], fs)
	ml := gojsonschema.NewStringLoader(string(buf))

	result, err := gojsonschema.Validate(sl, ml)
	if err != nil {
		return errors.Wrapf(
			WrapSyntaxError(bytes.NewReader(buf), err),
			"schema %s: unable to validate", v)
	}

	if result.Valid() {
		return nil
	}

	errs := make([]error, 0, len(result.Errors()))
	for _, desc := range result.Errors() {
		errs = append(errs, fmt.Errorf("%s", desc))
	}

	return ValidationError{
		Errs: errs,
	}
}

type unimplemented string

func (v unimplemented) Validate(src io.Reader) error {
	return fmt.Errorf("%s: unimplemented", v)
}

func validateManifest(r io.Reader) error {
	header := v1.Manifest{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "manifest format mismatch")
	}

	if header.Config.MediaType != string(v1.MediaTypeImageConfig) {
		fmt.Printf("warning: config %s has an unknown media type: %s\n", header.Config.Digest, header.Config.MediaType)
	}

	for _, layer := range header.Layers {
		if layer.MediaType != string(v1.MediaTypeImageLayer) &&
			layer.MediaType != string(v1.MediaTypeImageLayerGzip) &&
			layer.MediaType != string(v1.MediaTypeImageLayerNonDistributable) &&
			layer.MediaType != string(v1.MediaTypeImageLayerNonDistributableGzip) {
			fmt.Printf("warning: layer %s has an unknown media type: %s\n", layer.Digest, layer.MediaType)
		}
	}
	return nil
}

func validateDescriptor(r io.Reader) error {
	header := v1.Descriptor{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "descriptor format mismatch")
	}

	err = header.Digest.Validate()
	if err == digest.ErrDigestUnsupported {
		// we ignore unsupported algorithms
		fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err)
		return nil
	}
	return err
}

func validateIndex(r io.Reader) error {
	header := v1.Index{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "index format mismatch")
	}

	for _, manifest := range header.Manifests {
		if manifest.MediaType != string(v1.MediaTypeImageManifest) {
			fmt.Printf("warning: manifest %s has an unknown media type: %s\n", manifest.Digest, manifest.MediaType)
		}
		if manifest.Platform != nil {
			checkPlatform(manifest.Platform.OS, manifest.Platform.Architecture)
		}

	}

	return nil
}

func validateConfig(r io.Reader) error {
	header := v1.Image{}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Wrapf(err, "error reading the io stream")
	}

	err = json.Unmarshal(buf, &header)
	if err != nil {
		return errors.Wrap(err, "config format mismatch")
	}

	checkPlatform(header.OS, header.Architecture)

	envRegexp := regexp.MustCompile(`^[^=]+=.*$`)
	for _, e := range header.Config.Env {
		if !envRegexp.MatchString(e) {
			return errors.Errorf("unexpected env: %q", e)
		}
	}

	return nil
}

func checkPlatform(OS string, Architecture string) {
	validCombins := map[string][]string{
		"android":   {"arm"},
		"darwin":    {"386", "amd64", "arm", "arm64"},
		"dragonfly": {"amd64"},
		"freebsd":   {"386", "amd64", "arm"},
		"linux":     {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le", "s390x"},
		"netbsd":    {"386", "amd64", "arm"},
		"openbsd":   {"386", "amd64", "arm"},
		"plan9":     {"386", "amd64"},
		"solaris":   {"amd64"},
		"windows":   {"386", "amd64"}}
	for os, archs := range validCombins {
		if os == OS {
			for _, arch := range archs {
				if arch == Architecture {
					return
				}
			}
			fmt.Printf("warning: combination of %q and %q is invalid.", OS, Architecture)
		}
	}
	fmt.Printf("warning: operating system %q of the bundle is not supported yet.", OS)
}
<|endoftext|>


<commit_before>package testrunner

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/ginkgo/testsuite"
	"github.com/onsi/ginkgo/internal/remote"
	"github.com/onsi/ginkgo/reporters/stenographer"
	"github.com/onsi/ginkgo/types"
)

type TestRunner struct {
	Suite testsuite.TestSuite

	compiled              bool
	compilationTargetPath string

	numCPU         int
	parallelStream bool
	race           bool
	cover          bool
	coverPkg       string
	tags           string
	additionalArgs []string
}

func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, race bool, cover bool, coverPkg string, tags string, additionalArgs []string) *TestRunner {
	runner := &TestRunner{
		Suite:          suite,
		numCPU:         numCPU,
		parallelStream: parallelStream,
		race:           race,
		cover:          cover,
		coverPkg:       coverPkg,
		tags:           tags,
		additionalArgs: additionalArgs,
	}

	if !suite.Precompiled {
		dir, err := ioutil.TempDir("", "ginkgo")
		if err != nil {
			panic(fmt.Sprintf("coulnd't create temporary directory... might be time to rm -rf:\n%s", err.Error()))
		}
		runner.compilationTargetPath = filepath.Join(dir, suite.PackageName+".test")
	}

	return runner
}

func (t *TestRunner) Compile() error {
	return t.CompileTo(t.compilationTargetPath)
}

func (t *TestRunner) CompileTo(path string) error {
	if t.compiled {
		return nil
	}

	if t.Suite.Precompiled {
		return nil
	}

	args := []string{"test", "-c", "-i", "-o", path, t.Suite.Path}
	if t.race {
		args = append(args, "-race")
	}
	if t.cover || t.coverPkg != "" {
		args = append(args, "-cover", "-covermode=atomic")
	}
	if t.coverPkg != "" {
		args = append(args, fmt.Sprintf("-coverpkg=%s", t.coverPkg))
	}
	if t.tags != "" {
		args = append(args, fmt.Sprintf("-tags=%s", t.tags))
	}

	cmd := exec.Command("go", args...)

	output, err := cmd.CombinedOutput()

	if err != nil {
		fixedOutput := fixCompilationOutput(string(output), t.Suite.Path)
		if len(output) > 0 {
			return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, fixedOutput)
		}
		return fmt.Errorf("Failed to compile %s", t.Suite.PackageName)
	}

	if fileExists(path) == false {
		compiledFile := t.Suite.PackageName + ".test"
		if fileExists(compiledFile) {
			// seems like we are on an old go version that does not support the -o flag on go test
			// move the compiled test file to the desired location by hand
			err = os.Rename(compiledFile, path)
			if err != nil {
				// We cannot move the file, perhaps because the source and destination
				// are on different partitions. We can copy the file, however.
				err = copyFile(compiledFile, path)
				if err != nil {
					return fmt.Errorf("Failed to copy compiled file: %s", err)
				}
			}
		} else {
			return fmt.Errorf("Failed to compile %s: output file %q could not be found", t.Suite.PackageName, path)
		}
	}

	t.compiled = true

	return nil
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsNotExist(err) == false
}

// copyFile copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
func copyFile(src, dst string) error {
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}
	mode := srcInfo.Mode()

	in, err := os.Open(src)
	if err != nil {
		return err
	}

	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}

	defer func() {
		closeErr := out.Close()
		if err == nil {
			err = closeErr
		}
	}()

	_, err = io.Copy(out, in)
	if err != nil {
		return err
	}

	err = out.Sync()
	if err != nil {
		return err
	}

	return out.Chmod(mode)
}

/*
go test -c -i spits package.test out into the cwd. there's no way to change this.

to make sure it doesn't generate conflicting .test files in the cwd, Compile() must switch the cwd to the test package.

unfortunately, this causes go test's compile output to be expressed *relative to the test package* instead of the cwd.

this makes it hard to reason about what failed, and also prevents iterm's Cmd+click from working.

fixCompilationOutput..... rewrites the output to fix the paths.

yeah......
*/
func fixCompilationOutput(output string, relToPath string) string {
	re := regexp.MustCompile(`^(\S.*\.go)\:\d+\:`)
	lines := strings.Split(output, "\n")
	for i, line := range lines {
		indices := re.FindStringSubmatchIndex(line)
		if len(indices) == 0 {
			continue
		}

		path := line[indices[2]:indices[3]]
		path = filepath.Join(relToPath, path)
		lines[i] = path + line[indices[3]:]
	}
	return strings.Join(lines, "\n")
}

func (t *TestRunner) Run() RunResult {
	if t.Suite.IsGinkgo {
		if t.numCPU > 1 {
			if t.parallelStream {
				return t.runAndStreamParallelGinkgoSuite()
			} else {
				return t.runParallelGinkgoSuite()
			}
		} else {
			return t.runSerialGinkgoSuite()
		}
	} else {
		return t.runGoTestSuite()
	}
}

func (t *TestRunner) CleanUp() {
	if t.Suite.Precompiled {
		return
	}
	os.RemoveAll(filepath.Dir(t.compilationTargetPath))
}

func (t *TestRunner) runSerialGinkgoSuite() RunResult {
	ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
	return t.run(t.cmd(ginkgoArgs, os.Stdout, 1), nil)
}

func (t *TestRunner) runGoTestSuite() RunResult {
	return t.run(t.cmd([]string{"-test.v"}, os.Stdout, 1), nil)
}

func (t *TestRunner) runAndStreamParallelGinkgoSuite() RunResult {
	completions := make(chan RunResult)
	writers := make([]*logWriter, t.numCPU)

	server, err := remote.NewServer(t.numCPU)
	if err != nil {
		panic("Failed to start parallel spec server")
	}

	server.Start()
	defer server.Close()

	for cpu := 0; cpu < t.numCPU; cpu++ {
		config.GinkgoConfig.ParallelNode = cpu + 1
		config.GinkgoConfig.ParallelTotal = t.numCPU
		config.GinkgoConfig.SyncHost = server.Address()

		ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)

		writers[cpu] = newLogWriter(os.Stdout, cpu+1)

		cmd := t.cmd(ginkgoArgs, writers[cpu], cpu+1)

		server.RegisterAlive(cpu+1, func() bool {
			if cmd.ProcessState == nil {
				return true
			}
			return !cmd.ProcessState.Exited()
		})

		go t.run(cmd, completions)
	}

	res := PassingRunResult()

	for cpu := 0; cpu < t.numCPU; cpu++ {
		res = res.Merge(<-completions)
	}

	for _, writer := range writers {
		writer.Close()
	}

	os.Stdout.Sync()

	if t.cover || t.coverPkg != "" {
		t.combineCoverprofiles()
	}

	return res
}

func (t *TestRunner) runParallelGinkgoSuite() RunResult {
	result := make(chan bool)
	completions := make(chan RunResult)
	writers := make([]*logWriter, t.numCPU)
	reports := make([]*bytes.Buffer, t.numCPU)

	stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor)
	aggregator := remote.NewAggregator(t.numCPU, result, config.DefaultReporterConfig, stenographer)

	server, err := remote.NewServer(t.numCPU)
	if err != nil {
		panic("Failed to start parallel spec server")
	}
	server.RegisterReporters(aggregator)
	server.Start()
	defer server.Close()

	for cpu := 0; cpu < t.numCPU; cpu++ {
		config.GinkgoConfig.ParallelNode = cpu + 1
		config.GinkgoConfig.ParallelTotal = t.numCPU
		config.GinkgoConfig.SyncHost = server.Address()
		config.GinkgoConfig.StreamHost = server.Address()

		ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)

		reports[cpu] = &bytes.Buffer{}
		writers[cpu] = newLogWriter(reports[cpu], cpu+1)

		cmd := t.cmd(ginkgoArgs, writers[cpu], cpu+1)

		server.RegisterAlive(cpu+1, func() bool {
			if cmd.ProcessState == nil {
				return true
			}
			return !cmd.ProcessState.Exited()
		})

		go t.run(cmd, completions)
	}

	res := PassingRunResult()

	for cpu := 0; cpu < t.numCPU; cpu++ {
		res = res.Merge(<-completions)
	}

	//all test processes are done, at this point
	//we should be able to wait for the aggregator to tell us that it's done

	select {
	case <-result:
		fmt.Println("")
	case <-time.After(time.Second):
		//the aggregator never got back to us!  something must have gone wrong
		fmt.Println(`
	 -------------------------------------------------------------------
	|                                                                   |
	|  Ginkgo timed out waiting for all parallel nodes to report back!  |
	|                                                                   |
	 -------------------------------------------------------------------
`)

		os.Stdout.Sync()

		for _, writer := range writers {
			writer.Close()
		}

		for _, report := range reports {
			fmt.Print(report.String())
		}

		os.Stdout.Sync()
	}

	if t.cover || t.coverPkg != "" {
		t.combineCoverprofiles()
	}

	return res
}

func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
	args := []string{"--test.timeout=24h"}
	if t.cover || t.coverPkg != "" {
		coverprofile := "--test.coverprofile=" + t.Suite.PackageName + ".coverprofile"
		if t.numCPU > 1 {
			coverprofile = fmt.Sprintf("%s.%d", coverprofile, node)
		}
		args = append(args, coverprofile)
	}

	args = append(args, ginkgoArgs...)
	args = append(args, t.additionalArgs...)

	path := t.compilationTargetPath
	if t.Suite.Precompiled {
		path, _ = filepath.Abs(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.test", t.Suite.PackageName)))
	}

	cmd := exec.Command(path, args...)

	cmd.Dir = t.Suite.Path
	cmd.Stderr = stream
	cmd.Stdout = stream

	return cmd
}

func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
	var res RunResult

	defer func() {
		if completions != nil {
			completions <- res
		}
	}()

	err := cmd.Start()
	if err != nil {
		fmt.Printf("Failed to run test suite!\n\t%s", err.Error())
		return res
	}

	cmd.Wait()
	exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
	res.Passed = (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
	res.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)

	return res
}

func (t *TestRunner) combineCoverprofiles() {
	profiles := []string{}
	for cpu := 1; cpu <= t.numCPU; cpu++ {
		coverFile := fmt.Sprintf("%s.coverprofile.%d", t.Suite.PackageName, cpu)
		coverFile = filepath.Join(t.Suite.Path, coverFile)
		coverProfile, err := ioutil.ReadFile(coverFile)
		os.Remove(coverFile)

		if err == nil {
			profiles = append(profiles, string(coverProfile))
		}
	}

	if len(profiles) != t.numCPU {
		return
	}

	lines := map[string]int{}
	lineOrder := []string{}
	for i, coverProfile := range profiles {
		for _, line := range strings.Split(string(coverProfile), "\n")[1:] {
			if len(line) == 0 {
				continue
			}
			components := strings.Split(line, " ")
			count, _ := strconv.Atoi(components[len(components)-1])
			prefix := strings.Join(components[0:len(components)-1], " ")
			lines[prefix] += count
			if i == 0 {
				lineOrder = append(lineOrder, prefix)
			}
		}
	}

	output := []string{"mode: atomic"}
	for _, line := range lineOrder {
		output = append(output, fmt.Sprintf("%s %d", line, lines[line]))
	}
	finalOutput := strings.Join(output, "\n")
	ioutil.WriteFile(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.coverprofile", t.Suite.PackageName)), []byte(finalOutput), 0666)
}
<commit_msg>Fix misspelling<commit_after>package testrunner

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/ginkgo/testsuite"
	"github.com/onsi/ginkgo/internal/remote"
	"github.com/onsi/ginkgo/reporters/stenographer"
	"github.com/onsi/ginkgo/types"
)

type TestRunner struct {
	Suite testsuite.TestSuite

	compiled              bool
	compilationTargetPath string

	numCPU         int
	parallelStream bool
	race           bool
	cover          bool
	coverPkg       string
	tags           string
	additionalArgs []string
}

func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, race bool, cover bool, coverPkg string, tags string, additionalArgs []string) *TestRunner {
	runner := &TestRunner{
		Suite:          suite,
		numCPU:         numCPU,
		parallelStream: parallelStream,
		race:           race,
		cover:          cover,
		coverPkg:       coverPkg,
		tags:           tags,
		additionalArgs: additionalArgs,
	}

	if !suite.Precompiled {
		dir, err := ioutil.TempDir("", "ginkgo")
		if err != nil {
			panic(fmt.Sprintf("couldn't create temporary directory... might be time to rm -rf:\n%s", err.Error()))
		}
		runner.compilationTargetPath = filepath.Join(dir, suite.PackageName+".test")
	}

	return runner
}

func (t *TestRunner) Compile() error {
	return t.CompileTo(t.compilationTargetPath)
}

func (t *TestRunner) CompileTo(path string) error {
	if t.compiled {
		return nil
	}

	if t.Suite.Precompiled {
		return nil
	}

	args := []string{"test", "-c", "-i", "-o", path, t.Suite.Path}
	if t.race {
		args = append(args, "-race")
	}
	if t.cover || t.coverPkg != "" {
		args = append(args, "-cover", "-covermode=atomic")
	}
	if t.coverPkg != "" {
		args = append(args, fmt.Sprintf("-coverpkg=%s", t.coverPkg))
	}
	if t.tags != "" {
		args = append(args, fmt.Sprintf("-tags=%s", t.tags))
	}

	cmd := exec.Command("go", args...)

	output, err := cmd.CombinedOutput()

	if err != nil {
		fixedOutput := fixCompilationOutput(string(output), t.Suite.Path)
		if len(output) > 0 {
			return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, fixedOutput)
		}
		return fmt.Errorf("Failed to compile %s", t.Suite.PackageName)
	}

	if fileExists(path) == false {
		compiledFile := t.Suite.PackageName + ".test"
		if fileExists(compiledFile) {
			// seems like we are on an old go version that does not support the -o flag on go test
			// move the compiled test file to the desired location by hand
			err = os.Rename(compiledFile, path)
			if err != nil {
				// We cannot move the file, perhaps because the source and destination
				// are on different partitions. We can copy the file, however.
				err = copyFile(compiledFile, path)
				if err != nil {
					return fmt.Errorf("Failed to copy compiled file: %s", err)
				}
			}
		} else {
			return fmt.Errorf("Failed to compile %s: output file %q could not be found", t.Suite.PackageName, path)
		}
	}

	t.compiled = true

	return nil
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsNotExist(err) == false
}

// copyFile copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
func copyFile(src, dst string) error {
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}
	mode := srcInfo.Mode()

	in, err := os.Open(src)
	if err != nil {
		return err
	}

	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}

	defer func() {
		closeErr := out.Close()
		if err == nil {
			err = closeErr
		}
	}()

	_, err = io.Copy(out, in)
	if err != nil {
		return err
	}

	err = out.Sync()
	if err != nil {
		return err
	}

	return out.Chmod(mode)
}

/*
go test -c -i spits package.test out into the cwd. there's no way to change this.

to make sure it doesn't generate conflicting .test files in the cwd, Compile() must switch the cwd to the test package.

unfortunately, this causes go test's compile output to be expressed *relative to the test package* instead of the cwd.

this makes it hard to reason about what failed, and also prevents iterm's Cmd+click from working.

fixCompilationOutput..... rewrites the output to fix the paths.

yeah......
*/
func fixCompilationOutput(output string, relToPath string) string {
	re := regexp.MustCompile(`^(\S.*\.go)\:\d+\:`)
	lines := strings.Split(output, "\n")
	for i, line := range lines {
		indices := re.FindStringSubmatchIndex(line)
		if len(indices) == 0 {
			continue
		}

		path := line[indices[2]:indices[3]]
		path = filepath.Join(relToPath, path)
		lines[i] = path + line[indices[3]:]
	}
	return strings.Join(lines, "\n")
}

func (t *TestRunner) Run() RunResult {
	if t.Suite.IsGinkgo {
		if t.numCPU > 1 {
			if t.parallelStream {
				return t.runAndStreamParallelGinkgoSuite()
			} else {
				return t.runParallelGinkgoSuite()
			}
		} else {
			return t.runSerialGinkgoSuite()
		}
	} else {
		return t.runGoTestSuite()
	}
}

func (t *TestRunner) CleanUp() {
	if t.Suite.Precompiled {
		return
	}
	os.RemoveAll(filepath.Dir(t.compilationTargetPath))
}

func (t *TestRunner) runSerialGinkgoSuite() RunResult {
	ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
	return t.run(t.cmd(ginkgoArgs, os.Stdout, 1), nil)
}

func (t *TestRunner) runGoTestSuite() RunResult {
	return t.run(t.cmd([]string{"-test.v"}, os.Stdout, 1), nil)
}

func (t *TestRunner) runAndStreamParallelGinkgoSuite() RunResult {
	completions := make(chan RunResult)
	writers := make([]*logWriter, t.numCPU)

	server, err := remote.NewServer(t.numCPU)
	if err != nil {
		panic("Failed to start parallel spec server")
	}

	server.Start()
	defer server.Close()

	for cpu := 0; cpu < t.numCPU; cpu++ {
		config.GinkgoConfig.ParallelNode = cpu + 1
		config.GinkgoConfig.ParallelTotal = t.numCPU
		config.GinkgoConfig.SyncHost = server.Address()

		ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)

		writers[cpu] = newLogWriter(os.Stdout, cpu+1)

		cmd := t.cmd(ginkgoArgs, writers[cpu], cpu+1)

		server.RegisterAlive(cpu+1, func() bool {
			if cmd.ProcessState == nil {
				return true
			}
			return !cmd.ProcessState.Exited()
		})

		go t.run(cmd, completions)
	}

	res := PassingRunResult()

	for cpu := 0; cpu < t.numCPU; cpu++ {
		res = res.Merge(<-completions)
	}

	for _, writer := range writers {
		writer.Close()
	}

	os.Stdout.Sync()

	if t.cover || t.coverPkg != "" {
		t.combineCoverprofiles()
	}

	return res
}

func (t *TestRunner) runParallelGinkgoSuite() RunResult {
	result := make(chan bool)
	completions := make(chan RunResult)
	writers := make([]*logWriter, t.numCPU)
	reports := make([]*bytes.Buffer, t.numCPU)

	stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor)
	aggregator := remote.NewAggregator(t.numCPU, result, config.DefaultReporterConfig, stenographer)

	server, err := remote.NewServer(t.numCPU)
	if err != nil {
		panic("Failed to start parallel spec server")
	}
	server.RegisterReporters(aggregator)
	server.Start()
	defer server.Close()

	for cpu := 0; cpu < t.numCPU; cpu++ {
		config.GinkgoConfig.ParallelNode = cpu + 1
		config.GinkgoConfig.ParallelTotal = t.numCPU
		config.GinkgoConfig.SyncHost = server.Address()
		config.GinkgoConfig.StreamHost = server.Address()

		ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)

		reports[cpu] = &bytes.Buffer{}
		writers[cpu] = newLogWriter(reports[cpu], cpu+1)

		cmd := t.cmd(ginkgoArgs, writers[cpu], cpu+1)

		server.RegisterAlive(cpu+1, func() bool {
			if cmd.ProcessState == nil {
				return true
			}
			return !cmd.ProcessState.Exited()
		})

		go t.run(cmd, completions)
	}

	res := PassingRunResult()

	for cpu := 0; cpu < t.numCPU; cpu++ {
		res = res.Merge(<-completions)
	}

	//all test processes are done, at this point
	//we should be able to wait for the aggregator to tell us that it's done

	select {
	case <-result:
		fmt.Println("")
	case <-time.After(time.Second):
		//the aggregator never got back to us!  something must have gone wrong
		fmt.Println(`
	 -------------------------------------------------------------------
	|                                                                   |
	|  Ginkgo timed out waiting for all parallel nodes to report back!  |
	|                                                                   |
	 -------------------------------------------------------------------
`)

		os.Stdout.Sync()

		for _, writer := range writers {
			writer.Close()
		}

		for _, report := range reports {
			fmt.Print(report.String())
		}

		os.Stdout.Sync()
	}

	if t.cover || t.coverPkg != "" {
		t.combineCoverprofiles()
	}

	return res
}

func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
	args := []string{"--test.timeout=24h"}
	if t.cover || t.coverPkg != "" {
		coverprofile := "--test.coverprofile=" + t.Suite.PackageName + ".coverprofile"
		if t.numCPU > 1 {
			coverprofile = fmt.Sprintf("%s.%d", coverprofile, node)
		}
		args = append(args, coverprofile)
	}

	args = append(args, ginkgoArgs...)
	args = append(args, t.additionalArgs...)

	path := t.compilationTargetPath
	if t.Suite.Precompiled {
		path, _ = filepath.Abs(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.test", t.Suite.PackageName)))
	}

	cmd := exec.Command(path, args...)

	cmd.Dir = t.Suite.Path
	cmd.Stderr = stream
	cmd.Stdout = stream

	return cmd
}

func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
	var res RunResult

	defer func() {
		if completions != nil {
			completions <- res
		}
	}()

	err := cmd.Start()
	if err != nil {
		fmt.Printf("Failed to run test suite!\n\t%s", err.Error())
		return res
	}

	cmd.Wait()
	exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
	res.Passed = (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
	res.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)

	return res
}

func (t *TestRunner) combineCoverprofiles() {
	profiles := []string{}
	for cpu := 1; cpu <= t.numCPU; cpu++ {
		coverFile := fmt.Sprintf("%s.coverprofile.%d", t.Suite.PackageName, cpu)
		coverFile = filepath.Join(t.Suite.Path, coverFile)
		coverProfile, err := ioutil.ReadFile(coverFile)
		os.Remove(coverFile)

		if err == nil {
			profiles = append(profiles, string(coverProfile))
		}
	}

	if len(profiles) != t.numCPU {
		return
	}

	lines := map[string]int{}
	lineOrder := []string{}
	for i, coverProfile := range profiles {
		for _, line := range strings.Split(string(coverProfile), "\n")[1:] {
			if len(line) == 0 {
				continue
			}
			components := strings.Split(line, " ")
			count, _ := strconv.Atoi(components[len(components)-1])
			prefix := strings.Join(components[0:len(components)-1], " ")
			lines[prefix] += count
			if i == 0 {
				lineOrder = append(lineOrder, prefix)
			}
		}
	}

	output := []string{"mode: atomic"}
	for _, line := range lineOrder {
		output = append(output, fmt.Sprintf("%s %d", line, lines[line]))
	}
	finalOutput := strings.Join(output, "\n")
	ioutil.WriteFile(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.coverprofile", t.Suite.PackageName)), []byte(finalOutput), 0666)
}
<|endoftext|>


<commit_before>package csaupgrade

import (
	"encoding/json"
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
)

// Upgrades the Manager information for fields managed with CSA
// Prepares fields owned by `csaManager` for 'Update' operations for use now
// with the given `ssaManager` for `Apply` operations
//
// csaManager - Name of FieldManager formerly used for `Update` operations
// ssaManager - Name of FieldManager formerly used for `Apply` operations
// subResource - Name of subresource used for api calls or empty string for main resource
func UpgradeManagedFields(
	obj runtime.Object,
	csaManager string,
	ssaManager string,
	subResource string,
) (runtime.Object, error) {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return nil, fmt.Errorf("error accessing object metadata: %w", err)
	}

	managed, error := fieldmanager.DecodeManagedFields(accessor.GetManagedFields())
	if error != nil {
		return nil, fmt.Errorf("failed to decode managed fields: %w", error)
	}
	// If SSA manager  exists:
	//		find CSA manager of same version, union. discard the rest
	// Else SSA manager does not exist:
	//		find most recent CSA manager. convert to Apply operation

	ssaIdentifier, err := fieldmanager.BuildManagerIdentifier(&metav1.ManagedFieldsEntry{
		Manager:     ssaManager,
		Operation:   metav1.ManagedFieldsOperationApply,
		Subresource: subResource,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to build manager identifier for ssa manager")
	}

	ssaMan, ssaExists := managed.Fields()[ssaIdentifier]

	// Collect all relevant CSA managers before operating on them
	csaManagers := map[string]fieldpath.VersionedSet{}
	for name, entry := range managed.Fields() {
		if entry.Applied() {
			// Not interested in SSA managed fields entries
			continue
		}

		// Manager string is a JSON representation of encoded entry
		// Pull manager name and subresource from it
		encodedVersionedSet := &metav1.ManagedFieldsEntry{}
		err = json.Unmarshal([]byte(name), encodedVersionedSet)
		if err != nil {
			return nil, fmt.Errorf("error unmarshalling manager identifier %v: %v", name, err)
		}

		if encodedVersionedSet.Manager != csaManager ||
			encodedVersionedSet.Subresource != subResource {
			continue
		}

		csaManagers[name] = entry
	}

	if len(csaManagers) == 0 {
		return obj, nil
	}

	if ssaExists {
		for name, entry := range csaManagers {
			if entry.APIVersion() == ssaMan.APIVersion() {
				// Merge entries if they are compatible versions
				ssaMan = fieldpath.NewVersionedSet(
					ssaMan.Set().Union(entry.Set()),
					entry.APIVersion(),
					true,
				)
				managed.Fields()[ssaIdentifier] = ssaMan
			}

			// Discard entry in all cases:
			//	if it has the wrong version we discard since managed fields versions
			//		cannot be converted
			//	if it has the correct version its fields were moved into the
			//		ssaManager's fieldSet
			delete(managed.Fields(), name)
		}
	} else {
		// Loop through sorted CSA managers. Take the first one we care about
		firstName := ""
		for _, entry := range accessor.GetManagedFields() {
			if entry.Manager == csaManager &&
				entry.Subresource == subResource &&
				entry.Operation == metav1.ManagedFieldsOperationUpdate {

				if len(firstName) == 0 {
					ident, err := fieldmanager.BuildManagerIdentifier(&entry)
					if err != nil {
						return nil, fmt.Errorf("failed to build manager identifier: %w", err)
					}

					firstName = ident
					break
				}
			}
		}

		managed.Fields()[ssaIdentifier] = csaManagers[firstName]

		for name := range csaManagers {
			delete(managed.Fields(), name)
		}
	}

	now := metav1.Now()
	managed.Times()[ssaIdentifier] = &now

	copied := obj.DeepCopyObject()
	if err := fieldmanager.EncodeObjectManagedFields(copied, managed); err != nil {
		return nil, err
	}
	return copied, nil
}
<commit_msg>dont expose internal methods in implementatoin<commit_after>package csaupgrade

import (
	"bytes"
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
)

// Upgrades the Manager information for fields managed with CSA
// Prepares fields owned by `csaManager` for 'Update' operations for use now
// with the given `ssaManager` for `Apply` operations
//
// csaManager - Name of FieldManager formerly used for `Update` operations
// ssaManager - Name of FieldManager formerly used for `Apply` operations
// subResource - Name of subresource used for api calls or empty string for main resource
func UpgradeManagedFields(
	obj runtime.Object,
	csaManagerName string,
	ssaManagerName string,
	subResource string,
) (runtime.Object, error) {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return nil, fmt.Errorf("error accessing object metadata: %w", err)
	}

	// Create managed fields clone since we modify the values
	var managedFields []metav1.ManagedFieldsEntry
	managedFields = append(managedFields, accessor.GetManagedFields()...)

	// Locate SSA manager
	ssaManagerIndex, ssaManagerExists := findFirstIndex(managedFields,
		func(entry metav1.ManagedFieldsEntry) bool {
			return entry.Manager == ssaManagerName &&
				entry.Operation == metav1.ManagedFieldsOperationApply &&
				entry.Subresource == ""
		})

	if ssaManagerExists {
		ssaManager := managedFields[ssaManagerIndex]

		// find Update manager of same APIVersion, union ssa fields with it.
		// discard all other Update managers of the same name
		csaManagerIndex, csaManagerExists := findFirstIndex(managedFields,
			func(entry metav1.ManagedFieldsEntry) bool {
				return entry.Manager == csaManagerName &&
					entry.Operation == metav1.ManagedFieldsOperationUpdate &&
					entry.Subresource == "" &&
					entry.APIVersion == ssaManager.APIVersion
			})

		if csaManagerExists {
			csaManager := managedFields[csaManagerIndex]

			// Union the csa manager with the existing SSA manager
			ssaFieldSet, err := fieldsToSet(*ssaManager.FieldsV1)
			if err != nil {
				return nil, fmt.Errorf("failed to convert fields to set: %w", err)
			}

			csaFieldSet, err := fieldsToSet(*csaManager.FieldsV1)
			if err != nil {
				return nil, fmt.Errorf("failed to convert fields to set: %w", err)
			}

			combinedFieldSet := ssaFieldSet.Union(&csaFieldSet)
			combinedFieldSetEncoded, err := setToFields(*combinedFieldSet)
			if err != nil {
				return nil, fmt.Errorf("failed to encode field set: %w", err)
			}

			managedFields[ssaManagerIndex].FieldsV1 = &combinedFieldSetEncoded
		}
	} else {
		// SSA manager does not exist. Find the most recent matching CSA manager,
		// convert it to an SSA manager.
		//
		// (find first index, since managed fields are sorted so that most recent is
		//  first in the list)
		csaManagerIndex, csaManagerExists := findFirstIndex(managedFields, func(entry metav1.ManagedFieldsEntry) bool {
			return entry.Manager == csaManagerName && entry.Operation == metav1.ManagedFieldsOperationUpdate && entry.Subresource == ""
		})

		if !csaManagerExists {
			// There are no CSA managers that need to be converted. Nothing to do
			// Return early
			return obj, nil
		}

		// Convert the entry to apply operation
		managedFields[csaManagerIndex].Operation = metav1.ManagedFieldsOperationApply
		managedFields[csaManagerIndex].Manager = ssaManagerName
	}

	// Create version of managed fields which has no CSA managers with the given name
	filteredManagers := filter(managedFields, func(entry metav1.ManagedFieldsEntry) bool {
		return !(entry.Manager == csaManagerName &&
			entry.Operation == metav1.ManagedFieldsOperationUpdate &&
			entry.Subresource == "")
	})

	copied := obj.DeepCopyObject()
	copiedAccessor, err := meta.Accessor(copied)
	if err != nil {
		return nil, fmt.Errorf("failed to get meta accessor for copied object: %w", err)
	}
	copiedAccessor.SetManagedFields(filteredManagers)
	return copied, nil
}

func findFirstIndex[T any](
	collection []T,
	predicate func(T) bool,
) (int, bool) {
	for idx, entry := range collection {
		if predicate(entry) {
			return idx, true
		}
	}

	return -1, false
}

func filter[T any](
	collection []T,
	predicate func(T) bool,
) []T {
	result := make([]T, 0, len(collection))

	for _, value := range collection {
		if predicate(value) {
			result = append(result, value)
		}
	}

	if len(result) == 0 {
		return nil
	}

	return result
}

// FieldsToSet creates a set paths from an input trie of fields
func fieldsToSet(f metav1.FieldsV1) (s fieldpath.Set, err error) {
	err = s.FromJSON(bytes.NewReader(f.Raw))
	return s, err
}

// SetToFields creates a trie of fields from an input set of paths
func setToFields(s fieldpath.Set) (f metav1.FieldsV1, err error) {
	f.Raw, err = s.ToJSON()
	return f, err
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"sync"
)

// Shard is a partial piece of data in a block
// @todo Store CRC32 checksum in meta file to validate content of shard

type Shard struct {
	Id         []byte // Unique UUID
	BlockIndex uint   // Numeric index (from to 0 to number_of_data_shards+number_of_parity_shards), used to align the shards before a restore using Reed Solomon
	Parity     bool   // Is this real data or parity?

	// Block reference
	block *Block

	// Buffer mode
	bufferMode    ShardBufferMode
	bufferModeMux sync.RWMutex

	// Byte buffers
	// - ON READ: contents + file metadata + shard index + shard metadata
	// - ON WRITE (before flush): contains content
	contents       *bytes.Buffer // Actual byte buffer in-memory, is lazy loaded, use Contents() method to get
	contentsMux    sync.RWMutex
	contentsOffset uint32

	// File metadata, recovered from byte buffers on disk
	// @todo the FileMeta array should be a struct which implements the Bytes() and FromBytes() like ShardMeta and ShardIndex
	fileMeta    []*FileMeta
	fileMetaMux sync.RWMutex

	// Shard meta
	shardMeta *ShardMeta

	// Shard index
	shardIndex *ShardIndex
}

// Buffer mode
type ShardBufferMode uint

const (
	IdleShardBufferMode  ShardBufferMode = iota // 0
	ReadShardBufferMode                         // 1
	WriteShardBufferMode                        // 2
)

// Read contents
// - locking should be done in the buffer itself
func (this *Shard) Contents() *bytes.Buffer {
	// @todo read from disk
	if this.contents == nil {
		this.contents = bytes.NewBuffer(make([]byte, conf.ShardSizeInBytes))
	}
	return this.contents
}

// Persist
func (this *Shard) Persist() {
	// @todo better writing to files, guaranteeing no data corruption
	log.Infof("Persisting shard %s to disk", this.IdStr())
	path := this.FullPath()
	err := ioutil.WriteFile(path, this.Contents().Bytes(), conf.UnixFilePermissions)
	if err != nil {
		// @tood handle better
		panic(err)
	}
}

// Full path
func (this *Shard) FullPath() string {
	var infix string = ""
	if this.Parity {
		infix = ".parity"
	}
	return fmt.Sprintf("%s/s_%s%s.data", this.Block().FullPath(), this.IdStr(), infix)
}

// Block
func (this *Shard) Block() *Block {
	return this.block
}

// To string
func (this *Shard) IdStr() string {
	return uuidToString(this.Id)
}

// Set contents
func (this *Shard) SetContents(b *bytes.Buffer) {
	this.contentsMux.Lock()
	this.contents = b
	this.contentsMux.Unlock()
}

func newShard(b *Block) *Shard {
	return &Shard{
		contents:   nil,
		Id:         randomUuid(),
		block:      b,
		Parity:     false,
		shardMeta:  newShardMeta(),
		shardIndex: newShardIndex(),
	}
}
<commit_msg>More shrad modes<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"sync"
)

// Shard is a partial piece of data in a block
// @todo Store CRC32 checksum in meta file to validate content of shard

type Shard struct {
	Id         []byte // Unique UUID
	BlockIndex uint   // Numeric index (from to 0 to number_of_data_shards+number_of_parity_shards), used to align the shards before a restore using Reed Solomon
	Parity     bool   // Is this real data or parity?

	// Block reference
	block *Block

	// Buffer mode
	bufferMode    ShardBufferMode
	bufferModeMux sync.RWMutex

	// Byte buffers
	// - ON READ: contents + file metadata + shard index + shard metadata
	// - ON WRITE (before flush): contains content
	contents       *bytes.Buffer // Actual byte buffer in-memory, is lazy loaded, use Contents() method to get
	contentsMux    sync.RWMutex
	contentsOffset uint32

	// File metadata, recovered from byte buffers on disk
	// @todo the FileMeta array should be a struct which implements the Bytes() and FromBytes() like ShardMeta and ShardIndex
	fileMeta    []*FileMeta
	fileMetaMux sync.RWMutex

	// Shard meta
	shardMeta *ShardMeta

	// Shard index
	shardIndex *ShardIndex
}

// Buffer mode
type ShardBufferMode uint

const (
	IdleShardBufferMode  ShardBufferMode = iota // 0 = not doing anything
	ReadShardBufferMode                         // 1 = initial reading the shard from the bytes on disk into memory structures
	WriteShardBufferMode                        // 2 = writing into the shard
	FlushShardBufferMode                        // 3 = final process of writing the shard to bytes for the disk
	ReadyShardBufferMode                        // 4 = this is what an in-memory version looks like that is used for random access
)

// Read contents
// - locking should be done in the buffer itself
func (this *Shard) Contents() *bytes.Buffer {
	// @todo read from disk
	if this.contents == nil {
		this.contents = bytes.NewBuffer(make([]byte, conf.ShardSizeInBytes))
	}
	return this.contents
}

// Persist
func (this *Shard) Persist() {
	// @todo better writing to files, guaranteeing no data corruption
	log.Infof("Persisting shard %s to disk", this.IdStr())
	path := this.FullPath()
	err := ioutil.WriteFile(path, this.Contents().Bytes(), conf.UnixFilePermissions)
	if err != nil {
		// @tood handle better
		panic(err)
	}
}

// Full path
func (this *Shard) FullPath() string {
	var infix string = ""
	if this.Parity {
		infix = ".parity"
	}
	return fmt.Sprintf("%s/s_%s%s.data", this.Block().FullPath(), this.IdStr(), infix)
}

// Block
func (this *Shard) Block() *Block {
	return this.block
}

// To string
func (this *Shard) IdStr() string {
	return uuidToString(this.Id)
}

// Set contents
func (this *Shard) SetContents(b *bytes.Buffer) {
	this.contentsMux.Lock()
	this.contents = b
	this.contentsMux.Unlock()
}

func newShard(b *Block) *Shard {
	return &Shard{
		contents:   nil,
		Id:         randomUuid(),
		block:      b,
		Parity:     false,
		shardMeta:  newShardMeta(),
		shardIndex: newShardIndex(),
	}
}
<|endoftext|>


<commit_before>// package shell implements a remote API interface for a running ipfs daemon
package shell

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"

	cmds "github.com/ipfs/go-ipfs/commands"
	files "github.com/ipfs/go-ipfs/commands/files"
	http "github.com/ipfs/go-ipfs/commands/http"
	cc "github.com/ipfs/go-ipfs/core/commands"

	tar "github.com/ipfs/go-ipfs/thirdparty/tar"
)

type Shell struct {
	client http.Client
}

func NewShell(url string) *Shell {
	return &Shell{http.NewClient(url)}
}

type IdOutput struct {
	ID              string
	PublicKey       string
	Addresses       []string
	AgentVersion    string
	ProtocolVersion string
}

// ID gets information about a given peer.  Arguments:
//
// peer: peer.ID of the node to look up.  If no peer is specified,
//   return information about the local peer.
func (s *Shell) ID(peer ...string) (*IdOutput, error) {
	if len(peer) > 1 {
		return nil, fmt.Errorf("Too many peer arguments")
	}
	ropts, err := cc.Root.GetOptions([]string{"id"})
	if err != nil {
		return nil, err
	}

	req, err := cmds.NewRequest(append([]string{"id"}, peer...), nil, nil, nil, cc.IDCmd, ropts)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}

	reader, err := resp.Reader()
	if err != nil {
		return nil, err
	}

	decoder := json.NewDecoder(reader)
	out := new(IdOutput)
	err = decoder.Decode(out)
	if err != nil {
		return nil, err
	}

	return out, nil
}

// Cat the content at the given path
func (s *Shell) Cat(path string) (io.Reader, error) {
	ropts, err := cc.Root.GetOptions([]string{"cat"})
	if err != nil {
		return nil, err
	}

	req, err := cmds.NewRequest([]string{"cat", path}, nil, nil, nil, cc.CatCmd, ropts)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}

	return resp.Reader()
}

// Add a file to ipfs from the given reader, returns the hash of the added file
func (s *Shell) Add(r io.Reader) (string, error) {
	ropts, err := cc.Root.GetOptions([]string{"add"})
	if err != nil {
		return "", err
	}

	slf := files.NewSliceFile("", []files.File{files.NewReaderFile("", ioutil.NopCloser(r), nil)})

	req, err := cmds.NewRequest([]string{"add"}, nil, nil, slf, cc.AddCmd, ropts)
	if err != nil {
		return "", err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return "", err
	}
	if resp.Error() != nil {
		return "", resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return "", err
	}

	dec := json.NewDecoder(read)
	out := struct{ Hash string }{}
	err = dec.Decode(&out)
	if err != nil {
		return "", err
	}

	return out.Hash, nil
}

// AddDir adds a directory recursively with all of the files under it
func (s *Shell) AddDir(dir string) (string, error) {
	ropts, err := cc.Root.GetOptions([]string{"add"})
	if err != nil {
		return "", err
	}

	dfi, err := os.Open(dir)
	if err != nil {
		return "", err
	}

	sf, err := files.NewSerialFile(dir, dfi)
	if err != nil {
		return "", err
	}
	slf := files.NewSliceFile(dir, []files.File{sf})

	req, err := cmds.NewRequest([]string{"add"}, nil, nil, slf, cc.AddCmd, ropts)
	if err != nil {
		return "", err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return "", err
	}
	if resp.Error() != nil {
		return "", resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return "", err
	}

	dec := json.NewDecoder(read)
	out := struct{ Hash string }{}
	var final string
	for {
		err = dec.Decode(&out)
		if err != nil {
			if err == io.EOF {
				break
			}
			return "", err
		}
		final = out.Hash
	}

	if final == "" {
		return "", errors.New("no results received")
	}

	return out.Hash, nil
}

// List entries at the given path
func (s *Shell) List(path string) ([]cc.Link, error) {
	ropts, err := cc.Root.GetOptions([]string{"ls"})
	if err != nil {
		return nil, err
	}

	req, err := cmds.NewRequest([]string{"ls", path}, nil, nil, nil, cc.LsCmd, ropts)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}

	if resp.Error() != nil {
		return nil, resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return nil, err
	}

	dec := json.NewDecoder(read)
	out := struct{ Objects []cc.Object }{}
	err = dec.Decode(&out)
	if err != nil {
		return nil, err
	}

	return out.Objects[0].Links, nil
}

// Pin the given path
func (s *Shell) Pin(path string) error {
	ropts, err := cc.Root.GetOptions([]string{"pin", "add"})
	if err != nil {
		return err
	}
	pinadd := cc.PinCmd.Subcommands["add"]

	req, err := cmds.NewRequest([]string{"pin", "add", path}, nil, nil, nil, pinadd, ropts)
	if err != nil {
		return err
	}
	req.SetOption("r", true)

	resp, err := s.client.Send(req)
	if err != nil {
		return err
	}
	if resp.Error() != nil {
		return resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return err
	}

	out, err := ioutil.ReadAll(read)
	if err != nil {
		return err
	}

	fmt.Println(string(out))
	return nil
}

type PeerInfo struct {
	Addrs []string
	ID    string
}

func (s *Shell) FindPeer(peer string) (*PeerInfo, error) {
	ropts, err := cc.Root.GetOptions([]string{"dht", "findpeer"})
	if err != nil {
		return nil, err
	}
	fpeer := cc.DhtCmd.Subcommands["findpeer"]

	req, err := cmds.NewRequest([]string{"dht", "findpeer", peer}, nil, nil, nil, fpeer, ropts)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}
	if resp.Error() != nil {
		return nil, resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return nil, err
	}

	str := struct {
		Responses []PeerInfo
	}{}
	err = json.NewDecoder(read).Decode(&str)
	if err != nil {
		return nil, err
	}

	if len(str.Responses) == 0 {
		return nil, errors.New("peer not found")
	}

	return &str.Responses[0], nil
}

func (s *Shell) Refs(hash string, recursive bool) (<-chan string, error) {
	ropts, err := cc.Root.GetOptions([]string{"refs"})
	if err != nil {
		return nil, err
	}

	req, err := cmds.NewRequest([]string{"refs", hash}, nil, nil, nil, cc.RefsCmd, ropts)
	if err != nil {
		return nil, err
	}
	req.SetOption("r", recursive)

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}
	if resp.Error() != nil {
		return nil, resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return nil, err
	}

	out := make(chan string)
	go func() {
		scan := bufio.NewScanner(read)
		for scan.Scan() {
			if len(scan.Text()) > 0 {
				out <- scan.Text()
			}
		}
		close(out)
	}()

	return out, nil
}

func (s *Shell) Patch(root, action string, args ...string) (string, error) {
	ropts, err := cc.Root.GetOptions([]string{"object", "patch"})
	if err != nil {
		return "", err
	}

	patchcmd := cc.ObjectCmd.Subcommand("patch")

	cmdargs := append([]string{root, action}, args...)
	req, err := cmds.NewRequest([]string{"object", "patch"}, nil, cmdargs, nil, patchcmd, ropts)
	if err != nil {
		return "", err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return "", err
	}
	if resp.Error() != nil {
		return "", resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return "", err
	}

	dec := json.NewDecoder(read)
	var out map[string]interface{}
	err = dec.Decode(&out)
	if err != nil {
		return "", err
	}

	hash, ok := out["Hash"]
	if !ok {
		return "", errors.New("no Hash field in command response")
	}

	return hash.(string), nil
}

func (s *Shell) Get(hash, outdir string) error {
	ropts, err := cc.Root.GetOptions([]string{"get"})
	if err != nil {
		return err
	}

	req, err := cmds.NewRequest([]string{"get", hash}, nil, nil, nil, cc.GetCmd, ropts)
	if err != nil {
		return err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return err
	}
	if resp.Error() != nil {
		return resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return err
	}

	extractor := &tar.Extractor{Path: outdir}
	return extractor.Extract(read)
}

func (s *Shell) NewObject(template string) (string, error) {
	ropts, err := cc.Root.GetOptions([]string{"object", "new"})
	if err != nil {
		return "", err
	}

	newcmd := cc.ObjectCmd.Subcommand("new")

	args := []string{"object", "new"}
	if template != "" {
		args = append(args, template)
	}
	req, err := cmds.NewRequest(args, nil, nil, nil, newcmd, ropts)
	if err != nil {
		return "", err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return "", err
	}
	if resp.Error() != nil {
		return "", resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return "", err
	}

	dec := json.NewDecoder(read)
	var out map[string]interface{}
	err = dec.Decode(&out)
	if err != nil {
		return "", err
	}

	hash, ok := out["Hash"]
	if !ok {
		return "", errors.New("no Hash field in command response")
	}

	return hash.(string), nil
}
<commit_msg>Add resp.Error() checks to Shell.ID() and Shell.Cat()<commit_after>// package shell implements a remote API interface for a running ipfs daemon
package shell

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"

	cmds "github.com/ipfs/go-ipfs/commands"
	files "github.com/ipfs/go-ipfs/commands/files"
	http "github.com/ipfs/go-ipfs/commands/http"
	cc "github.com/ipfs/go-ipfs/core/commands"

	tar "github.com/ipfs/go-ipfs/thirdparty/tar"
)

type Shell struct {
	client http.Client
}

func NewShell(url string) *Shell {
	return &Shell{http.NewClient(url)}
}

type IdOutput struct {
	ID              string
	PublicKey       string
	Addresses       []string
	AgentVersion    string
	ProtocolVersion string
}

// ID gets information about a given peer.  Arguments:
//
// peer: peer.ID of the node to look up.  If no peer is specified,
//   return information about the local peer.
func (s *Shell) ID(peer ...string) (*IdOutput, error) {
	if len(peer) > 1 {
		return nil, fmt.Errorf("Too many peer arguments")
	}
	ropts, err := cc.Root.GetOptions([]string{"id"})
	if err != nil {
		return nil, err
	}

	req, err := cmds.NewRequest(append([]string{"id"}, peer...), nil, nil, nil, cc.IDCmd, ropts)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}
	if resp.Error() != nil {
		return nil, resp.Error()
	}

	reader, err := resp.Reader()
	if err != nil {
		return nil, err
	}

	decoder := json.NewDecoder(reader)
	out := new(IdOutput)
	err = decoder.Decode(out)
	if err != nil {
		return nil, err
	}

	return out, nil
}

// Cat the content at the given path
func (s *Shell) Cat(path string) (io.Reader, error) {
	ropts, err := cc.Root.GetOptions([]string{"cat"})
	if err != nil {
		return nil, err
	}

	req, err := cmds.NewRequest([]string{"cat", path}, nil, nil, nil, cc.CatCmd, ropts)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}
	if resp.Error() != nil {
		return nil, resp.Error()
	}

	return resp.Reader()
}

// Add a file to ipfs from the given reader, returns the hash of the added file
func (s *Shell) Add(r io.Reader) (string, error) {
	ropts, err := cc.Root.GetOptions([]string{"add"})
	if err != nil {
		return "", err
	}

	slf := files.NewSliceFile("", []files.File{files.NewReaderFile("", ioutil.NopCloser(r), nil)})

	req, err := cmds.NewRequest([]string{"add"}, nil, nil, slf, cc.AddCmd, ropts)
	if err != nil {
		return "", err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return "", err
	}
	if resp.Error() != nil {
		return "", resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return "", err
	}

	dec := json.NewDecoder(read)
	out := struct{ Hash string }{}
	err = dec.Decode(&out)
	if err != nil {
		return "", err
	}

	return out.Hash, nil
}

// AddDir adds a directory recursively with all of the files under it
func (s *Shell) AddDir(dir string) (string, error) {
	ropts, err := cc.Root.GetOptions([]string{"add"})
	if err != nil {
		return "", err
	}

	dfi, err := os.Open(dir)
	if err != nil {
		return "", err
	}

	sf, err := files.NewSerialFile(dir, dfi)
	if err != nil {
		return "", err
	}
	slf := files.NewSliceFile(dir, []files.File{sf})

	req, err := cmds.NewRequest([]string{"add"}, nil, nil, slf, cc.AddCmd, ropts)
	if err != nil {
		return "", err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return "", err
	}
	if resp.Error() != nil {
		return "", resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return "", err
	}

	dec := json.NewDecoder(read)
	out := struct{ Hash string }{}
	var final string
	for {
		err = dec.Decode(&out)
		if err != nil {
			if err == io.EOF {
				break
			}
			return "", err
		}
		final = out.Hash
	}

	if final == "" {
		return "", errors.New("no results received")
	}

	return out.Hash, nil
}

// List entries at the given path
func (s *Shell) List(path string) ([]cc.Link, error) {
	ropts, err := cc.Root.GetOptions([]string{"ls"})
	if err != nil {
		return nil, err
	}

	req, err := cmds.NewRequest([]string{"ls", path}, nil, nil, nil, cc.LsCmd, ropts)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}

	if resp.Error() != nil {
		return nil, resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return nil, err
	}

	dec := json.NewDecoder(read)
	out := struct{ Objects []cc.Object }{}
	err = dec.Decode(&out)
	if err != nil {
		return nil, err
	}

	return out.Objects[0].Links, nil
}

// Pin the given path
func (s *Shell) Pin(path string) error {
	ropts, err := cc.Root.GetOptions([]string{"pin", "add"})
	if err != nil {
		return err
	}
	pinadd := cc.PinCmd.Subcommands["add"]

	req, err := cmds.NewRequest([]string{"pin", "add", path}, nil, nil, nil, pinadd, ropts)
	if err != nil {
		return err
	}
	req.SetOption("r", true)

	resp, err := s.client.Send(req)
	if err != nil {
		return err
	}
	if resp.Error() != nil {
		return resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return err
	}

	out, err := ioutil.ReadAll(read)
	if err != nil {
		return err
	}

	fmt.Println(string(out))
	return nil
}

type PeerInfo struct {
	Addrs []string
	ID    string
}

func (s *Shell) FindPeer(peer string) (*PeerInfo, error) {
	ropts, err := cc.Root.GetOptions([]string{"dht", "findpeer"})
	if err != nil {
		return nil, err
	}
	fpeer := cc.DhtCmd.Subcommands["findpeer"]

	req, err := cmds.NewRequest([]string{"dht", "findpeer", peer}, nil, nil, nil, fpeer, ropts)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}
	if resp.Error() != nil {
		return nil, resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return nil, err
	}

	str := struct {
		Responses []PeerInfo
	}{}
	err = json.NewDecoder(read).Decode(&str)
	if err != nil {
		return nil, err
	}

	if len(str.Responses) == 0 {
		return nil, errors.New("peer not found")
	}

	return &str.Responses[0], nil
}

func (s *Shell) Refs(hash string, recursive bool) (<-chan string, error) {
	ropts, err := cc.Root.GetOptions([]string{"refs"})
	if err != nil {
		return nil, err
	}

	req, err := cmds.NewRequest([]string{"refs", hash}, nil, nil, nil, cc.RefsCmd, ropts)
	if err != nil {
		return nil, err
	}
	req.SetOption("r", recursive)

	resp, err := s.client.Send(req)
	if err != nil {
		return nil, err
	}
	if resp.Error() != nil {
		return nil, resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return nil, err
	}

	out := make(chan string)
	go func() {
		scan := bufio.NewScanner(read)
		for scan.Scan() {
			if len(scan.Text()) > 0 {
				out <- scan.Text()
			}
		}
		close(out)
	}()

	return out, nil
}

func (s *Shell) Patch(root, action string, args ...string) (string, error) {
	ropts, err := cc.Root.GetOptions([]string{"object", "patch"})
	if err != nil {
		return "", err
	}

	patchcmd := cc.ObjectCmd.Subcommand("patch")

	cmdargs := append([]string{root, action}, args...)
	req, err := cmds.NewRequest([]string{"object", "patch"}, nil, cmdargs, nil, patchcmd, ropts)
	if err != nil {
		return "", err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return "", err
	}
	if resp.Error() != nil {
		return "", resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return "", err
	}

	dec := json.NewDecoder(read)
	var out map[string]interface{}
	err = dec.Decode(&out)
	if err != nil {
		return "", err
	}

	hash, ok := out["Hash"]
	if !ok {
		return "", errors.New("no Hash field in command response")
	}

	return hash.(string), nil
}

func (s *Shell) Get(hash, outdir string) error {
	ropts, err := cc.Root.GetOptions([]string{"get"})
	if err != nil {
		return err
	}

	req, err := cmds.NewRequest([]string{"get", hash}, nil, nil, nil, cc.GetCmd, ropts)
	if err != nil {
		return err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return err
	}
	if resp.Error() != nil {
		return resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return err
	}

	extractor := &tar.Extractor{Path: outdir}
	return extractor.Extract(read)
}

func (s *Shell) NewObject(template string) (string, error) {
	ropts, err := cc.Root.GetOptions([]string{"object", "new"})
	if err != nil {
		return "", err
	}

	newcmd := cc.ObjectCmd.Subcommand("new")

	args := []string{"object", "new"}
	if template != "" {
		args = append(args, template)
	}
	req, err := cmds.NewRequest(args, nil, nil, nil, newcmd, ropts)
	if err != nil {
		return "", err
	}

	resp, err := s.client.Send(req)
	if err != nil {
		return "", err
	}
	if resp.Error() != nil {
		return "", resp.Error()
	}

	read, err := resp.Reader()
	if err != nil {
		return "", err
	}

	dec := json.NewDecoder(read)
	var out map[string]interface{}
	err = dec.Decode(&out)
	if err != nil {
		return "", err
	}

	hash, ok := out["Hash"]
	if !ok {
		return "", errors.New("no Hash field in command response")
	}

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


<commit_before>package pwd_hash

import (
	"crypto/md5"
	"crypto/sha1"
	"hash"
	"encoding/hex"
)

const Size = 16
const BlockSize = 64

type digest struct {
	bytes []byte
}

func New() hash.Hash {
	d := new(digest)
	d.Reset()
	return d
}

func (d *digest) Reset() {
	d.bytes = []byte{}
}

func (d *digest) Size() int {
	return Size
}

func (d *digest) BlockSize() int {
	return BlockSize
}

func (d *digest) Sum(b []byte) []byte {
	return Sum(d.bytes)
}

// Implement writer interface
func (d *digest) Write(p []byte) (n int, err error) {
	d.bytes = append(d.bytes, p...)

	n = len(p)
	err = nil
	return
}

func Sum(in []byte) []byte {
	sha1 := sha1.Sum(in)
	md5 := md5.Sum(sha1[:])

	return md5[:]
}

func HashPassword(password string) string {
	return hex.EncodeToString(Sum([]byte(password)))
}<commit_msg>Reformat file.<commit_after>package pwd_hash

import (
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"hash"
)

const Size = 16
const BlockSize = 64

type digest struct {
	bytes []byte
}

func New() hash.Hash {
	d := new(digest)
	d.Reset()
	return d
}

func (d *digest) Reset() {
	d.bytes = []byte{}
}

func (d *digest) Size() int {
	return Size
}

func (d *digest) BlockSize() int {
	return BlockSize
}

func (d *digest) Sum(b []byte) []byte {
	return Sum(d.bytes)
}

// Implement writer interface
func (d *digest) Write(p []byte) (n int, err error) {
	d.bytes = append(d.bytes, p...)

	n = len(p)
	err = nil
	return
}

func Sum(in []byte) []byte {
	sha1 := sha1.Sum(in)
	md5 := md5.Sum(sha1[:])

	return md5[:]
}

func HashPassword(password string) string {
	return hex.EncodeToString(Sum([]byte(password)))
}
<|endoftext|>


<commit_before>package i2c

import (
	"time"

	"github.com/hybridgroup/gobot"
)

const (
	REG_RED   = 0x04
	REG_GREEN = 0x03
	REG_BLUE  = 0x02

	LCD_CLEARDISPLAY        = 0x01
	LCD_RETURNHOME          = 0x02
	LCD_ENTRYMODESET        = 0x04
	LCD_DISPLAYCONTROL      = 0x08
	LCD_CURSORSHIFT         = 0x10
	LCD_FUNCTIONSET         = 0x20
	LCD_SETCGRAMADDR        = 0x40
	LCD_SETDDRAMADDR        = 0x80
	LCD_ENTRYRIGHT          = 0x00
	LCD_ENTRYLEFT           = 0x02
	LCD_ENTRYSHIFTINCREMENT = 0x01
	LCD_ENTRYSHIFTDECREMENT = 0x00
	LCD_DISPLAYON           = 0x04
	LCD_DISPLAYOFF          = 0x00
	LCD_CURSORON            = 0x02
	LCD_CURSOROFF           = 0x00
	LCD_BLINKON             = 0x01
	LCD_BLINKOFF            = 0x00
	LCD_DISPLAYMOVE         = 0x08
	LCD_CURSORMOVE          = 0x00
	LCD_MOVERIGHT           = 0x04
	LCD_MOVELEFT            = 0x00
	LCD_2LINE               = 0x08
)

var _ gobot.Driver = (*JHD1313M1Driver)(nil)

type JHD1313M1Driver struct {
	name       string
	connection I2c
	lcdAddress int
	rgbAddress int
}

// NewJHD1313M1Driver creates a new driver with specified name and i2c interface
func NewJHD1313M1Driver(a I2c, name string) *JHD1313M1Driver {
	return &JHD1313M1Driver{
		name:       name,
		connection: a,
		lcdAddress: 0x3E,
		rgbAddress: 0x62,
	}
}

func (h *JHD1313M1Driver) Name() string                 { return h.name }
func (h *JHD1313M1Driver) Connection() gobot.Connection { return h.connection.(gobot.Connection) }

func (h *JHD1313M1Driver) Start() (errs []error) {
	cmd := uint8(0)
	if err := h.connection.I2cStart(h.lcdAddress); err != nil {
		return []error{err}
	}

	if err := h.connection.I2cStart(h.rgbAddress); err != nil {
		return []error{err}
	}

	cmd |= LCD_2LINE

	<-time.After(30 * time.Millisecond)

	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_FUNCTIONSET | cmd}); err != nil {
		return []error{err}
	}
	<-time.After(40 * time.Nanosecond)

	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_FUNCTIONSET | cmd}); err != nil {
		return []error{err}
	}
	<-time.After(150 * time.Microsecond)
	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_FUNCTIONSET | cmd}); err != nil {
		return []error{err}
	}
	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_FUNCTIONSET | cmd}); err != nil {
		return []error{err}
	}
	cmd |= LCD_DISPLAYON

	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_DISPLAYCONTROL | cmd}); err != nil {
		return []error{err}
	}

	h.Clear()

	cmd |= LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT

	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_ENTRYMODESET | cmd}); err != nil {
		return []error{err}
	}

	if err := h.setReg(0, 1); err != nil {
		return []error{err}
	}
	if err := h.setReg(1, 0); err != nil {
		return []error{err}
	}
	if err := h.setReg(0x08, 0xAA); err != nil {
		return []error{err}
	}

	if err := h.SetRGB(255, 255, 255); err != nil {
		return []error{err}
	}

	return
}

func (h *JHD1313M1Driver) SetRGB(r, g, b int) (err error) {
	if err = h.setReg(REG_RED, r); err != nil {
		return
	}
	if err = h.setReg(REG_GREEN, g); err != nil {
		return
	}
	return h.setReg(REG_BLUE, b)
}

func (h *JHD1313M1Driver) setReg(command int, data int) (err error) {
	if err = h.connection.I2cWrite(h.rgbAddress, []byte{byte(command), byte(data)}); err != nil {
		return
	}
	return
}

func (h *JHD1313M1Driver) Clear() (err error) {
	return h.command([]byte{LCD_CLEARDISPLAY})
}

func (h *JHD1313M1Driver) Home() (err error) {
	return h.command([]byte{LCD_RETURNHOME})
}

func (h *JHD1313M1Driver) Write(message string) (err error) {
	for _, val := range message {
		if err = h.connection.I2cWrite(h.lcdAddress, []byte{0x40, byte(val)}); err != nil {
			break
		}
	}
	return
}

func (h *JHD1313M1Driver) Halt() (errs []error) { return }

func (h *JHD1313M1Driver) command(buf []byte) (err error) {
	return h.connection.I2cWrite(h.lcdAddress, append([]byte{0x80}, buf...))
}
<commit_msg>Adding 2ms after Clear() and Home() to avoid issues + refactoring<commit_after>package i2c

import (
	"time"

	"github.com/hybridgroup/gobot"
)

const (
	REG_RED   = 0x04
	REG_GREEN = 0x03
	REG_BLUE  = 0x02

	LCD_CLEARDISPLAY        = 0x01
	LCD_RETURNHOME          = 0x02
	LCD_ENTRYMODESET        = 0x04
	LCD_DISPLAYCONTROL      = 0x08
	LCD_CURSORSHIFT         = 0x10
	LCD_FUNCTIONSET         = 0x20
	LCD_SETCGRAMADDR        = 0x40
	LCD_SETDDRAMADDR        = 0x80
	LCD_ENTRYRIGHT          = 0x00
	LCD_ENTRYLEFT           = 0x02
	LCD_ENTRYSHIFTINCREMENT = 0x01
	LCD_ENTRYSHIFTDECREMENT = 0x00
	LCD_DISPLAYON           = 0x04
	LCD_DISPLAYOFF          = 0x00
	LCD_CURSORON            = 0x02
	LCD_CURSOROFF           = 0x00
	LCD_BLINKON             = 0x01
	LCD_BLINKOFF            = 0x00
	LCD_DISPLAYMOVE         = 0x08
	LCD_CURSORMOVE          = 0x00
	LCD_MOVERIGHT           = 0x04
	LCD_MOVELEFT            = 0x00
	LCD_2LINE               = 0x08
)

var _ gobot.Driver = (*JHD1313M1Driver)(nil)

// JHD1313M1Driver is a driver for the Jhd1313m1 LCD display which has two i2c addreses,
// one belongs to a controller and the other controls solely the backlight.
// This module was tested with the Seed Grove LCD RGB Backlight v2.0 display which requires 5V to operate.
// http://www.seeedstudio.com/wiki/Grove_-_LCD_RGB_Backlight
type JHD1313M1Driver struct {
	name       string
	connection I2c
	lcdAddress int
	rgbAddress int
}

// NewJHD1313M1Driver creates a new driver with specified name and i2c interface.
func NewJHD1313M1Driver(a I2c, name string) *JHD1313M1Driver {
	return &JHD1313M1Driver{
		name:       name,
		connection: a,
		lcdAddress: 0x3E,
		rgbAddress: 0x62,
	}
}

// Name returns the name the JHD1313M1 Driver was given when created.
func (h *JHD1313M1Driver) Name() string { return h.name }

// Connection returns the driver connection to the device.
func (h *JHD1313M1Driver) Connection() gobot.Connection {
	return h.connection.(gobot.Connection)
}

// Start starts the backlit and the screen and initializes the states.
func (h *JHD1313M1Driver) Start() []error {
	cmd := uint8(0)
	if err := h.connection.I2cStart(h.lcdAddress); err != nil {
		return []error{err}
	}

	if err := h.connection.I2cStart(h.rgbAddress); err != nil {
		return []error{err}
	}

	cmd |= LCD_2LINE

	<-time.After(30 * time.Millisecond)

	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_FUNCTIONSET | cmd}); err != nil {
		return []error{err}
	}
	<-time.After(40 * time.Nanosecond)

	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_FUNCTIONSET | cmd}); err != nil {
		return []error{err}
	}
	<-time.After(150 * time.Microsecond)
	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_FUNCTIONSET | cmd}); err != nil {
		return []error{err}
	}
	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_FUNCTIONSET | cmd}); err != nil {
		return []error{err}
	}
	cmd |= LCD_DISPLAYON

	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_DISPLAYCONTROL | cmd}); err != nil {
		return []error{err}
	}

	h.Clear()

	cmd |= LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT

	if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x80, LCD_ENTRYMODESET | cmd}); err != nil {
		return []error{err}
	}

	if err := h.setReg(0, 1); err != nil {
		return []error{err}
	}
	if err := h.setReg(1, 0); err != nil {
		return []error{err}
	}
	if err := h.setReg(0x08, 0xAA); err != nil {
		return []error{err}
	}

	if err := h.SetRGB(255, 255, 255); err != nil {
		return []error{err}
	}

	return nil
}

// SetRGB sets the Red Green Blue value of backlit.
func (h *JHD1313M1Driver) SetRGB(r, g, b int) error {
	if err := h.setReg(REG_RED, r); err != nil {
		return err
	}
	if err := h.setReg(REG_GREEN, g); err != nil {
		return err
	}
	return h.setReg(REG_BLUE, b)
}

// Clear clears the text on the lCD display.
func (h *JHD1313M1Driver) Clear() error {
	err := h.command([]byte{LCD_CLEARDISPLAY})
	<-time.After(2 * time.Millisecond)
	return err
}

// Home sets the cursor to the origin position on the display.
func (h *JHD1313M1Driver) Home() error {
	err := h.command([]byte{LCD_RETURNHOME})
	<-time.After(2 * time.Millisecond)
	return err
}

// Write displays the passed message on the screen.
func (h *JHD1313M1Driver) Write(message string) error {
	for _, val := range message {
		if err := h.connection.I2cWrite(h.lcdAddress, []byte{0x40, byte(val)}); err != nil {
			return err
		}
	}
	return nil
}

// Halt is a noop function.
func (h *JHD1313M1Driver) Halt() []error { return nil }

func (h *JHD1313M1Driver) setReg(command int, data int) error {
	if err := h.connection.I2cWrite(h.rgbAddress, []byte{byte(command), byte(data)}); err != nil {
		return err
	}
	return nil
}

func (h *JHD1313M1Driver) command(buf []byte) error {
	return h.connection.I2cWrite(h.lcdAddress, append([]byte{0x80}, buf...))
}
<|endoftext|>


<commit_before>package sphero

import (
	"fmt"
	"time"

	"github.com/hybridgroup/gobot"
)

type packet struct {
	header   []uint8
	body     []uint8
	checksum uint8
}

type SpheroDriver struct {
	gobot.Driver
	seq             uint8
	asyncResponse   [][]uint8
	syncResponse    [][]uint8
	packetChannel   chan *packet
	responseChannel chan []uint8
}

func NewSpheroDriver(a *SpheroAdaptor, name string) *SpheroDriver {
	s := &SpheroDriver{
		Driver: *gobot.NewDriver(
			name,
			"SpheroDriver",
			a,
		),
		packetChannel:   make(chan *packet, 1024),
		responseChannel: make(chan []uint8, 1024),
	}

	s.AddEvent("collision")
	s.AddCommand("SetRGB", func(params map[string]interface{}) interface{} {
		r := uint8(params["r"].(float64))
		g := uint8(params["g"].(float64))
		b := uint8(params["b"].(float64))
		s.SetRGB(r, g, b)
		return nil
	})

	s.AddCommand("Roll", func(params map[string]interface{}) interface{} {
		speed := uint8(params["speed"].(float64))
		heading := uint16(params["heading"].(float64))
		s.Roll(speed, heading)
		return nil
	})

	s.AddCommand("Stop", func(params map[string]interface{}) interface{} {
		s.Stop()
		return nil
	})

	s.AddCommand("GetRGB", func(params map[string]interface{}) interface{} {
		return s.GetRGB()
	})

	s.AddCommand("SetBackLED", func(params map[string]interface{}) interface{} {
		level := uint8(params["level"].(float64))
		s.SetBackLED(level)
		return nil
	})

	s.AddCommand("SetHeading", func(params map[string]interface{}) interface{} {
		heading := uint16(params["heading"].(float64))
		s.SetHeading(heading)
		return nil
	})
	s.AddCommand("SetStabilization", func(params map[string]interface{}) interface{} {
		on := params["heading"].(bool)
		s.SetStabilization(on)
		return nil
	})

	return s
}

func (s *SpheroDriver) adaptor() *SpheroAdaptor {
	return s.Adaptor().(*SpheroAdaptor)
}

func (s *SpheroDriver) Init() bool {
	return true
}

func (s *SpheroDriver) Start() bool {
	go func() {
		for {
			packet := <-s.packetChannel
			s.write(packet)
		}
	}()

	go func() {
		for {
			response := <-s.responseChannel
			s.syncResponse = append(s.syncResponse, response)
		}
	}()

	go func() {
		for {
			header := s.readHeader()
			if header != nil && len(header) != 0 {
				body := s.readBody(header[4])
				if header[1] == 0xFE {
					async := append(header, body...)
					s.asyncResponse = append(s.asyncResponse, async)
				} else {
					s.responseChannel <- append(header, body...)
				}
			}
		}
	}()

	go func() {
		for {
			var evt []uint8
			for len(s.asyncResponse) != 0 {
				evt, s.asyncResponse = s.asyncResponse[len(s.asyncResponse)-1], s.asyncResponse[:len(s.asyncResponse)-1]
				if evt[2] == 0x07 {
					s.handleCollisionDetected(evt)
				}
			}
			time.Sleep(100 * time.Millisecond)
		}
	}()

	s.configureCollisionDetection()

	return true
}

func (s *SpheroDriver) Halt() bool {
	gobot.Every(10*time.Millisecond, func() {
		s.Stop()
	})
	time.Sleep(1 * time.Second)
	return true
}

func (s *SpheroDriver) SetRGB(r uint8, g uint8, b uint8) {
	s.packetChannel <- s.craftPacket([]uint8{r, g, b, 0x01}, 0x20)
}

func (s *SpheroDriver) GetRGB() []uint8 {
	return s.getSyncResponse(s.craftPacket([]uint8{}, 0x22))
}

func (s *SpheroDriver) SetBackLED(level uint8) {
	s.packetChannel <- s.craftPacket([]uint8{level}, 0x21)
}

func (s *SpheroDriver) SetHeading(heading uint16) {
	s.packetChannel <- s.craftPacket([]uint8{uint8(heading >> 8), uint8(heading & 0xFF)}, 0x01)
}

func (s *SpheroDriver) SetStabilization(on bool) {
	b := uint8(0x01)
	if on == false {
		b = 0x00
	}
	s.packetChannel <- s.craftPacket([]uint8{b}, 0x02)
}

func (s *SpheroDriver) Roll(speed uint8, heading uint16) {
	s.packetChannel <- s.craftPacket([]uint8{speed, uint8(heading >> 8), uint8(heading & 0xFF), 0x01}, 0x30)
}

func (s *SpheroDriver) Stop() {
	s.Roll(0, 0)
}

func (s *SpheroDriver) configureCollisionDetection() {
	s.packetChannel <- s.craftPacket([]uint8{0x01, 0x40, 0x40, 0x50, 0x50, 0x60}, 0x12)
}

func (s *SpheroDriver) handleCollisionDetected(data []uint8) {
	gobot.Publish(s.Event("collision"), data)
}

func (s *SpheroDriver) getSyncResponse(packet *packet) []byte {
	s.packetChannel <- packet
	for i := 0; i < 500; i++ {
		for key := range s.syncResponse {
			if s.syncResponse[key][3] == packet.header[4] && len(s.syncResponse[key]) > 6 {
				var response []byte
				response, s.syncResponse = s.syncResponse[len(s.syncResponse)-1], s.syncResponse[:len(s.syncResponse)-1]
				return response
			}
		}
		time.Sleep(10 * time.Microsecond)
	}

	return []byte{}
}

func (s *SpheroDriver) craftPacket(body []uint8, cid byte) *packet {
	packet := new(packet)
	packet.body = body
	dlen := len(packet.body) + 1
	packet.header = []uint8{0xFF, 0xFF, 0x02, cid, s.seq, uint8(dlen)}
	packet.checksum = s.calculateChecksum(packet)
	return packet
}

func (s *SpheroDriver) write(packet *packet) {
	buf := append(packet.header, packet.body...)
	buf = append(buf, packet.checksum)
	length, err := s.adaptor().sp.Write(buf)
	if err != nil {
		fmt.Println(s.Name, err)
		s.adaptor().Disconnect()
		fmt.Println("Reconnecting to SpheroDriver...")
		s.adaptor().Connect()
		return
	} else if length != len(buf) {
		fmt.Println("Not enough bytes written", s.Name)
	}
	s.seq++
}

func (s *SpheroDriver) calculateChecksum(packet *packet) uint8 {
	buf := append(packet.header, packet.body...)
	buf = buf[2:]
	var calculatedChecksum uint16
	for i := range buf {
		calculatedChecksum += uint16(buf[i])
	}
	return uint8(^(calculatedChecksum % 256))
}

func (s *SpheroDriver) readHeader() []uint8 {
	data := s.readNextChunk(5)
	if data == nil {
		return nil
	}
	return data
}

func (s *SpheroDriver) readBody(length uint8) []uint8 {
	data := s.readNextChunk(length)
	if data == nil {
		return nil
	}
	return data
}

func (s *SpheroDriver) readNextChunk(length uint8) []uint8 {
	time.Sleep(1000 * time.Microsecond)
	var read = make([]uint8, int(length))
	l, err := s.adaptor().sp.Read(read[:])
	if err != nil || length != uint8(l) {
		return nil
	}
	return read
}
<commit_msg>enable stop on disconnect via sphero api<commit_after>package sphero

import (
	"fmt"
	"time"

	"github.com/hybridgroup/gobot"
)

type packet struct {
	header   []uint8
	body     []uint8
	checksum uint8
}

type SpheroDriver struct {
	gobot.Driver
	seq             uint8
	asyncResponse   [][]uint8
	syncResponse    [][]uint8
	packetChannel   chan *packet
	responseChannel chan []uint8
}

func NewSpheroDriver(a *SpheroAdaptor, name string) *SpheroDriver {
	s := &SpheroDriver{
		Driver: *gobot.NewDriver(
			name,
			"SpheroDriver",
			a,
		),
		packetChannel:   make(chan *packet, 1024),
		responseChannel: make(chan []uint8, 1024),
	}

	s.AddEvent("collision")
	s.AddCommand("SetRGB", func(params map[string]interface{}) interface{} {
		r := uint8(params["r"].(float64))
		g := uint8(params["g"].(float64))
		b := uint8(params["b"].(float64))
		s.SetRGB(r, g, b)
		return nil
	})

	s.AddCommand("Roll", func(params map[string]interface{}) interface{} {
		speed := uint8(params["speed"].(float64))
		heading := uint16(params["heading"].(float64))
		s.Roll(speed, heading)
		return nil
	})

	s.AddCommand("Stop", func(params map[string]interface{}) interface{} {
		s.Stop()
		return nil
	})

	s.AddCommand("GetRGB", func(params map[string]interface{}) interface{} {
		return s.GetRGB()
	})

	s.AddCommand("SetBackLED", func(params map[string]interface{}) interface{} {
		level := uint8(params["level"].(float64))
		s.SetBackLED(level)
		return nil
	})

	s.AddCommand("SetHeading", func(params map[string]interface{}) interface{} {
		heading := uint16(params["heading"].(float64))
		s.SetHeading(heading)
		return nil
	})
	s.AddCommand("SetStabilization", func(params map[string]interface{}) interface{} {
		on := params["heading"].(bool)
		s.SetStabilization(on)
		return nil
	})

	return s
}

func (s *SpheroDriver) adaptor() *SpheroAdaptor {
	return s.Adaptor().(*SpheroAdaptor)
}

func (s *SpheroDriver) Init() bool {
	return true
}

func (s *SpheroDriver) Start() bool {
	go func() {
		for {
			packet := <-s.packetChannel
			s.write(packet)
		}
	}()

	go func() {
		for {
			response := <-s.responseChannel
			s.syncResponse = append(s.syncResponse, response)
		}
	}()

	go func() {
		for {
			header := s.readHeader()
			if header != nil && len(header) != 0 {
				body := s.readBody(header[4])
				if header[1] == 0xFE {
					async := append(header, body...)
					s.asyncResponse = append(s.asyncResponse, async)
				} else {
					s.responseChannel <- append(header, body...)
				}
			}
		}
	}()

	go func() {
		for {
			var evt []uint8
			for len(s.asyncResponse) != 0 {
				evt, s.asyncResponse = s.asyncResponse[len(s.asyncResponse)-1], s.asyncResponse[:len(s.asyncResponse)-1]
				if evt[2] == 0x07 {
					s.handleCollisionDetected(evt)
				}
			}
			time.Sleep(100 * time.Millisecond)
		}
	}()

	s.configureCollisionDetection()
	s.enableStopOnDisconnect()

	return true
}

func (s *SpheroDriver) Halt() bool {
	gobot.Every(10*time.Millisecond, func() {
		s.Stop()
	})
	time.Sleep(1 * time.Second)
	return true
}

func (s *SpheroDriver) SetRGB(r uint8, g uint8, b uint8) {
	s.packetChannel <- s.craftPacket([]uint8{r, g, b, 0x01}, 0x20)
}

func (s *SpheroDriver) GetRGB() []uint8 {
	return s.getSyncResponse(s.craftPacket([]uint8{}, 0x22))
}

func (s *SpheroDriver) SetBackLED(level uint8) {
	s.packetChannel <- s.craftPacket([]uint8{level}, 0x21)
}

func (s *SpheroDriver) SetHeading(heading uint16) {
	s.packetChannel <- s.craftPacket([]uint8{uint8(heading >> 8), uint8(heading & 0xFF)}, 0x01)
}

func (s *SpheroDriver) SetStabilization(on bool) {
	b := uint8(0x01)
	if on == false {
		b = 0x00
	}
	s.packetChannel <- s.craftPacket([]uint8{b}, 0x02)
}

func (s *SpheroDriver) Roll(speed uint8, heading uint16) {
	s.packetChannel <- s.craftPacket([]uint8{speed, uint8(heading >> 8), uint8(heading & 0xFF), 0x01}, 0x30)
}

func (s *SpheroDriver) Stop() {
	s.Roll(0, 0)
}

func (s *SpheroDriver) configureCollisionDetection() {
	s.packetChannel <- s.craftPacket([]uint8{0x01, 0x40, 0x40, 0x50, 0x50, 0x60}, 0x12)
}

func (s *SpheroDriver) enableStopOnDisconnect() {
	s.packetChannel <- s.craftPacket([]uint8{0x00, 0x00, 0x00, 0x01}, 0x37)
}

func (s *SpheroDriver) handleCollisionDetected(data []uint8) {
	gobot.Publish(s.Event("collision"), data)
}

func (s *SpheroDriver) getSyncResponse(packet *packet) []byte {
	s.packetChannel <- packet
	for i := 0; i < 500; i++ {
		for key := range s.syncResponse {
			if s.syncResponse[key][3] == packet.header[4] && len(s.syncResponse[key]) > 6 {
				var response []byte
				response, s.syncResponse = s.syncResponse[len(s.syncResponse)-1], s.syncResponse[:len(s.syncResponse)-1]
				return response
			}
		}
		time.Sleep(10 * time.Microsecond)
	}

	return []byte{}
}

func (s *SpheroDriver) craftPacket(body []uint8, cid byte) *packet {
	packet := new(packet)
	packet.body = body
	dlen := len(packet.body) + 1
	packet.header = []uint8{0xFF, 0xFF, 0x02, cid, s.seq, uint8(dlen)}
	packet.checksum = s.calculateChecksum(packet)
	return packet
}

func (s *SpheroDriver) write(packet *packet) {
	buf := append(packet.header, packet.body...)
	buf = append(buf, packet.checksum)
	length, err := s.adaptor().sp.Write(buf)
	if err != nil {
		fmt.Println(s.Name, err)
		s.adaptor().Disconnect()
		fmt.Println("Reconnecting to SpheroDriver...")
		s.adaptor().Connect()
		return
	} else if length != len(buf) {
		fmt.Println("Not enough bytes written", s.Name)
	}
	s.seq++
}

func (s *SpheroDriver) calculateChecksum(packet *packet) uint8 {
	buf := append(packet.header, packet.body...)
	buf = buf[2:]
	var calculatedChecksum uint16
	for i := range buf {
		calculatedChecksum += uint16(buf[i])
	}
	return uint8(^(calculatedChecksum % 256))
}

func (s *SpheroDriver) readHeader() []uint8 {
	data := s.readNextChunk(5)
	if data == nil {
		return nil
	}
	return data
}

func (s *SpheroDriver) readBody(length uint8) []uint8 {
	data := s.readNextChunk(length)
	if data == nil {
		return nil
	}
	return data
}

func (s *SpheroDriver) readNextChunk(length uint8) []uint8 {
	time.Sleep(1000 * time.Microsecond)
	var read = make([]uint8, int(length))
	l, err := s.adaptor().sp.Read(read[:])
	if err != nil || length != uint8(l) {
		return nil
	}
	return read
}
<|endoftext|>