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

// Package gmail provides a gmail barista module.
package gmail // import "barista.run/modules/gsuite/gmail"

import (
	"net/http"
	"time"

	"barista.run/bar"
	"barista.run/base/value"
	"barista.run/oauth"
	"barista.run/outputs"
	"barista.run/timing"

	"golang.org/x/oauth2/google"
	gmail "google.golang.org/api/gmail/v1"
)

// Info represents the unread and total thread counts for labels.
// The keys are the names (not IDs) of the labels, and the values are the thread
// counts (Threads is total threads, while Unread is just unread threads).
type Info struct {
	Threads map[string]int64
	Unread  map[string]int64
}

// TotalUnread is the total number of unread threads across all labels. (as set
// during construction).
func (i Info) TotalUnread() int64 {
	t := int64(0)
	for _, u := range i.Unread {
		t += u
	}
	return t
}

// TotalThreads is the total number of threads across all configured labels.
func (i Info) TotalThreads() int64 {
	t := int64(0)
	for _, c := range i.Threads {
		t += c
	}
	return t
}

// Module represents a Gmail barista module.
type Module struct {
	config     *oauth.Config
	labels     []string
	scheduler  timing.Scheduler
	outputFunc value.Value // of func(Info) bar.Output
}

// New creates a gmail module from the given oauth config, that fetches unread
// and total thread counts for the given set of labels.
func New(clientConfig []byte, labels ...string) *Module {
	config, err := google.ConfigFromJSON(clientConfig, gmail.GmailLabelsScope)
	if err != nil {
		panic("Bad client config: " + err.Error())
	}
	if len(labels) == 0 {
		labels = []string{"INBOX"}
	}
	m := &Module{
		config:    oauth.Register(config),
		labels:    labels,
		scheduler: timing.NewScheduler(),
	}
	m.RefreshInterval(5 * time.Minute)
	m.Output(func(i Info) bar.Output {
		if i.TotalUnread() == 0 {
			return nil
		}
		return outputs.Textf("Gmail: %d", i.TotalUnread())
	})
	return m
}

// for tests, to wrap the client in a transport that redirects requests.
var wrapForTest func(*http.Client)

// Stream starts the module.
func (m *Module) Stream(sink bar.Sink) {
	client, _ := m.config.Client()
	if wrapForTest != nil {
		wrapForTest(client)
	}
	srv, err := gmail.New(client)
	if sink.Error(err) {
		return
	}
	r, err := srv.Users.Labels.List("me").Do()
	if sink.Error(err) {
		return
	}
	labelIDs := map[string]string{}
	for _, l := range r.Labels {
		labelIDs[l.Name] = l.Id
	}
	i, err := fetch(srv, m.labels, labelIDs)
	outf := m.outputFunc.Get().(func(Info) bar.Output)
	nextOutputFunc := m.outputFunc.Next()
	for {
		if sink.Error(err) {
			return
		}
		sink.Output(outf(i))
		select {
		case <-nextOutputFunc:
			nextOutputFunc = m.outputFunc.Next()
			outf = m.outputFunc.Get().(func(Info) bar.Output)
		case <-m.scheduler.Tick():
			i, err = fetch(srv, m.labels, labelIDs)
		}
	}
}

func fetch(srv *gmail.Service, labels []string, labelIDs map[string]string) (Info, error) {
	i := Info{
		Threads: map[string]int64{},
		Unread:  map[string]int64{},
	}
	for _, l := range labels {
		r, err := srv.Users.Labels.Get("me", labelIDs[l]).Do()
		if err != nil {
			return i, err
		}
		i.Threads[l] = r.ThreadsTotal
		i.Unread[l] = r.ThreadsUnread
	}
	return i, nil
}

// Output sets the output format for the module.
func (m *Module) Output(outputFunc func(Info) bar.Output) *Module {
	m.outputFunc.Set(outputFunc)
	return m
}

// RefreshInterval sets the interval between consecutive checks for new mail.
func (m *Module) RefreshInterval(interval time.Duration) *Module {
	m.scheduler.Every(interval)
	return m
}
<commit_msg>Ignore impossible error when creating gmail service<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package gmail provides a gmail barista module.
package gmail // import "barista.run/modules/gsuite/gmail"

import (
	"net/http"
	"time"

	"barista.run/bar"
	"barista.run/base/value"
	"barista.run/oauth"
	"barista.run/outputs"
	"barista.run/timing"

	"golang.org/x/oauth2/google"
	gmail "google.golang.org/api/gmail/v1"
)

// Info represents the unread and total thread counts for labels.
// The keys are the names (not IDs) of the labels, and the values are the thread
// counts (Threads is total threads, while Unread is just unread threads).
type Info struct {
	Threads map[string]int64
	Unread  map[string]int64
}

// TotalUnread is the total number of unread threads across all labels. (as set
// during construction).
func (i Info) TotalUnread() int64 {
	t := int64(0)
	for _, u := range i.Unread {
		t += u
	}
	return t
}

// TotalThreads is the total number of threads across all configured labels.
func (i Info) TotalThreads() int64 {
	t := int64(0)
	for _, c := range i.Threads {
		t += c
	}
	return t
}

// Module represents a Gmail barista module.
type Module struct {
	config     *oauth.Config
	labels     []string
	scheduler  timing.Scheduler
	outputFunc value.Value // of func(Info) bar.Output
}

// New creates a gmail module from the given oauth config, that fetches unread
// and total thread counts for the given set of labels.
func New(clientConfig []byte, labels ...string) *Module {
	config, err := google.ConfigFromJSON(clientConfig, gmail.GmailLabelsScope)
	if err != nil {
		panic("Bad client config: " + err.Error())
	}
	if len(labels) == 0 {
		labels = []string{"INBOX"}
	}
	m := &Module{
		config:    oauth.Register(config),
		labels:    labels,
		scheduler: timing.NewScheduler(),
	}
	m.RefreshInterval(5 * time.Minute)
	m.Output(func(i Info) bar.Output {
		if i.TotalUnread() == 0 {
			return nil
		}
		return outputs.Textf("Gmail: %d", i.TotalUnread())
	})
	return m
}

// for tests, to wrap the client in a transport that redirects requests.
var wrapForTest func(*http.Client)

// Stream starts the module.
func (m *Module) Stream(sink bar.Sink) {
	client, _ := m.config.Client()
	if wrapForTest != nil {
		wrapForTest(client)
	}
	srv, _ := gmail.New(client)
	r, err := srv.Users.Labels.List("me").Do()
	if sink.Error(err) {
		return
	}
	labelIDs := map[string]string{}
	for _, l := range r.Labels {
		labelIDs[l.Name] = l.Id
	}
	i, err := fetch(srv, m.labels, labelIDs)
	outf := m.outputFunc.Get().(func(Info) bar.Output)
	nextOutputFunc := m.outputFunc.Next()
	for {
		if sink.Error(err) {
			return
		}
		sink.Output(outf(i))
		select {
		case <-nextOutputFunc:
			nextOutputFunc = m.outputFunc.Next()
			outf = m.outputFunc.Get().(func(Info) bar.Output)
		case <-m.scheduler.Tick():
			i, err = fetch(srv, m.labels, labelIDs)
		}
	}
}

func fetch(srv *gmail.Service, labels []string, labelIDs map[string]string) (Info, error) {
	i := Info{
		Threads: map[string]int64{},
		Unread:  map[string]int64{},
	}
	for _, l := range labels {
		r, err := srv.Users.Labels.Get("me", labelIDs[l]).Do()
		if err != nil {
			return i, err
		}
		i.Threads[l] = r.ThreadsTotal
		i.Unread[l] = r.ThreadsUnread
	}
	return i, nil
}

// Output sets the output format for the module.
func (m *Module) Output(outputFunc func(Info) bar.Output) *Module {
	m.outputFunc.Set(outputFunc)
	return m
}

// RefreshInterval sets the interval between consecutive checks for new mail.
func (m *Module) RefreshInterval(interval time.Duration) *Module {
	m.scheduler.Every(interval)
	return m
}
<|endoftext|>


<commit_before>package main

var (
	programName = "gpdb"
	programVersion = "3.0.1"
)

func main() {
	// Execute the cobra CLI & run the program
	rootCmd.Execute()
}
<commit_msg>Bumping the version of go-gpdb<commit_after>package main

var (
	programName = "gpdb"
	programVersion = "3.1.0"
)

func main() {
	// Execute the cobra CLI & run the program
	rootCmd.Execute()
}
<|endoftext|>


<commit_before>package server

import (
	"../bitset"
	"../hashtree"
	"../network"
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

var ERROR_NOT_LOCAL = errors.New("file is not locally available")
var ERROR_LEVEL_LOW = errors.New("the inner hash level is lower than cached")
var ERROR_INDEX_OFF = errors.New("the index of inner hashes is out of range for file of this size")
var ERROR_ALREADY_EXIST = errors.New("the thing was already added or started")

type FileState int

const (
	FILE_UNKNOW FileState = iota
	FILE_PART
	FILE_COMPLETE
)

type Database interface {
	LowestInnerHashes() hashtree.Level
	ImportFromReader(r io.Reader) network.StaticId
	GetState(id network.StaticId) FileState
	GetAt(b []byte, id network.StaticId, off hashtree.Bytes) (int, error)
	GetInnerHashes(id network.StaticId, req network.InnerHashes) (network.InnerHashes, error)
	StartPart(id network.StaticId) error
	PutAt(b []byte, id network.StaticId, off hashtree.Bytes) error
	PutInnerHashes(id network.StaticId, set network.InnerHashes) (hashtree.Nodes ,bool, error)
	Remove(id network.StaticId)
}

func ImportLocalFile(d Database, location string) (id network.StaticId) {
	f, _ := os.Open(location)
	r := bufio.NewReader(f)
	id = d.ImportFromReader(r)
	f.Close()
	return
}

type simpleDatabase struct {
	datafolder        *os.File
	dirname           string
	lowestInnerHashes hashtree.Level
}

func OpenSimpleDatabase(dirname string, lowestInnerHashes hashtree.Level) Database {
	os.MkdirAll(dirname, 0777)
	dir, err := os.Open(dirname)
	if err != nil {
		panic(err)
	}
	d := &simpleDatabase{
		datafolder:        dir,
		dirname:           dirname,
		lowestInnerHashes: lowestInnerHashes,
	}

	return d
}

func (d *simpleDatabase) LowestInnerHashes() hashtree.Level {
	return d.lowestInnerHashes
}

var refHash = hashtree.NewFile()

func (d *simpleDatabase) hashNumber(leafs hashtree.Nodes, l hashtree.Level, n hashtree.Nodes) int64 {
	sum := hashtree.Nodes(0)
	for i := hashtree.Level(0); i < l; i++ {
		sum += refHash.LevelWidth(leafs, i)
	}
	return int64(sum + n)
}
func (d *simpleDatabase) hashTopNumber(leafs hashtree.Nodes) int64 {
	sum := hashtree.Nodes(0)
	l := hashtree.Levels(leafs)
	for i := hashtree.Level(0); i < l; i++ {
		sum += refHash.LevelWidth(leafs, i)
	}
	return int64(sum)
}

func (d *simpleDatabase) hashPosition(leafs hashtree.Nodes, l hashtree.Level, n hashtree.Nodes) int64 {
	return d.hashNumber(leafs, l, n) * int64(refHash.Size())
}

func (d *simpleDatabase) innerHashListenerFile(hasher hashtree.HashTree, len hashtree.Bytes) *os.File {
	leafs := hasher.Nodes(len)
	top := hasher.Levels(leafs) - 1
	file, err := ioutil.TempFile(d.dirname, "listener-")
	if err != nil {
		panic(err)
	}
	listener := func(l hashtree.Level, i hashtree.Nodes, h *hashtree.H256) {
		//TODO: don't save levels lower than needed
		if l == top {
			return //don't need the root here
		}
		b := h.ToBytes()
		off := d.hashPosition(leafs, l, i)
		if _, err := file.WriteAt(b, off); err != nil {
			panic(err)
		}
	}
	hasher.SetInnerHashListener(listener)
	return file
}

func (d *simpleDatabase) ImportFromReader(r io.Reader) network.StaticId {
	f, err := ioutil.TempFile(d.dirname, "import-")
	if err != nil {
		panic(err)
	}
	len, err2 := io.Copy(f, r)
	if err2 != nil {
		panic(err2)
	}
	hasher := hashtree.NewFile()
	hashFile := d.innerHashListenerFile(hasher, hashtree.Bytes(len))

	f.Seek(0, os.SEEK_SET)
	io.Copy(hasher, f)
	id := network.StaticId{
		Hash:   hasher.Sum(nil),
		Length: &len,
	}
	f.Close()
	hashFile.Close()

	err = os.Rename(f.Name(), d.fileNameForId(id))
	if err != nil {
		if os.IsExist(err) {
			os.Remove(f.Name())
		} else {
			panic(err)
		}
	}
	err = os.Rename(hashFile.Name(), d.hashFileNameForId(id))
	if err != nil {
		if os.IsExist(err) {
			os.Remove(hashFile.Name())
		} else {
			panic(err)
		}
	}
	return id
}

func (d *simpleDatabase) GetState(id network.StaticId) FileState {
	_, err := os.Stat(d.fileNameForId(id))
	if os.IsNotExist(err) {
		_, err = os.Stat(d.hashFileNameForId(id))
		if os.IsNotExist(err) {
			return FILE_UNKNOW
		} else {
			return FILE_PART
		}
	} else {
		return FILE_COMPLETE
	}
}

func (d *simpleDatabase) GetAt(b []byte, id network.StaticId, off hashtree.Bytes) (int, error) {
	f, err := os.Open(d.fileNameForId(id))
	if err != nil {
		return 0, ERROR_NOT_LOCAL
	}
	defer f.Close()
	return f.ReadAt(b, int64(off))
}

func (d *simpleDatabase) GetInnerHashes(id network.StaticId, req network.InnerHashes) (network.InnerHashes, error) {
	leafs := refHash.Nodes(hashtree.Bytes(id.GetLength()))
	level := hashtree.Level(req.GetHeight())
	from := hashtree.Nodes(req.GetFrom())
	nodes := hashtree.Nodes(req.GetLength())
	if nodes == 0 {
		return req, nil //nothing was requested
	} else if level < d.lowestInnerHashes {
		return req, ERROR_LEVEL_LOW
	} else if level >= refHash.Levels(leafs)-1 || from+nodes > refHash.LevelWidth(leafs, level) {
		return req, ERROR_INDEX_OFF
	}
	f, err := os.Open(d.hashFileNameForId(id))
	if err != nil {
		return req, ERROR_NOT_LOCAL
	}
	defer f.Close()
	off := d.hashPosition(leafs, level, from)
	b := make([]byte, refHash.Size()*int(nodes))
	if _, err := f.ReadAt(b, off); err != nil {
		panic(err)
	}
	req.Hashes = b
	return req, nil
}

func (d *simpleDatabase) StartPart(id network.StaticId) error {
	_, err := os.Stat(d.hashFileNameForId(id))
	if os.IsNotExist(err) {
		pf, err := os.Create(d.partFileNameForId(id))
		if err != nil {
			return err
		}
		defer pf.Close()
		hf, err2 := os.Create(d.hashFileNameForId(id))
		if err2 != nil {
			return err2
		}
		defer hf.Close()
		return nil
	} else {
		return ERROR_ALREADY_EXIST
	}

}
func (d *simpleDatabase) PutAt(b []byte, id network.StaticId, off hashtree.Bytes) error {
	return nil
}
func (d *simpleDatabase) PutInnerHashes(id network.StaticId, set network.InnerHashes) (has hashtree.Nodes, complete bool, err error) {
	leafs := id.Blocks()
	bits := bitset.OpenCountingFileBacked(d.haveHashNameForId(id), int(d.hashTopNumber(leafs)-1))
	defer bits.Close()
	f, err := os.OpenFile(d.hashFileNameForId(id), os.O_RDWR, 0666)
	if err != nil {
		return 0, false, ERROR_NOT_LOCAL
	}
	defer f.Close()
	hashBuffer := make([]byte, refHash.Size())
	splited := set.SplitLocalSummable(&id)
	for _, hashes := range splited {
		rootL, rootN := hashes.LocalRoot(leafs)
		key := int(d.hashNumber(leafs, rootL, rootN))
		if key == bits.Capacity() {
			//this is root
		} else if !bits.Get(key) {
			continue // this part of hashes can not be verified, skiped
		}
		sum := hashes.LocalSum()
		if key == bits.Capacity() {
			copy(hashBuffer, id.GetHash())
		} else {
			off := d.hashPosition(leafs, rootL, rootN)
			if _, err := f.ReadAt(hashBuffer, off); err != nil {
				panic(err)
			}
		}
		if bytes.Equal(sum, hashBuffer) {
			//verified, now save
			listener := func(l hashtree.Level, i hashtree.Nodes, h *hashtree.H256) {
				realL := set.GetHeightL() + l
				realN := i + rootN<<uint32(rootL-l)
				if realL == rootL {
					return //don't need the root here, already verified
				}
				b := h.ToBytes()
				off := d.hashPosition(leafs, realL, realN)
				if _, err := f.WriteAt(b, off); err != nil {
					panic(err)
				}
				n := int(d.hashNumber(leafs, realL, realN))
				bits.Set(n)
			}
			hasher := hashtree.NewTree()
			hasher.SetInnerHashListener(listener)
			hasher.Write(hashes.GetHashes())
			hasher.Sum(nil)
		}
	}
	return hashtree.Nodes(bits.Count()), bits.Full(), nil
}

func remove(filename string) {
	err := os.Remove(filename)
	if err != nil && !os.IsNotExist(err) {
		panic(err)
	}
}

func (d *simpleDatabase) Remove(id network.StaticId) {
	remove(d.havePartNameForId(id))
	remove(d.haveHashNameForId(id))
	remove(d.partFileNameForId(id))
	remove(d.fileNameForId(id))
	remove(d.hashFileNameForId(id))
}

func (d *simpleDatabase) fileNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/F-%s", d.datafolder.Name(), id.CompactId())
}
func (d *simpleDatabase) hashFileNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/H-%s", d.datafolder.Name(), id.CompactId())
}
func (d *simpleDatabase) partFileNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/P-%s", d.datafolder.Name(), id.CompactId())
}
func (d *simpleDatabase) haveHashNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/hH-%s", d.datafolder.Name(), id.CompactId())
}
func (d *simpleDatabase) havePartNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/hP-%s", d.datafolder.Name(), id.CompactId())
}
<commit_msg>add close to allow tests to restart databases<commit_after>package server

import (
	"../bitset"
	"../hashtree"
	"../network"
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

var ERROR_NOT_LOCAL = errors.New("file is not locally available")
var ERROR_LEVEL_LOW = errors.New("the inner hash level is lower than cached")
var ERROR_INDEX_OFF = errors.New("the index of inner hashes is out of range for file of this size")
var ERROR_ALREADY_EXIST = errors.New("the thing was already added or started")

type FileState int

const (
	FILE_UNKNOW FileState = iota
	FILE_PART
	FILE_COMPLETE
)

type Database interface {
	LowestInnerHashes() hashtree.Level
	ImportFromReader(r io.Reader) network.StaticId
	GetState(id network.StaticId) FileState
	GetAt(b []byte, id network.StaticId, off hashtree.Bytes) (int, error)
	GetInnerHashes(id network.StaticId, req network.InnerHashes) (network.InnerHashes, error)
	StartPart(id network.StaticId) error
	PutAt(b []byte, id network.StaticId, off hashtree.Bytes) error
	PutInnerHashes(id network.StaticId, set network.InnerHashes) (has hashtree.Nodes, complete bool, err error)
	Remove(id network.StaticId)
	Close()
}

func ImportLocalFile(d Database, location string) (id network.StaticId) {
	f, _ := os.Open(location)
	r := bufio.NewReader(f)
	id = d.ImportFromReader(r)
	f.Close()
	return
}

type simpleDatabase struct {
	datafolder        *os.File
	dirname           string
	lowestInnerHashes hashtree.Level
}

func OpenSimpleDatabase(dirname string, lowestInnerHashes hashtree.Level) Database {
	os.MkdirAll(dirname, 0777)
	dir, err := os.Open(dirname)
	if err != nil {
		panic(err)
	}
	d := &simpleDatabase{
		datafolder:        dir,
		dirname:           dirname,
		lowestInnerHashes: lowestInnerHashes,
	}

	return d
}

func (d *simpleDatabase) Close() {
	err := d.datafolder.Close()
	if err != nil {
		panic(err)
	}
	d.datafolder = nil
}

func (d *simpleDatabase) LowestInnerHashes() hashtree.Level {
	return d.lowestInnerHashes
}

var refHash = hashtree.NewFile()

func (d *simpleDatabase) hashNumber(leafs hashtree.Nodes, l hashtree.Level, n hashtree.Nodes) int64 {
	sum := hashtree.Nodes(0)
	for i := hashtree.Level(0); i < l; i++ {
		sum += refHash.LevelWidth(leafs, i)
	}
	return int64(sum + n)
}
func (d *simpleDatabase) hashTopNumber(leafs hashtree.Nodes) int64 {
	sum := hashtree.Nodes(0)
	l := hashtree.Levels(leafs)
	for i := hashtree.Level(0); i < l; i++ {
		sum += refHash.LevelWidth(leafs, i)
	}
	return int64(sum)
}

func (d *simpleDatabase) hashPosition(leafs hashtree.Nodes, l hashtree.Level, n hashtree.Nodes) int64 {
	return d.hashNumber(leafs, l, n) * int64(refHash.Size())
}

func (d *simpleDatabase) innerHashListenerFile(hasher hashtree.HashTree, len hashtree.Bytes) *os.File {
	leafs := hasher.Nodes(len)
	top := hasher.Levels(leafs) - 1
	file, err := ioutil.TempFile(d.dirname, "listener-")
	if err != nil {
		panic(err)
	}
	listener := func(l hashtree.Level, i hashtree.Nodes, h *hashtree.H256) {
		//TODO: don't save levels lower than needed
		if l == top {
			return //don't need the root here
		}
		b := h.ToBytes()
		off := d.hashPosition(leafs, l, i)
		if _, err := file.WriteAt(b, off); err != nil {
			panic(err)
		}
	}
	hasher.SetInnerHashListener(listener)
	return file
}

func (d *simpleDatabase) ImportFromReader(r io.Reader) network.StaticId {
	f, err := ioutil.TempFile(d.dirname, "import-")
	if err != nil {
		panic(err)
	}
	len, err2 := io.Copy(f, r)
	if err2 != nil {
		panic(err2)
	}
	hasher := hashtree.NewFile()
	hashFile := d.innerHashListenerFile(hasher, hashtree.Bytes(len))

	f.Seek(0, os.SEEK_SET)
	io.Copy(hasher, f)
	id := network.StaticId{
		Hash:   hasher.Sum(nil),
		Length: &len,
	}
	f.Close()
	hashFile.Close()

	err = os.Rename(f.Name(), d.fileNameForId(id))
	if err != nil {
		if os.IsExist(err) {
			os.Remove(f.Name())
		} else {
			panic(err)
		}
	}
	err = os.Rename(hashFile.Name(), d.hashFileNameForId(id))
	if err != nil {
		if os.IsExist(err) {
			os.Remove(hashFile.Name())
		} else {
			panic(err)
		}
	}
	return id
}

func (d *simpleDatabase) GetState(id network.StaticId) FileState {
	_, err := os.Stat(d.fileNameForId(id))
	if os.IsNotExist(err) {
		_, err = os.Stat(d.hashFileNameForId(id))
		if os.IsNotExist(err) {
			return FILE_UNKNOW
		} else {
			return FILE_PART
		}
	} else {
		return FILE_COMPLETE
	}
}

func (d *simpleDatabase) GetAt(b []byte, id network.StaticId, off hashtree.Bytes) (int, error) {
	f, err := os.Open(d.fileNameForId(id))
	if err != nil {
		return 0, ERROR_NOT_LOCAL
	}
	defer f.Close()
	return f.ReadAt(b, int64(off))
}

func (d *simpleDatabase) GetInnerHashes(id network.StaticId, req network.InnerHashes) (network.InnerHashes, error) {
	leafs := refHash.Nodes(hashtree.Bytes(id.GetLength()))
	level := hashtree.Level(req.GetHeight())
	from := hashtree.Nodes(req.GetFrom())
	nodes := hashtree.Nodes(req.GetLength())
	if nodes == 0 {
		return req, nil //nothing was requested
	} else if level < d.lowestInnerHashes {
		return req, ERROR_LEVEL_LOW
	} else if level >= refHash.Levels(leafs)-1 || from+nodes > refHash.LevelWidth(leafs, level) {
		return req, ERROR_INDEX_OFF
	}
	f, err := os.Open(d.hashFileNameForId(id))
	if err != nil {
		return req, ERROR_NOT_LOCAL
	}
	defer f.Close()
	off := d.hashPosition(leafs, level, from)
	b := make([]byte, refHash.Size()*int(nodes))
	if _, err := f.ReadAt(b, off); err != nil {
		panic(err)
	}
	req.Hashes = b
	return req, nil
}

func (d *simpleDatabase) StartPart(id network.StaticId) error {
	_, err := os.Stat(d.hashFileNameForId(id))
	if os.IsNotExist(err) {
		pf, err := os.Create(d.partFileNameForId(id))
		if err != nil {
			return err
		}
		defer pf.Close()
		hf, err2 := os.Create(d.hashFileNameForId(id))
		if err2 != nil {
			return err2
		}
		defer hf.Close()
		return nil
	} else {
		return ERROR_ALREADY_EXIST
	}

}
func (d *simpleDatabase) PutAt(b []byte, id network.StaticId, off hashtree.Bytes) error {
	return nil
}
func (d *simpleDatabase) PutInnerHashes(id network.StaticId, set network.InnerHashes) (has hashtree.Nodes, complete bool, err error) {
	leafs := id.Blocks()
	bits := bitset.OpenCountingFileBacked(d.haveHashNameForId(id), int(d.hashTopNumber(leafs)-1))
	defer bits.Close()
	f, err := os.OpenFile(d.hashFileNameForId(id), os.O_RDWR, 0666)
	if err != nil {
		return 0, false, ERROR_NOT_LOCAL
	}
	defer f.Close()
	hashBuffer := make([]byte, refHash.Size())
	splited := set.SplitLocalSummable(&id)
	for _, hashes := range splited {
		rootL, rootN := hashes.LocalRoot(leafs)
		key := int(d.hashNumber(leafs, rootL, rootN))
		if key == bits.Capacity() {
			//this is root
		} else if !bits.Get(key) {
			continue // this part of hashes can not be verified, skiped
		}
		sum := hashes.LocalSum()
		if key == bits.Capacity() {
			copy(hashBuffer, id.GetHash())
		} else {
			off := d.hashPosition(leafs, rootL, rootN)
			if _, err := f.ReadAt(hashBuffer, off); err != nil {
				panic(err)
			}
		}
		if bytes.Equal(sum, hashBuffer) {
			//verified, now save
			listener := func(l hashtree.Level, i hashtree.Nodes, h *hashtree.H256) {
				realL := set.GetHeightL() + l
				realN := i + rootN<<uint32(rootL-l)
				if realL == rootL {
					return //don't need the root here, already verified
				}
				b := h.ToBytes()
				off := d.hashPosition(leafs, realL, realN)
				if _, err := f.WriteAt(b, off); err != nil {
					panic(err)
				}
				n := int(d.hashNumber(leafs, realL, realN))
				bits.Set(n)
			}
			hasher := hashtree.NewTree()
			hasher.SetInnerHashListener(listener)
			hasher.Write(hashes.GetHashes())
			hasher.Sum(nil)
		}
	}
	return hashtree.Nodes(bits.Count()), bits.Full(), nil
}

func remove(filename string) {
	err := os.Remove(filename)
	if err != nil && !os.IsNotExist(err) {
		panic(err)
	}
}

func (d *simpleDatabase) Remove(id network.StaticId) {
	remove(d.havePartNameForId(id))
	remove(d.haveHashNameForId(id))
	remove(d.partFileNameForId(id))
	remove(d.fileNameForId(id))
	remove(d.hashFileNameForId(id))
}

func (d *simpleDatabase) fileNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/F-%s", d.datafolder.Name(), id.CompactId())
}
func (d *simpleDatabase) hashFileNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/H-%s", d.datafolder.Name(), id.CompactId())
}
func (d *simpleDatabase) partFileNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/P-%s", d.datafolder.Name(), id.CompactId())
}
func (d *simpleDatabase) haveHashNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/hH-%s", d.datafolder.Name(), id.CompactId())
}
func (d *simpleDatabase) havePartNameForId(id network.StaticId) string {
	return fmt.Sprintf("%s/hP-%s", d.datafolder.Name(), id.CompactId())
}
<|endoftext|>


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

// Download remote packages.

package main

import (
	"http"
	"os"
	"path"
	"regexp"
	"strings"
)

const dashboardURL = "http://godashboard.appspot.com/package"

// maybeReportToDashboard reports path to dashboard unless
// -dashboard=false is on command line.  It ignores errors.
func maybeReportToDashboard(path string) {
	// if -dashboard=false was on command line, do nothing
	if !*reportToDashboard {
		return
	}

	// otherwise lob url to dashboard
	r, _ := http.Post(dashboardURL, "application/x-www-form-urlencoded", strings.NewReader("path="+path))
	if r != nil && r.Body != nil {
		r.Body.Close()
	}
}

var googlecode = regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|hg))(/[a-z0-9A-Z_.\-/]*)?$`)
var github = regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`)
var bitbucket = regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`)
var launchpad = regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`)

// download checks out or updates pkg from the remote server.
func download(pkg string) (string, os.Error) {
	if strings.Index(pkg, "..") >= 0 {
		return "", os.ErrorString("invalid path (contains ..)")
	}
	if m := bitbucket.FindStringSubmatch(pkg); m != nil {
		if err := vcsCheckout(&hg, root+m[1], "http://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := googlecode.FindStringSubmatch(pkg); m != nil {
		var v *vcs
		switch m[2] {
		case "hg":
			v = &hg
		case "svn":
			v = &svn
		default:
			// regexp only allows hg, svn to get through
			panic("missing case in download: " + pkg)
		}
		if err := vcsCheckout(v, root+m[1], "http://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := github.FindStringSubmatch(pkg); m != nil {
		if strings.HasSuffix(m[1], ".git") {
			return "", os.ErrorString("repository " + pkg + " should not have .git suffix")
		}
		if err := vcsCheckout(&git, root+m[1], "http://"+m[1]+".git", m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := launchpad.FindStringSubmatch(pkg); m != nil {
		// Either lp.net/<project>[/<series>[/<path>]]
		//	 or lp.net/~<user or team>/<project>/<branch>[/<path>]
		if err := vcsCheckout(&bzr, root+m[1], "https://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	return "", os.ErrorString("unknown repository: " + pkg)
}

// a vcs represents a version control system
// like Mercurial, Git, or Subversion.
type vcs struct {
	cmd               string
	metadir           string
	clone             string
	update            string
	updateReleaseFlag string
	pull              string
	pullForceFlag     string
	log               string
	logLimitFlag      string
	logReleaseFlag    string
}

var hg = vcs{
	cmd:               "hg",
	metadir:           ".hg",
	clone:             "clone",
	update:            "update",
	updateReleaseFlag: "release",
	pull:              "pull",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

var git = vcs{
	cmd:               "git",
	metadir:           ".git",
	clone:             "clone",
	update:            "pull",
	updateReleaseFlag: "release",
	pull:              "fetch",
	log:               "log",
	logLimitFlag:      "-n1",
	logReleaseFlag:    "release",
}

var svn = vcs{
	cmd:               "svn",
	metadir:           ".svn",
	clone:             "checkout",
	update:            "update",
	updateReleaseFlag: "release",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "release",
}

var bzr = vcs{
	cmd:               "bzr",
	metadir:           ".bzr",
	clone:             "branch",
	update:            "update",
	updateReleaseFlag: "-rrelease",
	pull:              "pull",
	pullForceFlag:     "--overwrite",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

// vcsCheckout checks out repo into dst using vcs.
// It tries to check out (or update, if the dst already
// exists and -u was specified on the command line)
// the repository at tag/branch "release".  If there is no
// such tag or branch, it falls back to the repository tip.
func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error {
	dir, err := os.Stat(dst + "/" + vcs.metadir)
	if err == nil && !dir.IsDirectory() {
		return os.ErrorString("not a directory: " + dst)
	}
	if err != nil {
		parent, _ := path.Split(dst)
		if err := os.MkdirAll(parent, 0777); err != nil {
			return err
		}
		if err := run("/", nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
			return err
		}
		quietRun(dst, nil, vcs.cmd, vcs.update, vcs.updateReleaseFlag)

		// success on first installation - report
		maybeReportToDashboard(dashpath)
	} else if *update {
		// Retrieve new revisions from the remote branch, if the VCS
		// supports this operation independently (e.g. svn doesn't)
		if vcs.pull != "" {
			if vcs.pullForceFlag != "" {
				if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
					return err
				}
			} else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil {
				return err
			}
		}

		// Try to detect if a "release" tag exists.  If it does, update
		// to the tagged version.  If no tag is found, then update to the
		// tip afterwards.
		// NOTE(gustavo@niemeyer.net): What is the expected behavior with
		// svn here? "svn log -l1 release" doesn't make sense in this
		// context and will probably fail.
		if err := quietRun(dst, nil, vcs.cmd, vcs.log, vcs.logLimitFlag, vcs.logReleaseFlag); err == nil {
			if err := run(dst, nil, vcs.cmd, vcs.update, vcs.updateReleaseFlag); err != nil {
				// The VCS supports tagging, has the "release" tag, but
				// something else went wrong.  Report.
				return err
			}
		} else if err := run(dst, nil, vcs.cmd, vcs.update); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>goinstall: use https for Google Code checkouts<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Download remote packages.

package main

import (
	"http"
	"os"
	"path"
	"regexp"
	"strings"
)

const dashboardURL = "http://godashboard.appspot.com/package"

// maybeReportToDashboard reports path to dashboard unless
// -dashboard=false is on command line.  It ignores errors.
func maybeReportToDashboard(path string) {
	// if -dashboard=false was on command line, do nothing
	if !*reportToDashboard {
		return
	}

	// otherwise lob url to dashboard
	r, _ := http.Post(dashboardURL, "application/x-www-form-urlencoded", strings.NewReader("path="+path))
	if r != nil && r.Body != nil {
		r.Body.Close()
	}
}

var googlecode = regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|hg))(/[a-z0-9A-Z_.\-/]*)?$`)
var github = regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`)
var bitbucket = regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`)
var launchpad = regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`)

// download checks out or updates pkg from the remote server.
func download(pkg string) (string, os.Error) {
	if strings.Index(pkg, "..") >= 0 {
		return "", os.ErrorString("invalid path (contains ..)")
	}
	if m := bitbucket.FindStringSubmatch(pkg); m != nil {
		if err := vcsCheckout(&hg, root+m[1], "http://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := googlecode.FindStringSubmatch(pkg); m != nil {
		var v *vcs
		switch m[2] {
		case "hg":
			v = &hg
		case "svn":
			v = &svn
		default:
			// regexp only allows hg, svn to get through
			panic("missing case in download: " + pkg)
		}
		if err := vcsCheckout(v, root+m[1], "https://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := github.FindStringSubmatch(pkg); m != nil {
		if strings.HasSuffix(m[1], ".git") {
			return "", os.ErrorString("repository " + pkg + " should not have .git suffix")
		}
		if err := vcsCheckout(&git, root+m[1], "http://"+m[1]+".git", m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := launchpad.FindStringSubmatch(pkg); m != nil {
		// Either lp.net/<project>[/<series>[/<path>]]
		//	 or lp.net/~<user or team>/<project>/<branch>[/<path>]
		if err := vcsCheckout(&bzr, root+m[1], "https://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	return "", os.ErrorString("unknown repository: " + pkg)
}

// a vcs represents a version control system
// like Mercurial, Git, or Subversion.
type vcs struct {
	cmd               string
	metadir           string
	clone             string
	update            string
	updateReleaseFlag string
	pull              string
	pullForceFlag     string
	log               string
	logLimitFlag      string
	logReleaseFlag    string
}

var hg = vcs{
	cmd:               "hg",
	metadir:           ".hg",
	clone:             "clone",
	update:            "update",
	updateReleaseFlag: "release",
	pull:              "pull",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

var git = vcs{
	cmd:               "git",
	metadir:           ".git",
	clone:             "clone",
	update:            "pull",
	updateReleaseFlag: "release",
	pull:              "fetch",
	log:               "log",
	logLimitFlag:      "-n1",
	logReleaseFlag:    "release",
}

var svn = vcs{
	cmd:               "svn",
	metadir:           ".svn",
	clone:             "checkout",
	update:            "update",
	updateReleaseFlag: "release",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "release",
}

var bzr = vcs{
	cmd:               "bzr",
	metadir:           ".bzr",
	clone:             "branch",
	update:            "update",
	updateReleaseFlag: "-rrelease",
	pull:              "pull",
	pullForceFlag:     "--overwrite",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

// vcsCheckout checks out repo into dst using vcs.
// It tries to check out (or update, if the dst already
// exists and -u was specified on the command line)
// the repository at tag/branch "release".  If there is no
// such tag or branch, it falls back to the repository tip.
func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error {
	dir, err := os.Stat(dst + "/" + vcs.metadir)
	if err == nil && !dir.IsDirectory() {
		return os.ErrorString("not a directory: " + dst)
	}
	if err != nil {
		parent, _ := path.Split(dst)
		if err := os.MkdirAll(parent, 0777); err != nil {
			return err
		}
		if err := run("/", nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
			return err
		}
		quietRun(dst, nil, vcs.cmd, vcs.update, vcs.updateReleaseFlag)

		// success on first installation - report
		maybeReportToDashboard(dashpath)
	} else if *update {
		// Retrieve new revisions from the remote branch, if the VCS
		// supports this operation independently (e.g. svn doesn't)
		if vcs.pull != "" {
			if vcs.pullForceFlag != "" {
				if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
					return err
				}
			} else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil {
				return err
			}
		}

		// Try to detect if a "release" tag exists.  If it does, update
		// to the tagged version.  If no tag is found, then update to the
		// tip afterwards.
		// NOTE(gustavo@niemeyer.net): What is the expected behavior with
		// svn here? "svn log -l1 release" doesn't make sense in this
		// context and will probably fail.
		if err := quietRun(dst, nil, vcs.cmd, vcs.log, vcs.logLimitFlag, vcs.logReleaseFlag); err == nil {
			if err := run(dst, nil, vcs.cmd, vcs.update, vcs.updateReleaseFlag); err != nil {
				// The VCS supports tagging, has the "release" tag, but
				// something else went wrong.  Report.
				return err
			}
		} else if err := run(dst, nil, vcs.cmd, vcs.update); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package runner

import (
	"fmt"
	"os"
	"path/filepath"
	"sync"
)

const (
	STATUS_QUEUED      = "queued"
	STATUS_IN_PROGRESS = "in_progress"
	STATUS_FINISHED    = "finished"
)

type OffsetMap struct {
	mu            sync.Mutex
	sourceOffsets map[string]int
}

func (m *OffsetMap) get(source string) int {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.sourceOffsets[source]
}

func (m *OffsetMap) set(source string, offset int) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.sourceOffsets[source] = offset
}

func reportChunks(r *Reporter, cID string, c chan LogChunk, offsetMap *OffsetMap) {
	for l := range c {
		// Override offset until we figure how to show multi streams in changes UI
		sourceOffset := offsetMap.get(l.Source)
		l.Offset = sourceOffset

		sourceOffset += l.Length
		offsetMap.set(l.Source, sourceOffset)

		fmt.Printf("Got another chunk from %s (%d-%d)\n", l.Source, l.Offset, l.Length)
		fmt.Printf("%s", l.Payload)
		r.PushLogChunk(cID, l)
	}
}

func publishArtifacts(reporter *Reporter, cID string, artifacts []string) {
	if len(artifacts) == 0 {
		return
	}

	var matches []string
	for _, pattern := range artifacts {
		m, err := filepath.Glob(pattern)
		if err != nil {
			panic("Invalid artifact pattern" + err.Error())
		}
		matches = append(matches, m...)
	}

	reporter.PushArtifacts(cID, matches)
}

func RunAllCmds(reporter *Reporter, config *Config, result string) {
	wg := sync.WaitGroup{}

	offsetMap := OffsetMap{sourceOffsets: make(map[string]int)}

	for _, cmd := range config.Cmds {
		reporter.PushStatus(cmd.Id, STATUS_IN_PROGRESS, -1)
		r, err := NewRunner(cmd.Id, cmd.Script)
		if err != nil {
			reporter.PushStatus(cmd.Id, STATUS_FINISHED, 255)
			result = "failed"
			break
		}

		env := os.Environ()
		for k, v := range cmd.Env {
			env = append(env, k+"="+v)
		}
		r.Cmd.Env = env

		if len(cmd.Cwd) > 0 {
			r.Cmd.Dir = cmd.Cwd
		}

		wg.Add(1)
		go func() {
			reportChunks(reporter, config.JobstepID, r.ChunkChan, &offsetMap)
			wg.Done()
		}()

		pState, err := r.Run()
		if err != nil {
			reporter.PushStatus(cmd.Id, STATUS_FINISHED, 255)
			result = "failed"
			break
		} else {
			if pState.Success() {
				reporter.PushStatus(cmd.Id, STATUS_FINISHED, 0)
			} else {
				reporter.PushStatus(cmd.Id, STATUS_FINISHED, 1)
				result = "failed"
				break
			}
		}

		wg.Add(1)
		go func() {
			publishArtifacts(reporter, config.JobstepID, cmd.Artifacts)
			wg.Done()
		}()
	}

	wg.Wait()
}

func RunBuildPlan(reporter *Reporter, source *Source, config *Config) {
	result := "passed"

	reporter.PushJobStatus(config.JobstepID, STATUS_IN_PROGRESS, "")

	// TODO(dcramer): the workspace setup needs to correctly report log chunks
	// back upstream
	err := source.SetupWorkspace(reporter, config.Workspace)
	if err != nil {
		result = "failed"
	} else {
		RunAllCmds(reporter, config, result)
	}

	reporter.PushJobStatus(config.JobstepID, STATUS_FINISHED, result)
}
<commit_msg>Revert goroutine change for publishArtifacts<commit_after>package runner

import (
	"fmt"
	"os"
	"path/filepath"
	"sync"
)

const (
	STATUS_QUEUED      = "queued"
	STATUS_IN_PROGRESS = "in_progress"
	STATUS_FINISHED    = "finished"
)

type OffsetMap struct {
	mu            sync.Mutex
	sourceOffsets map[string]int
}

func (m *OffsetMap) get(source string) int {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.sourceOffsets[source]
}

func (m *OffsetMap) set(source string, offset int) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.sourceOffsets[source] = offset
}

func reportChunks(r *Reporter, cID string, c chan LogChunk, offsetMap *OffsetMap) {
	for l := range c {
		// Override offset until we figure how to show multi streams in changes UI
		sourceOffset := offsetMap.get(l.Source)
		l.Offset = sourceOffset

		sourceOffset += l.Length
		offsetMap.set(l.Source, sourceOffset)

		fmt.Printf("Got another chunk from %s (%d-%d)\n", l.Source, l.Offset, l.Length)
		fmt.Printf("%s", l.Payload)
		r.PushLogChunk(cID, l)
	}
}

func publishArtifacts(reporter *Reporter, cID string, artifacts []string) {
	if len(artifacts) == 0 {
		return
	}

	var matches []string
	for _, pattern := range artifacts {
		m, err := filepath.Glob(pattern)
		if err != nil {
			panic("Invalid artifact pattern" + err.Error())
		}
		matches = append(matches, m...)
	}

	reporter.PushArtifacts(cID, matches)
}

func RunAllCmds(reporter *Reporter, config *Config, result string) {
	wg := sync.WaitGroup{}

	offsetMap := OffsetMap{sourceOffsets: make(map[string]int)}

	for _, cmd := range config.Cmds {
		reporter.PushStatus(cmd.Id, STATUS_IN_PROGRESS, -1)
		r, err := NewRunner(cmd.Id, cmd.Script)
		if err != nil {
			reporter.PushStatus(cmd.Id, STATUS_FINISHED, 255)
			result = "failed"
			break
		}

		env := os.Environ()
		for k, v := range cmd.Env {
			env = append(env, k+"="+v)
		}
		r.Cmd.Env = env

		if len(cmd.Cwd) > 0 {
			r.Cmd.Dir = cmd.Cwd
		}

		wg.Add(1)
		go func() {
			reportChunks(reporter, config.JobstepID, r.ChunkChan, &offsetMap)
			wg.Done()
		}()

		pState, err := r.Run()
		if err != nil {
			reporter.PushStatus(cmd.Id, STATUS_FINISHED, 255)
			result = "failed"
			break
		} else {
			if pState.Success() {
				reporter.PushStatus(cmd.Id, STATUS_FINISHED, 0)
			} else {
				reporter.PushStatus(cmd.Id, STATUS_FINISHED, 1)
				result = "failed"
				break
			}
		}

		publishArtifacts(reporter, config.JobstepID, cmd.Artifacts)
	}
	wg.Wait()
}

func RunBuildPlan(reporter *Reporter, source *Source, config *Config) {
	result := "passed"

	reporter.PushJobStatus(config.JobstepID, STATUS_IN_PROGRESS, "")

	// TODO(dcramer): the workspace setup needs to correctly report log chunks
	// back upstream
	err := source.SetupWorkspace(reporter, config.Workspace)
	if err != nil {
		result = "failed"
	} else {
		RunAllCmds(reporter, config, result)
	}

	reporter.PushJobStatus(config.JobstepID, STATUS_FINISHED, result)
}
<|endoftext|>


<commit_before>// A command-line utility to mark and sweep Jekyll issues for staleness.
package main

import (
	"context"
	"flag"
	"log"
	"strings"
	"time"

	"github.com/google/go-github/github"
	"github.com/parkr/auto-reply/ctx"
	"github.com/parkr/auto-reply/stale"
	"golang.org/x/sync/errgroup"
)

type repo struct {
	Owner, Name string
}

var (
	// Labels which mean the issue is already stale.
	staleLabels = []string{
		"pending-feedback",
	}

	// Labels which can be used to disable the staleable functionality for an issue.
	nonStaleableLabels = []string{
		"has-pull-request",
		"pinned",
		"security",
	}

	// All the repos to apply apply these to.
	defaultRepos = []repo{
		repo{"jekyll", "jekyll"},
		repo{"jekyll", "jekyll-admin"},
		repo{"jekyll", "jekyll-import"},
		repo{"jekyll", "github-metadata"},
		repo{"jekyll", "jekyll-redirect-from"},
		repo{"jekyll", "jekyll-feed"},
		repo{"jekyll", "jekyll-compose"},
		repo{"jekyll", "jekyll-watch"},
		repo{"jekyll", "jekyll-seo-tag"},
		repo{"jekyll", "jekyll-sitemap"},
		repo{"jekyll", "jekyll-sass-converter"},
		repo{"jekyll", "jemoji"},
		repo{"jekyll", "jekyll-gist"},
		repo{"jekyll", "jekyll-coffeescript"},
		repo{"jekyll", "plugins"},
	}

	twoMonthsAgo = time.Now().AddDate(0, -2, 0)

	staleIssuesListOptions = &github.IssueListByRepoOptions{
		State:       "open",
		Sort:        "updated",
		Direction:   "asc",
		ListOptions: github.ListOptions{PerPage: 200},
	}

	staleJekyllIssueComment = &github.IssueComment{
		Body: github.String(`
This issue has been automatically marked as stale because it has not been commented on for at least two months.

The resources of the Jekyll team are limited, and so we are asking for your help.

If this is a **bug** and you can still reproduce this error on the 3.3-stable or master branch, please reply with all of the information you have about it in order to keep the issue open.

If this is a **feature request**, please consider building it first as a plugin. Jekyll 3 introduced [hooks](http://jekyllrb.com/docs/plugins/#hooks) which provide convenient access points throughout the Jekyll build pipeline whereby most needs can be fulfilled. If this is something that cannot be built as a plugin, then please provide more information about why in order to keep this issue open.

This issue will automatically be closed in two months if no further activity occurs. Thank you for all your contributions.
`),
	}

	staleNonJekyllIssueComment = &github.IssueComment{
		Body: github.String(`
This issue has been automatically marked as stale because it has not been commented on for at least two months.

The resources of the Jekyll team are limited, and so we are asking for your help.

If this is a **bug** and you can still reproduce this error on the master branch, please reply with all of the information you have about it in order to keep the issue open.

If this is a feature request, please consider whether it can be accomplished in another way. If it cannot, please elaborate on why it is core to this project and why you feel more than 80% of users would find this beneficial.

This issue will automatically be closed in two months if no further activity occurs. Thank you for all your contributions.
`),
	}
)

func main() {
	var actuallyDoIt bool
	flag.BoolVar(&actuallyDoIt, "f", false, "Whether to actually mark the issues or close them.")
	var inputRepos string
	flag.StringVar(&inputRepos, "repos", "", "Specify a list of comma-separated repo name/owner pairs, e.g. 'jekyll/jekyll-admin'.")
	flag.Parse()

	if ctx.NewDefaultContext().GitHub == nil {
		log.Fatalln("cannot proceed without github client")
	}

	var repos []repo
	if inputRepos != "" {
		for _, nwo := range strings.Split(inputRepos, ",") {
			pieces := strings.Split(nwo, "/")
			repos = append(repos, repo{Owner: pieces[0], Name: pieces[1]})
		}
	} else {
		repos = defaultRepos
	}

	wg, _ := errgroup.WithContext(context.Background())
	for _, repo := range repos {
		repo := repo
		wg.Go(func() error {
			return stale.MarkAndCloseForRepo(
				ctx.WithRepo(repo.Owner, repo.Name),
				stale.Configuration{
					Perform:             actuallyDoIt,
					StaleLabels:         staleLabels,
					ExemptLabels:        nonStaleableLabels,
					DormantDuration:     time.Since(twoMonthsAgo),
					NotificationComment: staleIssueComment(repo.Owner, repo.Name),
				},
			)
		})
	}
	if err := wg.Wait(); err != nil {
		log.Fatal("error: ", err)
	}
}

func staleIssueComment(repoOwner, repoName string) *github.IssueComment {
	if repoName == "jekyll" {
		return staleJekyllIssueComment
	} else {
		return staleNonJekyllIssueComment
	}
}
<commit_msg>Stale issues on jekyll-docs and minima<commit_after>// A command-line utility to mark and sweep Jekyll issues for staleness.
package main

import (
	"context"
	"flag"
	"log"
	"strings"
	"time"

	"github.com/google/go-github/github"
	"github.com/parkr/auto-reply/ctx"
	"github.com/parkr/auto-reply/stale"
	"golang.org/x/sync/errgroup"
)

type repo struct {
	Owner, Name string
}

var (
	// Labels which mean the issue is already stale.
	staleLabels = []string{
		"pending-feedback",
	}

	// Labels which can be used to disable the staleable functionality for an issue.
	nonStaleableLabels = []string{
		"has-pull-request",
		"pinned",
		"security",
	}

	// All the repos to apply apply these to.
	defaultRepos = []repo{
		repo{"jekyll", "jekyll"},
		repo{"jekyll", "jekyll-admin"},
		repo{"jekyll", "jekyll-import"},
		repo{"jekyll", "github-metadata"},
		repo{"jekyll", "jekyll-redirect-from"},
		repo{"jekyll", "jekyll-feed"},
		repo{"jekyll", "jekyll-compose"},
		repo{"jekyll", "jekyll-docs"},
		repo{"jekyll", "jekyll-watch"},
		repo{"jekyll", "jekyll-seo-tag"},
		repo{"jekyll", "jekyll-sitemap"},
		repo{"jekyll", "jekyll-sass-converter"},
		repo{"jekyll", "jemoji"},
		repo{"jekyll", "jekyll-gist"},
		repo{"jekyll", "jekyll-coffeescript"},
		repo{"jekyll", "minima"},
		repo{"jekyll", "plugins"},
	}

	twoMonthsAgo = time.Now().AddDate(0, -2, 0)

	staleIssuesListOptions = &github.IssueListByRepoOptions{
		State:       "open",
		Sort:        "updated",
		Direction:   "asc",
		ListOptions: github.ListOptions{PerPage: 200},
	}

	staleJekyllIssueComment = &github.IssueComment{
		Body: github.String(`
This issue has been automatically marked as stale because it has not been commented on for at least two months.

The resources of the Jekyll team are limited, and so we are asking for your help.

If this is a **bug** and you can still reproduce this error on the 3.3-stable or master branch, please reply with all of the information you have about it in order to keep the issue open.

If this is a **feature request**, please consider building it first as a plugin. Jekyll 3 introduced [hooks](http://jekyllrb.com/docs/plugins/#hooks) which provide convenient access points throughout the Jekyll build pipeline whereby most needs can be fulfilled. If this is something that cannot be built as a plugin, then please provide more information about why in order to keep this issue open.

This issue will automatically be closed in two months if no further activity occurs. Thank you for all your contributions.
`),
	}

	staleNonJekyllIssueComment = &github.IssueComment{
		Body: github.String(`
This issue has been automatically marked as stale because it has not been commented on for at least two months.

The resources of the Jekyll team are limited, and so we are asking for your help.

If this is a **bug** and you can still reproduce this error on the master branch, please reply with all of the information you have about it in order to keep the issue open.

If this is a feature request, please consider whether it can be accomplished in another way. If it cannot, please elaborate on why it is core to this project and why you feel more than 80% of users would find this beneficial.

This issue will automatically be closed in two months if no further activity occurs. Thank you for all your contributions.
`),
	}
)

func main() {
	var actuallyDoIt bool
	flag.BoolVar(&actuallyDoIt, "f", false, "Whether to actually mark the issues or close them.")
	var inputRepos string
	flag.StringVar(&inputRepos, "repos", "", "Specify a list of comma-separated repo name/owner pairs, e.g. 'jekyll/jekyll-admin'.")
	flag.Parse()

	if ctx.NewDefaultContext().GitHub == nil {
		log.Fatalln("cannot proceed without github client")
	}

	var repos []repo
	if inputRepos != "" {
		for _, nwo := range strings.Split(inputRepos, ",") {
			pieces := strings.Split(nwo, "/")
			repos = append(repos, repo{Owner: pieces[0], Name: pieces[1]})
		}
	} else {
		repos = defaultRepos
	}

	wg, _ := errgroup.WithContext(context.Background())
	for _, repo := range repos {
		repo := repo
		wg.Go(func() error {
			return stale.MarkAndCloseForRepo(
				ctx.WithRepo(repo.Owner, repo.Name),
				stale.Configuration{
					Perform:             actuallyDoIt,
					StaleLabels:         staleLabels,
					ExemptLabels:        nonStaleableLabels,
					DormantDuration:     time.Since(twoMonthsAgo),
					NotificationComment: staleIssueComment(repo.Owner, repo.Name),
				},
			)
		})
	}
	if err := wg.Wait(); err != nil {
		log.Fatal("error: ", err)
	}
}

func staleIssueComment(repoOwner, repoName string) *github.IssueComment {
	if repoName == "jekyll" {
		return staleJekyllIssueComment
	} else {
		return staleNonJekyllIssueComment
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/pkg/errors"
	"github.com/sdboyer/gps"
)

const ensureShortHelp = `Ensure a dependency is safely vendored in the project`
const ensureLongHelp = `
Ensure is used to fetch project dependencies into the vendor folder, as well as
to set version constraints for specific dependencies. It takes user input,
solves the updated dependency graph of the project, writes any changes to the
manifest and lock file, and places dependencies in the vendor folder.

Package spec:

  <path>[:alt location][@<version specifier>]

Examples:

  dep ensure                            Populate vendor from existing manifest and lock
  dep ensure github.com/pkg/foo@^1.0.1  Update a specific dependency to a specific version

For more detailed usage examples, see dep ensure -examples.
`
const ensureExamples = `
dep ensure

    Solve the project's dependency graph, and place all dependencies in the
    vendor folder. If a dependency is in the lock file, use the version
    specified there. Otherwise, use the most recent version that can satisfy the
    constraints in the manifest file.

dep ensure github.com/pkg/foo@^1.0.1

    Same as above, but choose any release >= 1.0.1, < 2.0.0. If a constraint was
    previously set in the manifest, this resets it. This form of constraint
    strikes a good balance of safety and flexibility, and should be preferred
    for libraries.

dep ensure github.com/pkg/foo@~1.0.1

    Same as above, but choose any release matching 1.0.x, preferring latest.

dep ensure github.com/pkg/foo:git.internal.com/alt/foo

    Fetch the dependency from a different location.

dep ensure -override github.com/pkg/foo@^1.0.1

    Forcefully and transitively override any constraint for this dependency.
    Overrides are powerful, but harmful in the long term. They should be used
    as a last resort, especially if your project may be imported by others.
`

func (cmd *ensureCommand) Name() string      { return "ensure" }
func (cmd *ensureCommand) Args() string      { return "[spec...]" }
func (cmd *ensureCommand) ShortHelp() string { return ensureShortHelp }
func (cmd *ensureCommand) LongHelp() string  { return ensureLongHelp }

func (cmd *ensureCommand) Register(fs *flag.FlagSet) {
	fs.BoolVar(&cmd.examples, "examples", false, "print detailed usage examples")
	fs.BoolVar(&cmd.update, "update", false, "ensure all dependencies are at the latest version allowed by the manifest")
	fs.BoolVar(&cmd.dryRun, "n", false, "dry run, don't actually ensure anything")
	fs.Var(&cmd.overrides, "override", "specify an override constraint spec (repeatable)")
}

type ensureCommand struct {
	examples  bool
	update    bool
	dryRun    bool
	overrides stringSlice
}

func (cmd *ensureCommand) Run(args []string) error {
	if cmd.examples {
		fmt.Fprintln(os.Stderr, strings.TrimSpace(ensureExamples))
		return nil
	}

	p, err := depContext.loadProject("")
	if err != nil {
		return err
	}

	sm, err := depContext.sourceManager()
	if err != nil {
		return err
	}
	defer sm.Release()

	var errs []error
	for _, arg := range args {
		// default persist to manifest
		pc, err := getProjectConstraint(arg, sm)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		if gps.IsAny(pc.Constraint) && pc.Ident.Source == "" {
			// If the input specified neither a network name nor a constraint,
			// then the strict thing to do would be to remove the entry
			// entirely. But that would probably be quite surprising for users,
			// and it's what rm is for, so just ignore the input.
			//
			// TODO(sdboyer): for this case - or just in general - do we want to
			// add project args to the requires list temporarily for this run?
			if _, has := p.m.Dependencies[pc.Ident.ProjectRoot]; !has {
				logf("No constraint or alternate source specified for %q, omitting from manifest", pc.Ident.ProjectRoot)
			}
			// If it's already in the manifest, no need to log
			continue
		}

		p.m.Dependencies[pc.Ident.ProjectRoot] = gps.ProjectProperties{
			Source: pc.Ident.Source,
			Constraint:  pc.Constraint,
		}

		for i, lp := range p.l.P {
			if lp.Ident() == pc.Ident {
				p.l.P = append(p.l.P[:i], p.l.P[i+1:]...)
				break
			}
		}
	}

	for _, ovr := range cmd.overrides {
		pc, err := getProjectConstraint(ovr, sm)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		// Empty overrides are fine (in contrast to deps), because they actually
		// carry meaning - they force the constraints entirely open for a given
		// project. Inadvisable, but meaningful.

		p.m.Ovr[pc.Ident.ProjectRoot] = gps.ProjectProperties{
			Source: pc.Ident.Source,
			Constraint:  pc.Constraint,
		}

		for i, lp := range p.l.P {
			if lp.Ident() == pc.Ident {
				p.l.P = append(p.l.P[:i], p.l.P[i+1:]...)
				break
			}
		}
	}

	if len(errs) > 0 {
		var buf bytes.Buffer
		for _, err := range errs {
			fmt.Fprintln(&buf, err)
		}

		return errors.New(buf.String())
	}

	params := gps.SolveParameters{
		RootDir:  p.absroot,
		Manifest: p.m,
		Lock:     p.l,
	}
	if *verbose {
		params.Trace = true
		params.TraceLogger = log.New(os.Stderr, "", 0)
	}

	params.RootPackageTree, err = gps.ListPackages(p.absroot, string(p.importroot))
	if err != nil {
		return errors.Wrap(err, "ensure ListPackage for project")
	}
	solver, err := gps.Prepare(params, sm)
	if err != nil {
		return errors.Wrap(err, "ensure Prepare")
	}
	solution, err := solver.Solve()
	if err != nil {
		handleAllTheFailuresOfTheWorld(err)
		return errors.Wrap(err, "ensure Solve()")
	}

	sw := safeWriter{
		root: p.absroot,
		m:    p.m,
		l:    p.l,
		nl:   solution,
		sm:   sm,
	}

	if err := sw.writeAllSafe(false); err != nil {
		return errors.Wrap(err, "grouped write of manifest, lock and vendor")
	}
	return nil
}

type stringSlice []string

func (s *stringSlice) String() string {
	if len(*s) == 0 {
		return "<none>"
	}
	return strings.Join(*s, ", ")
}

func (s *stringSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}

func getProjectConstraint(arg string, sm *gps.SourceMgr) (gps.ProjectConstraint, error) {
	constraint := gps.ProjectConstraint{
		Constraint: gps.Any(), // default to any; avoids panics later
	}

	// try to split on '@'
	var versionStr string
	atIndex := strings.Index(arg, "@")
	if atIndex > 0 {
		parts := strings.SplitN(arg, "@", 2)
		arg = parts[0]
		versionStr = parts[1]
		constraint.Constraint = deduceConstraint(parts[1])
	}
	// TODO: What if there is no @, assume default branch (which may not be master) ?
	// TODO: if we decide to keep equals.....

	// split on colon if there is a network location
	colonIndex := strings.Index(arg, ":")
	if colonIndex > 0 {
		parts := strings.SplitN(arg, ":", 2)
		arg = parts[0]
		constraint.Ident.Source = parts[1]
	}

	pr, err := sm.DeduceProjectRoot(arg)
	if err != nil {
		return constraint, errors.Wrapf(err, "could not infer project root from dependency path: %s", arg) // this should go through to the user
	}

	if string(pr) != arg {
		return constraint, fmt.Errorf("dependency path %s is not a project root, try %s instead", arg, pr)
	}

	constraint.Ident.ProjectRoot = gps.ProjectRoot(arg)

	// Below we are checking if the constraint we deduced was valid.
	if v, ok := constraint.Constraint.(gps.Version); ok && versionStr != "" {
		if v.Type() == gps.IsVersion {
			// we hit the fall through case in deduce constraint, let's call out to network
			// and get the package's versions
			versions, err := sm.ListVersions(constraint.Ident)
			if err != nil {
				return constraint, errors.Wrapf(err, "list versions for %s", arg) // means repo does not exist
			}

			var found bool
			for _, version := range versions {
				if versionStr == version.String() {
					if pv, ok := version.(gps.PairedVersion); ok {
						version = pv.Unpair()
					}
					found = true
					constraint.Constraint = version
					break
				}
			}

			if !found {
				return constraint, fmt.Errorf("%s is not a valid version for the package %s", versionStr, arg)
			}
		}
	}

	return constraint, nil
}

// deduceConstraint tries to puzzle out what kind of version is given in a string -
// semver, a revision, or as a fallback, a plain tag
func deduceConstraint(s string) gps.Constraint {
	// always semver if we can
	c, err := gps.NewSemverConstraint(s)
	if err == nil {
		return c
	}

	slen := len(s)
	if slen == 40 {
		if _, err = hex.DecodeString(s); err == nil {
			// Whether or not it's intended to be a SHA1 digest, this is a
			// valid byte sequence for that, so go with Revision. This
			// covers git and hg
			return gps.Revision(s)
		}
	}
	// Next, try for bzr, which has a three-component GUID separated by
	// dashes. There should be two, but the email part could contain
	// internal dashes
	if strings.Count(s, "-") >= 2 {
		// Work from the back to avoid potential confusion from the email
		i3 := strings.LastIndex(s, "-")
		// Skip if - is last char, otherwise this would panic on bounds err
		if slen == i3+1 {
			return gps.NewVersion(s)
		}

		i2 := strings.LastIndex(s[:i3], "-")
		if _, err = strconv.ParseUint(s[i2+1:i3], 10, 64); err == nil {
			// Getting this far means it'd pretty much be nuts if it's not a
			// bzr rev, so don't bother parsing the email.
			return gps.Revision(s)
		}
	}

	// If not a plain SHA1 or bzr custom GUID, assume a plain version.
	// TODO: if there is amgibuity here, then prompt the user?
	return gps.NewVersion(s)
}

// renameWithFallback attempts to rename a file or directory, but falls back to
// copying in the event of a cross-link device error. If the fallback copy
// succeeds, src is still removed, emulating normal rename behavior.
func renameWithFallback(src, dest string) error {
	fi, err := os.Lstat(src)
	if err != nil {
		return err
	}

	err = os.Rename(src, dest)
	if err == nil {
		return nil
	}

	terr, ok := err.(*os.LinkError)
	if !ok {
		return err
	}

	// Rename may fail if src and dest are on different devices; fall back to
	// copy if we detect that case. syscall.EXDEV is the common name for the
	// cross device link error which has varying output text across different
	// operating systems.
	var cerr error
	if terr.Err == syscall.EXDEV {
		vlogf("Cross link err (is temp dir on same partition as project?); falling back to manual copy: %s", terr)
		if fi.IsDir() {
			cerr = copyFolder(src, dest)
		} else {
			cerr = copyFile(src, dest)
		}
	} else if runtime.GOOS == "windows" {
		// In windows it can drop down to an operating system call that
		// returns an operating system error with a different number and
		// message. Checking for that as a fall back.
		noerr, ok := terr.Err.(syscall.Errno)
		// 0x11 (ERROR_NOT_SAME_DEVICE) is the windows error.
		// See https://msdn.microsoft.com/en-us/library/cc231199.aspx
		if ok && noerr == 0x11 {
			vlogf("Cross link err (is temp dir on same partition as project?); falling back to manual copy: %s", terr)
			if fi.IsDir() {
				cerr = copyFolder(src, dest)
			} else {
				cerr = copyFile(src, dest)
			}
		}
	} else {
		return terr
	}

	if cerr != nil {
		return cerr
	}

	return os.RemoveAll(src)
}

// copyFolder takes in a directory and copies its contents to the destination.
// It preserves the file mode on files as well.
func copyFolder(src string, dest string) error {
	fi, err := os.Lstat(src)
	if err != nil {
		return err
	}

	err = os.MkdirAll(dest, fi.Mode())
	if err != nil {
		return err
	}

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

	objects, err := dir.Readdir(-1)
	if err != nil {
		return err
	}

	for _, obj := range objects {
		if obj.Mode()&os.ModeSymlink != 0 {
			continue
		}

		srcfile := filepath.Join(src, obj.Name())
		destfile := filepath.Join(dest, obj.Name())

		if obj.IsDir() {
			err = copyFolder(srcfile, destfile)
			if err != nil {
				return err
			}
			continue
		}

		if err := copyFile(srcfile, destfile); err != nil {
			return err
		}
	}

	return nil
}

// copyFile copies a file from one place to another with the permission bits
// perserved as well.
func copyFile(src string, dest string) error {
	srcfile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcfile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer destfile.Close()

	if _, err := io.Copy(destfile, srcfile); err != nil {
		return err
	}

	srcinfo, err := os.Stat(src)
	if err != nil {
		return err
	}

	return os.Chmod(dest, srcinfo.Mode())
}
<commit_msg>goimports, rebase<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/pkg/errors"
	"github.com/sdboyer/gps"
)

const ensureShortHelp = `Ensure a dependency is safely vendored in the project`
const ensureLongHelp = `
Ensure is used to fetch project dependencies into the vendor folder, as well as
to set version constraints for specific dependencies. It takes user input,
solves the updated dependency graph of the project, writes any changes to the
manifest and lock file, and places dependencies in the vendor folder.

Package spec:

  <path>[:alt location][@<version specifier>]

Examples:

  dep ensure                            Populate vendor from existing manifest and lock
  dep ensure github.com/pkg/foo@^1.0.1  Update a specific dependency to a specific version

For more detailed usage examples, see dep ensure -examples.
`
const ensureExamples = `
dep ensure

    Solve the project's dependency graph, and place all dependencies in the
    vendor folder. If a dependency is in the lock file, use the version
    specified there. Otherwise, use the most recent version that can satisfy the
    constraints in the manifest file.

dep ensure github.com/pkg/foo@^1.0.1

    Same as above, but choose any release >= 1.0.1, < 2.0.0. If a constraint was
    previously set in the manifest, this resets it. This form of constraint
    strikes a good balance of safety and flexibility, and should be preferred
    for libraries.

dep ensure github.com/pkg/foo@~1.0.1

    Same as above, but choose any release matching 1.0.x, preferring latest.

dep ensure github.com/pkg/foo:git.internal.com/alt/foo

    Fetch the dependency from a different location.

dep ensure -override github.com/pkg/foo@^1.0.1

    Forcefully and transitively override any constraint for this dependency.
    Overrides are powerful, but harmful in the long term. They should be used
    as a last resort, especially if your project may be imported by others.
`

func (cmd *ensureCommand) Name() string      { return "ensure" }
func (cmd *ensureCommand) Args() string      { return "[spec...]" }
func (cmd *ensureCommand) ShortHelp() string { return ensureShortHelp }
func (cmd *ensureCommand) LongHelp() string  { return ensureLongHelp }

func (cmd *ensureCommand) Register(fs *flag.FlagSet) {
	fs.BoolVar(&cmd.examples, "examples", false, "print detailed usage examples")
	fs.BoolVar(&cmd.update, "update", false, "ensure all dependencies are at the latest version allowed by the manifest")
	fs.BoolVar(&cmd.dryRun, "n", false, "dry run, don't actually ensure anything")
	fs.Var(&cmd.overrides, "override", "specify an override constraint spec (repeatable)")
}

type ensureCommand struct {
	examples  bool
	update    bool
	dryRun    bool
	overrides stringSlice
}

func (cmd *ensureCommand) Run(args []string) error {
	if cmd.examples {
		fmt.Fprintln(os.Stderr, strings.TrimSpace(ensureExamples))
		return nil
	}

	p, err := depContext.loadProject("")
	if err != nil {
		return err
	}

	sm, err := depContext.sourceManager()
	if err != nil {
		return err
	}
	defer sm.Release()

	var errs []error
	for _, arg := range args {
		// default persist to manifest
		pc, err := getProjectConstraint(arg, sm)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		if gps.IsAny(pc.Constraint) && pc.Ident.Source == "" {
			// If the input specified neither a network name nor a constraint,
			// then the strict thing to do would be to remove the entry
			// entirely. But that would probably be quite surprising for users,
			// and it's what rm is for, so just ignore the input.
			//
			// TODO(sdboyer): for this case - or just in general - do we want to
			// add project args to the requires list temporarily for this run?
			if _, has := p.m.Dependencies[pc.Ident.ProjectRoot]; !has {
				logf("No constraint or alternate source specified for %q, omitting from manifest", pc.Ident.ProjectRoot)
			}
			// If it's already in the manifest, no need to log
			continue
		}

		p.m.Dependencies[pc.Ident.ProjectRoot] = gps.ProjectProperties{
			Source:     pc.Ident.Source,
			Constraint: pc.Constraint,
		}

		for i, lp := range p.l.P {
			if lp.Ident() == pc.Ident {
				p.l.P = append(p.l.P[:i], p.l.P[i+1:]...)
				break
			}
		}
	}

	for _, ovr := range cmd.overrides {
		pc, err := getProjectConstraint(ovr, sm)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		// Empty overrides are fine (in contrast to deps), because they actually
		// carry meaning - they force the constraints entirely open for a given
		// project. Inadvisable, but meaningful.

		p.m.Ovr[pc.Ident.ProjectRoot] = gps.ProjectProperties{
			Source:     pc.Ident.Source,
			Constraint: pc.Constraint,
		}

		for i, lp := range p.l.P {
			if lp.Ident() == pc.Ident {
				p.l.P = append(p.l.P[:i], p.l.P[i+1:]...)
				break
			}
		}
	}

	if len(errs) > 0 {
		var buf bytes.Buffer
		for _, err := range errs {
			fmt.Fprintln(&buf, err)
		}

		return errors.New(buf.String())
	}

	params := gps.SolveParameters{
		RootDir:  p.absroot,
		Manifest: p.m,
		Lock:     p.l,
	}
	if *verbose {
		params.Trace = true
		params.TraceLogger = log.New(os.Stderr, "", 0)
	}

	params.RootPackageTree, err = gps.ListPackages(p.absroot, string(p.importroot))
	if err != nil {
		return errors.Wrap(err, "ensure ListPackage for project")
	}
	solver, err := gps.Prepare(params, sm)
	if err != nil {
		return errors.Wrap(err, "ensure Prepare")
	}
	solution, err := solver.Solve()
	if err != nil {
		handleAllTheFailuresOfTheWorld(err)
		return errors.Wrap(err, "ensure Solve()")
	}

	sw := safeWriter{
		root: p.absroot,
		m:    p.m,
		l:    p.l,
		nl:   solution,
		sm:   sm,
	}

	if err := sw.writeAllSafe(false); err != nil {
		return errors.Wrap(err, "grouped write of manifest, lock and vendor")
	}
	return nil
}

type stringSlice []string

func (s *stringSlice) String() string {
	if len(*s) == 0 {
		return "<none>"
	}
	return strings.Join(*s, ", ")
}

func (s *stringSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}

func getProjectConstraint(arg string, sm *gps.SourceMgr) (gps.ProjectConstraint, error) {
	constraint := gps.ProjectConstraint{
		Constraint: gps.Any(), // default to any; avoids panics later
	}

	// try to split on '@'
	var versionStr string
	atIndex := strings.Index(arg, "@")
	if atIndex > 0 {
		parts := strings.SplitN(arg, "@", 2)
		arg = parts[0]
		versionStr = parts[1]
		constraint.Constraint = deduceConstraint(parts[1])
	}
	// TODO: What if there is no @, assume default branch (which may not be master) ?
	// TODO: if we decide to keep equals.....

	// split on colon if there is a network location
	colonIndex := strings.Index(arg, ":")
	if colonIndex > 0 {
		parts := strings.SplitN(arg, ":", 2)
		arg = parts[0]
		constraint.Ident.Source = parts[1]
	}

	pr, err := sm.DeduceProjectRoot(arg)
	if err != nil {
		return constraint, errors.Wrapf(err, "could not infer project root from dependency path: %s", arg) // this should go through to the user
	}

	if string(pr) != arg {
		return constraint, fmt.Errorf("dependency path %s is not a project root, try %s instead", arg, pr)
	}

	constraint.Ident.ProjectRoot = gps.ProjectRoot(arg)

	// Below we are checking if the constraint we deduced was valid.
	if v, ok := constraint.Constraint.(gps.Version); ok && versionStr != "" {
		if v.Type() == gps.IsVersion {
			// we hit the fall through case in deduce constraint, let's call out to network
			// and get the package's versions
			versions, err := sm.ListVersions(constraint.Ident)
			if err != nil {
				return constraint, errors.Wrapf(err, "list versions for %s", arg) // means repo does not exist
			}

			var found bool
			for _, version := range versions {
				if versionStr == version.String() {
					if pv, ok := version.(gps.PairedVersion); ok {
						version = pv.Unpair()
					}
					found = true
					constraint.Constraint = version
					break
				}
			}

			if !found {
				return constraint, fmt.Errorf("%s is not a valid version for the package %s", versionStr, arg)
			}
		}
	}

	return constraint, nil
}

// deduceConstraint tries to puzzle out what kind of version is given in a string -
// semver, a revision, or as a fallback, a plain tag
func deduceConstraint(s string) gps.Constraint {
	// always semver if we can
	c, err := gps.NewSemverConstraint(s)
	if err == nil {
		return c
	}

	slen := len(s)
	if slen == 40 {
		if _, err = hex.DecodeString(s); err == nil {
			// Whether or not it's intended to be a SHA1 digest, this is a
			// valid byte sequence for that, so go with Revision. This
			// covers git and hg
			return gps.Revision(s)
		}
	}
	// Next, try for bzr, which has a three-component GUID separated by
	// dashes. There should be two, but the email part could contain
	// internal dashes
	if strings.Count(s, "-") >= 2 {
		// Work from the back to avoid potential confusion from the email
		i3 := strings.LastIndex(s, "-")
		// Skip if - is last char, otherwise this would panic on bounds err
		if slen == i3+1 {
			return gps.NewVersion(s)
		}

		i2 := strings.LastIndex(s[:i3], "-")
		if _, err = strconv.ParseUint(s[i2+1:i3], 10, 64); err == nil {
			// Getting this far means it'd pretty much be nuts if it's not a
			// bzr rev, so don't bother parsing the email.
			return gps.Revision(s)
		}
	}

	// If not a plain SHA1 or bzr custom GUID, assume a plain version.
	// TODO: if there is amgibuity here, then prompt the user?
	return gps.NewVersion(s)
}

// renameWithFallback attempts to rename a file or directory, but falls back to
// copying in the event of a cross-link device error. If the fallback copy
// succeeds, src is still removed, emulating normal rename behavior.
func renameWithFallback(src, dest string) error {
	fi, err := os.Lstat(src)
	if err != nil {
		return err
	}

	err = os.Rename(src, dest)
	if err == nil {
		return nil
	}

	terr, ok := err.(*os.LinkError)
	if !ok {
		return err
	}

	// Rename may fail if src and dest are on different devices; fall back to
	// copy if we detect that case. syscall.EXDEV is the common name for the
	// cross device link error which has varying output text across different
	// operating systems.
	var cerr error
	if terr.Err == syscall.EXDEV {
		vlogf("Cross link err (is temp dir on same partition as project?); falling back to manual copy: %s", terr)
		if fi.IsDir() {
			cerr = copyFolder(src, dest)
		} else {
			cerr = copyFile(src, dest)
		}
	} else if runtime.GOOS == "windows" {
		// In windows it can drop down to an operating system call that
		// returns an operating system error with a different number and
		// message. Checking for that as a fall back.
		noerr, ok := terr.Err.(syscall.Errno)
		// 0x11 (ERROR_NOT_SAME_DEVICE) is the windows error.
		// See https://msdn.microsoft.com/en-us/library/cc231199.aspx
		if ok && noerr == 0x11 {
			vlogf("Cross link err (is temp dir on same partition as project?); falling back to manual copy: %s", terr)
			if fi.IsDir() {
				cerr = copyFolder(src, dest)
			} else {
				cerr = copyFile(src, dest)
			}
		}
	} else {
		return terr
	}

	if cerr != nil {
		return cerr
	}

	return os.RemoveAll(src)
}

// copyFolder takes in a directory and copies its contents to the destination.
// It preserves the file mode on files as well.
func copyFolder(src string, dest string) error {
	fi, err := os.Lstat(src)
	if err != nil {
		return err
	}

	err = os.MkdirAll(dest, fi.Mode())
	if err != nil {
		return err
	}

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

	objects, err := dir.Readdir(-1)
	if err != nil {
		return err
	}

	for _, obj := range objects {
		if obj.Mode()&os.ModeSymlink != 0 {
			continue
		}

		srcfile := filepath.Join(src, obj.Name())
		destfile := filepath.Join(dest, obj.Name())

		if obj.IsDir() {
			err = copyFolder(srcfile, destfile)
			if err != nil {
				return err
			}
			continue
		}

		if err := copyFile(srcfile, destfile); err != nil {
			return err
		}
	}

	return nil
}

// copyFile copies a file from one place to another with the permission bits
// perserved as well.
func copyFile(src string, dest string) error {
	srcfile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcfile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer destfile.Close()

	if _, err := io.Copy(destfile, srcfile); err != nil {
		return err
	}

	srcinfo, err := os.Stat(src)
	if err != nil {
		return err
	}

	return os.Chmod(dest, srcinfo.Mode())
}
<|endoftext|>


<commit_before>package multiplex

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	logging "github.com/ipfs/go-log"
	mpool "github.com/jbenet/go-msgio/mpool"
)

var log = logging.Logger("multiplex")

var MaxMessageSize = 1 << 20

var ErrShutdown = errors.New("session shut down")

// +1 for initiator
const (
	NewStream = 0
	Message   = 1
	Close     = 3
	Reset     = 5
)

type Multiplex struct {
	con       net.Conn
	buf       *bufio.Reader
	nextID    uint64
	initiator bool

	closed       chan struct{}
	shutdown     chan struct{}
	shutdownErr  error
	shutdownLock sync.Mutex

	wrLock sync.Mutex

	nstreams chan *Stream
	hdrBuf   []byte

	channels map[uint64]*Stream
	chLock   sync.Mutex
}

func NewMultiplex(con net.Conn, initiator bool) *Multiplex {
	mp := &Multiplex{
		con:       con,
		initiator: initiator,
		buf:       bufio.NewReader(con),
		channels:  make(map[uint64]*Stream),
		closed:    make(chan struct{}),
		shutdown:  make(chan struct{}),
		nstreams:  make(chan *Stream, 16),
		hdrBuf:    make([]byte, 20),
	}

	go mp.handleIncoming()

	return mp
}

func (mp *Multiplex) newStream(id uint64, name string, initiator bool) *Stream {
	var i uint64
	if initiator {
		i = 1
	}
	return &Stream{
		id:        id,
		name:      name,
		initiator: i,
		dataIn:    make(chan []byte, 8),
		mp:        mp,
	}
}

func (m *Multiplex) Accept() (*Stream, error) {
	select {
	case s, ok := <-m.nstreams:
		if !ok {
			return nil, errors.New("multiplex closed")
		}
		return s, nil
	case <-m.closed:
		return nil, m.shutdownErr
	}
}

func (mp *Multiplex) Close() error {
	mp.closeNoWait()

	// Wait for the receive loop to finish.
	<-mp.closed

	return nil
}

func (mp *Multiplex) closeNoWait() {
	mp.shutdownLock.Lock()
	select {
	case <-mp.shutdown:
	default:
		mp.con.Close()
		close(mp.shutdown)
	}
	mp.shutdownLock.Unlock()
}

func (mp *Multiplex) IsClosed() bool {
	select {
	case <-mp.closed:
		return true
	default:
		return false
	}
}

func (mp *Multiplex) sendMsg(header uint64, data []byte, dl time.Time) error {
	mp.wrLock.Lock()
	defer mp.wrLock.Unlock()
	if !dl.IsZero() {
		if err := mp.con.SetWriteDeadline(dl); err != nil {
			return err
		}
	}
	n := binary.PutUvarint(mp.hdrBuf, header)
	n += binary.PutUvarint(mp.hdrBuf[n:], uint64(len(data)))
	_, err := mp.con.Write(mp.hdrBuf[:n])
	if err != nil {
		return err
	}

	if len(data) != 0 {
		_, err = mp.con.Write(data)
		if err != nil {
			return err
		}
	}
	if !dl.IsZero() {
		if err := mp.con.SetWriteDeadline(time.Time{}); err != nil {
			return err
		}
	}

	return nil
}

func (mp *Multiplex) nextChanID() (out uint64) {
	if mp.initiator {
		out = mp.nextID + 1
	} else {
		out = mp.nextID
	}
	mp.nextID += 2
	return
}

func (mp *Multiplex) NewStream() (*Stream, error) {
	return mp.NewNamedStream("")
}

func (mp *Multiplex) NewNamedStream(name string) (*Stream, error) {
	mp.chLock.Lock()

	// We could call IsClosed but this is faster (given that we already have
	// the lock).
	if mp.channels == nil {
		return nil, ErrShutdown
	}

	sid := mp.nextChanID()
	header := (sid << 3) | NewStream

	if name == "" {
		name = fmt.Sprint(sid)
	}
	s := mp.newStream(sid, name, true)
	mp.channels[sid] = s
	mp.chLock.Unlock()

	err := mp.sendMsg(header, []byte(name), time.Time{})
	if err != nil {
		return nil, err
	}

	return s, nil
}

func (mp *Multiplex) cleanup() {
	mp.closeNoWait()
	mp.chLock.Lock()
	defer mp.chLock.Unlock()
	for _, msch := range mp.channels {
		msch.clLock.Lock()
		if !msch.closedRemote {
			msch.closedRemote = true
			// Cancel readers
			close(msch.dataIn)
		}
		msch.closedLocal = true
		msch.clLock.Unlock()
	}
	// Don't remove this nil assignment. We check if this is nil to check if
	// the connection is closed when we already have the lock (faster than
	// checking if the stream is closed).
	mp.channels = nil
	if mp.shutdownErr == nil {
		mp.shutdownErr = ErrShutdown
	}
	close(mp.closed)
}

func (mp *Multiplex) handleIncoming() {
	defer mp.cleanup()
	for {
		ch, tag, err := mp.readNextHeader()
		if err != nil {
			mp.shutdownErr = err
			return
		}

		b, err := mp.readNext()
		if err != nil {
			mp.shutdownErr = err
			return
		}

		mp.chLock.Lock()
		msch, ok := mp.channels[ch]
		mp.chLock.Unlock()

		// Why the +1s? 1 is added to the tag if the message is sent by an initiator.
		// While we don't actually care about which side is the
		// initiator (why should we?) it's the spec...
		switch tag {
		case NewStream:
			if ok {
				log.Debugf("received NewStream message for existing stream: %d", ch)
				continue
			}

			name := string(b)
			msch = mp.newStream(ch, name, false)
			mp.chLock.Lock()
			mp.channels[ch] = msch
			mp.chLock.Unlock()
			select {
			case mp.nstreams <- msch:
			case <-mp.shutdown:
				return
			}

		case Reset, Reset + 1:
			if !ok {
				continue
			}
			msch.clLock.Lock()

			// Honestly, this check should never be true... It means we've leaked.
			if msch.closedLocal && msch.closedRemote {
				msch.clLock.Unlock()
				continue
			}

			if !msch.closedRemote {
				close(msch.dataIn)
			}
			msch.closedRemote = true
			msch.closedLocal = true
			msch.clLock.Unlock()

			mp.chLock.Lock()
			delete(mp.channels, ch)
			mp.chLock.Unlock()
		case Close, Close + 1:
			if !ok {
				continue
			}

			msch.clLock.Lock()

			if msch.closedRemote {
				msch.clLock.Unlock()
				continue
			}

			close(msch.dataIn)
			msch.closedRemote = true

			cleanup := msch.closedLocal

			msch.clLock.Unlock()

			if cleanup {
				mp.chLock.Lock()
				delete(mp.channels, ch)
				mp.chLock.Unlock()
			}
		case Message, Message + 1:
			if !ok {
				log.Debugf("message for non-existant stream, dropping data: %d", ch)
				// Guess initiator status based on the tag.
				var initiator uint64
				if tag == Message {
					initiator = 1
				}
				go mp.sendMsg(ch<<3|Reset+initiator, nil, time.Time{})
				continue
			}
			msch.clLock.Lock()
			remoteClosed := msch.closedRemote
			msch.clLock.Unlock()
			if remoteClosed {
				log.Errorf("Received data from remote after stream was closed by them. (len = %d)", len(b))
				go mp.sendMsg(ch<<3|Reset+msch.initiator, nil, time.Time{})
				continue
			}
			select {
			case msch.dataIn <- b:
			case <-mp.shutdown:
				return
			}
		default:
			log.Debugf("message with unknown header on stream %s", ch)
			if ok {
				msch.Reset()
			}
		}
	}
}

func (mp *Multiplex) readNextHeader() (uint64, uint64, error) {
	h, err := binary.ReadUvarint(mp.buf)
	if err != nil {
		return 0, 0, err
	}

	// get channel ID
	ch := h >> 3

	rem := h & 7

	return ch, rem, nil
}

func (mp *Multiplex) readNext() ([]byte, error) {
	// get length
	l, err := binary.ReadUvarint(mp.buf)
	if err != nil {
		return nil, err
	}

	if l > uint64(MaxMessageSize) {
		return nil, fmt.Errorf("message size too large!")
	}

	if l == 0 {
		return nil, nil
	}

	buf := mpool.ByteSlicePool.Get(uint32(l)).([]byte)[:l]
	n, err := io.ReadFull(mp.buf, buf)
	if err != nil {
		return nil, err
	}

	return buf[:n], nil
}
<commit_msg>log on leaked stream<commit_after>package multiplex

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	logging "github.com/ipfs/go-log"
	mpool "github.com/jbenet/go-msgio/mpool"
)

var log = logging.Logger("multiplex")

var MaxMessageSize = 1 << 20

var ErrShutdown = errors.New("session shut down")

// +1 for initiator
const (
	NewStream = 0
	Message   = 1
	Close     = 3
	Reset     = 5
)

type Multiplex struct {
	con       net.Conn
	buf       *bufio.Reader
	nextID    uint64
	initiator bool

	closed       chan struct{}
	shutdown     chan struct{}
	shutdownErr  error
	shutdownLock sync.Mutex

	wrLock sync.Mutex

	nstreams chan *Stream
	hdrBuf   []byte

	channels map[uint64]*Stream
	chLock   sync.Mutex
}

func NewMultiplex(con net.Conn, initiator bool) *Multiplex {
	mp := &Multiplex{
		con:       con,
		initiator: initiator,
		buf:       bufio.NewReader(con),
		channels:  make(map[uint64]*Stream),
		closed:    make(chan struct{}),
		shutdown:  make(chan struct{}),
		nstreams:  make(chan *Stream, 16),
		hdrBuf:    make([]byte, 20),
	}

	go mp.handleIncoming()

	return mp
}

func (mp *Multiplex) newStream(id uint64, name string, initiator bool) *Stream {
	var i uint64
	if initiator {
		i = 1
	}
	return &Stream{
		id:        id,
		name:      name,
		initiator: i,
		dataIn:    make(chan []byte, 8),
		mp:        mp,
	}
}

func (m *Multiplex) Accept() (*Stream, error) {
	select {
	case s, ok := <-m.nstreams:
		if !ok {
			return nil, errors.New("multiplex closed")
		}
		return s, nil
	case <-m.closed:
		return nil, m.shutdownErr
	}
}

func (mp *Multiplex) Close() error {
	mp.closeNoWait()

	// Wait for the receive loop to finish.
	<-mp.closed

	return nil
}

func (mp *Multiplex) closeNoWait() {
	mp.shutdownLock.Lock()
	select {
	case <-mp.shutdown:
	default:
		mp.con.Close()
		close(mp.shutdown)
	}
	mp.shutdownLock.Unlock()
}

func (mp *Multiplex) IsClosed() bool {
	select {
	case <-mp.closed:
		return true
	default:
		return false
	}
}

func (mp *Multiplex) sendMsg(header uint64, data []byte, dl time.Time) error {
	mp.wrLock.Lock()
	defer mp.wrLock.Unlock()
	if !dl.IsZero() {
		if err := mp.con.SetWriteDeadline(dl); err != nil {
			return err
		}
	}
	n := binary.PutUvarint(mp.hdrBuf, header)
	n += binary.PutUvarint(mp.hdrBuf[n:], uint64(len(data)))
	_, err := mp.con.Write(mp.hdrBuf[:n])
	if err != nil {
		return err
	}

	if len(data) != 0 {
		_, err = mp.con.Write(data)
		if err != nil {
			return err
		}
	}
	if !dl.IsZero() {
		if err := mp.con.SetWriteDeadline(time.Time{}); err != nil {
			return err
		}
	}

	return nil
}

func (mp *Multiplex) nextChanID() (out uint64) {
	if mp.initiator {
		out = mp.nextID + 1
	} else {
		out = mp.nextID
	}
	mp.nextID += 2
	return
}

func (mp *Multiplex) NewStream() (*Stream, error) {
	return mp.NewNamedStream("")
}

func (mp *Multiplex) NewNamedStream(name string) (*Stream, error) {
	mp.chLock.Lock()

	// We could call IsClosed but this is faster (given that we already have
	// the lock).
	if mp.channels == nil {
		return nil, ErrShutdown
	}

	sid := mp.nextChanID()
	header := (sid << 3) | NewStream

	if name == "" {
		name = fmt.Sprint(sid)
	}
	s := mp.newStream(sid, name, true)
	mp.channels[sid] = s
	mp.chLock.Unlock()

	err := mp.sendMsg(header, []byte(name), time.Time{})
	if err != nil {
		return nil, err
	}

	return s, nil
}

func (mp *Multiplex) cleanup() {
	mp.closeNoWait()
	mp.chLock.Lock()
	defer mp.chLock.Unlock()
	for _, msch := range mp.channels {
		msch.clLock.Lock()
		if !msch.closedRemote {
			msch.closedRemote = true
			// Cancel readers
			close(msch.dataIn)
		}
		msch.closedLocal = true
		msch.clLock.Unlock()
	}
	// Don't remove this nil assignment. We check if this is nil to check if
	// the connection is closed when we already have the lock (faster than
	// checking if the stream is closed).
	mp.channels = nil
	if mp.shutdownErr == nil {
		mp.shutdownErr = ErrShutdown
	}
	close(mp.closed)
}

func (mp *Multiplex) handleIncoming() {
	defer mp.cleanup()
	for {
		ch, tag, err := mp.readNextHeader()
		if err != nil {
			mp.shutdownErr = err
			return
		}

		b, err := mp.readNext()
		if err != nil {
			mp.shutdownErr = err
			return
		}

		mp.chLock.Lock()
		msch, ok := mp.channels[ch]
		mp.chLock.Unlock()

		// Why the +1s? 1 is added to the tag if the message is sent by an initiator.
		// While we don't actually care about which side is the
		// initiator (why should we?) it's the spec...
		switch tag {
		case NewStream:
			if ok {
				log.Debugf("received NewStream message for existing stream: %d", ch)
				continue
			}

			name := string(b)
			msch = mp.newStream(ch, name, false)
			mp.chLock.Lock()
			mp.channels[ch] = msch
			mp.chLock.Unlock()
			select {
			case mp.nstreams <- msch:
			case <-mp.shutdown:
				return
			}

		case Reset, Reset + 1:
			if !ok {
				continue
			}
			msch.clLock.Lock()

			// Honestly, this check should never be true... It means we've leaked.
			if msch.closedLocal && msch.closedRemote {
				msch.clLock.Unlock()
				log.Errorf("leaked a completely closed stream")
				continue
			}

			if !msch.closedRemote {
				close(msch.dataIn)
			}
			msch.closedRemote = true
			msch.closedLocal = true
			msch.clLock.Unlock()

			mp.chLock.Lock()
			delete(mp.channels, ch)
			mp.chLock.Unlock()
		case Close, Close + 1:
			if !ok {
				continue
			}

			msch.clLock.Lock()

			if msch.closedRemote {
				msch.clLock.Unlock()
				continue
			}

			close(msch.dataIn)
			msch.closedRemote = true

			cleanup := msch.closedLocal

			msch.clLock.Unlock()

			if cleanup {
				mp.chLock.Lock()
				delete(mp.channels, ch)
				mp.chLock.Unlock()
			}
		case Message, Message + 1:
			if !ok {
				log.Debugf("message for non-existant stream, dropping data: %d", ch)
				// Guess initiator status based on the tag.
				var initiator uint64
				if tag == Message {
					initiator = 1
				}
				go mp.sendMsg(ch<<3|Reset+initiator, nil, time.Time{})
				continue
			}
			msch.clLock.Lock()
			remoteClosed := msch.closedRemote
			msch.clLock.Unlock()
			if remoteClosed {
				log.Errorf("Received data from remote after stream was closed by them. (len = %d)", len(b))
				go mp.sendMsg(ch<<3|Reset+msch.initiator, nil, time.Time{})
				continue
			}
			select {
			case msch.dataIn <- b:
			case <-mp.shutdown:
				return
			}
		default:
			log.Debugf("message with unknown header on stream %s", ch)
			if ok {
				msch.Reset()
			}
		}
	}
}

func (mp *Multiplex) readNextHeader() (uint64, uint64, error) {
	h, err := binary.ReadUvarint(mp.buf)
	if err != nil {
		return 0, 0, err
	}

	// get channel ID
	ch := h >> 3

	rem := h & 7

	return ch, rem, nil
}

func (mp *Multiplex) readNext() ([]byte, error) {
	// get length
	l, err := binary.ReadUvarint(mp.buf)
	if err != nil {
		return nil, err
	}

	if l > uint64(MaxMessageSize) {
		return nil, fmt.Errorf("message size too large!")
	}

	if l == 0 {
		return nil, nil
	}

	buf := mpool.ByteSlicePool.Get(uint32(l)).([]byte)[:l]
	n, err := io.ReadFull(mp.buf, buf)
	if err != nil {
		return nil, err
	}

	return buf[:n], nil
}
<|endoftext|>


<commit_before>package mysqltest

import (
	"bytes"
	"database/sql"
	"fmt"
	"net"
	"os"
	"os/exec"
	"time"
)

type MysqldConfig struct {
	Tag string
}

func NewMysqldConfig() *MysqldConfig {
	return &MysqldConfig{
		Tag: "mysql:latest",
	}
}

type Mysqld struct {
	port      string
	host      string
	config    *MysqldConfig
	container string
}

func NewMysqld(config *MysqldConfig) (*Mysqld, error) {
	if config == nil {
		config = NewMysqldConfig()
	}
	mysqld := &Mysqld{
		config: config,
	}
	if err := mysqld.start(); err != nil {
		return nil, err
	}
	return mysqld, nil
}

func (m *Mysqld) DSN() string {
	return fmt.Sprintf("%s@tcp(%s:%s)/%s", "root", m.host, m.port, "test")
}

func (m *Mysqld) Stop() {
	killCointainer(m.container)
	removeContainer(m.container)
}

func (m *Mysqld) start() error {
	cmd, port, err := m.dockerRunCommand()
	if err != nil {
		return err
	}
	_container, err := cmd.Output()
	container := string(chomp(_container))
	if err != nil {
		return err
	}

	m.container = container
	m.port = port

	if inDockerContainer() {
		o, err := exec.Command("docker", "inspect", "--format={{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}", m.container).Output()
		if err != nil {
			return err
		}
		m.host = string(chomp(o))
	} else {
		m.host = "127.0.0.1"
	}

	timeout := time.NewTimer(time.Second * 30)
	connect := time.NewTicker(time.Second)

	for {
		select {
		case <-timeout.C:
			killCointainer(container)
			removeContainer(container)
			return fmt.Errorf("timeout: failed to connect mysqld")
		case <-connect.C:
			dsn := m.DSN()
			db, err := sql.Open("mysql", dsn)
			if err != nil {
				return err
			}
			if err := db.Ping(); err != nil {
				continue
			}
			return nil
		}
	}
}

func (m *Mysqld) dockerRunCommand() (*exec.Cmd, string, error) {
	var args = []string{"run"}
	var port string
	if inDockerContainer() {
		o, err := exec.Command("docker", "inspect", "--format={{.HostConfig.NetworkMode}}", os.Getenv("HOSTNAME")).Output()
		if err != nil {
			return nil, "", err
		}
		network := chomp(o)
		args = append(args, "--network", string(network))
		port = "3306"
	} else {
		forward, err := emptyPort()
		if err != nil {
			return nil, "", err
		}
		args = append(args, "-p", fmt.Sprintf("%s:3306", forward))
		port = forward
	}
	args = append(args, "-e", "MYSQL_ALLOW_EMPTY_PASSWORD=1")
	args = append(args, "-e", "MYSQL_DATABASE=test")
	args = append(args, "-d", m.config.Tag)
	return exec.Command("docker", args...), port, nil
}

func inDockerContainer() bool {
	_, err := os.Stat("/.dockerenv")
	return err == nil
}

func killCointainer(id string) error {
	return exec.Command("docker", "kill", id).Run()
}

func removeContainer(id string) error {
	return exec.Command("docker", "rm", "-v", id).Run()
}

func emptyPort() (string, error) {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return "", err
	}
	_, port, err := net.SplitHostPort(l.Addr().String())
	if err != nil {
		return "", err
	}
	l.Close()
	return port, nil
}

func chomp(v []byte) []byte {
	return bytes.TrimRight(v, "\n")
}
<commit_msg>Add Timeout option<commit_after>package mysqltest

import (
	"bytes"
	"database/sql"
	"fmt"
	"net"
	"os"
	"os/exec"
	"time"
)

type MysqldConfig struct {
	Tag     string
	Timeout int
}

func NewMysqldConfig() *MysqldConfig {
	return &MysqldConfig{
		Tag:     "mysql:latest",
		Timeout: 30,
	}
}

type Mysqld struct {
	port      string
	host      string
	config    *MysqldConfig
	container string
}

func NewMysqld(config *MysqldConfig) (*Mysqld, error) {
	if config == nil {
		config = NewMysqldConfig()
	}
	mysqld := &Mysqld{
		config: config,
	}
	if err := mysqld.start(); err != nil {
		return nil, err
	}
	return mysqld, nil
}

func (m *Mysqld) DSN() string {
	return fmt.Sprintf("%s@tcp(%s:%s)/%s", "root", m.host, m.port, "test")
}

func (m *Mysqld) Stop() {
	killCointainer(m.container)
	removeContainer(m.container)
}

func (m *Mysqld) start() error {
	cmd, port, err := m.dockerRunCommand()
	if err != nil {
		return err
	}
	_container, err := cmd.Output()
	container := string(chomp(_container))
	if err != nil {
		return err
	}

	m.container = container
	m.port = port

	if inDockerContainer() {
		o, err := exec.Command("docker", "inspect", "--format={{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}", m.container).Output()
		if err != nil {
			return err
		}
		m.host = string(chomp(o))
	} else {
		m.host = "127.0.0.1"
	}

	timeout := time.NewTimer(time.Second * time.Duration(m.config.Timeout))
	connect := time.NewTicker(time.Second)

	for {
		select {
		case <-timeout.C:
			killCointainer(container)
			removeContainer(container)
			return fmt.Errorf("timeout: failed to connect mysqld")
		case <-connect.C:
			dsn := m.DSN()
			db, err := sql.Open("mysql", dsn)
			if err != nil {
				return err
			}
			if err := db.Ping(); err != nil {
				continue
			}
			return nil
		}
	}
}

func (m *Mysqld) dockerRunCommand() (*exec.Cmd, string, error) {
	var args = []string{"run"}
	var port string
	if inDockerContainer() {
		o, err := exec.Command("docker", "inspect", "--format={{.HostConfig.NetworkMode}}", os.Getenv("HOSTNAME")).Output()
		if err != nil {
			return nil, "", err
		}
		network := chomp(o)
		args = append(args, "--network", string(network))
		port = "3306"
	} else {
		forward, err := emptyPort()
		if err != nil {
			return nil, "", err
		}
		args = append(args, "-p", fmt.Sprintf("%s:3306", forward))
		port = forward
	}
	args = append(args, "-e", "MYSQL_ALLOW_EMPTY_PASSWORD=1")
	args = append(args, "-e", "MYSQL_DATABASE=test")
	args = append(args, "-d", m.config.Tag)
	return exec.Command("docker", args...), port, nil
}

func inDockerContainer() bool {
	_, err := os.Stat("/.dockerenv")
	return err == nil
}

func killCointainer(id string) error {
	return exec.Command("docker", "kill", id).Run()
}

func removeContainer(id string) error {
	return exec.Command("docker", "rm", "-v", id).Run()
}

func emptyPort() (string, error) {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return "", err
	}
	_, port, err := net.SplitHostPort(l.Addr().String())
	if err != nil {
		return "", err
	}
	l.Close()
	return port, nil
}

func chomp(v []byte) []byte {
	return bytes.TrimRight(v, "\n")
}
<|endoftext|>